/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.ctecinf.orm.swing;

import br.com.ctecinf.table.Table;
import br.com.ctecinf.table.TableModel;
import br.com.ctecinf.Daruma;
import br.com.ctecinf.PDF;
import br.com.ctecinf.Utils;
import br.com.ctecinf.orm.Column;
import br.com.ctecinf.orm.Model;
import br.com.ctecinf.swing.Image;
import br.com.ctecinf.swing.OptionPane;
import br.com.ctecinf.swing.PleaseWaitDialog;
import br.com.ctecinf.table.TablePopupMenu;
import br.com.ctecinf.text.UpperCaseFormatter;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.table.TableRowSorter;

/**
 *
 * @author Cássio Conceição
 * @param <T>
 * @since 24/09/2019
 * @version 1909
 * @see http://ctecinf.com.br/
 */
public abstract class TablePanelController<T extends Model> extends JPanel {

    private Class<T> controller;

    private Table table;
    private JToolBar buttonBar;
    private TablePopupMenu popup;
    private JFormattedTextField filter;
    private boolean reloadOnClose;

    protected abstract FormPanelController getForm(T controller);

    public TablePanelController() {
        this.controller = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        initUI(null);
    }

    public TablePanelController(Class<T> controller) {
        this.controller = controller;
        initUI(null);
    }

    public TablePanelController(TableModel tableModel) {
        this.controller = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        initUI(tableModel);
    }

    private void initUI(TableModel tableModel) {

        reloadOnClose = tableModel == null;

        setLayout(new BorderLayout());

        try {
            table = new Table(tableModel == null ? controller.newInstance().getTableModel() : tableModel);
        } catch (Exception ex) {
            OptionPane.error(ex);
        }

        filter = new JFormattedTextField(new UpperCaseFormatter());
        filter.setBorder(BorderFactory.createTitledBorder("Filtrar"));

        filter.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {

                if (e.getKeyCode() == KeyEvent.VK_ENTER) {

                    String search = filter.getText();

                    if (search.length() > 2) {
                        ((TableRowSorter) table.getRowSorter()).setRowFilter(RowFilter.regexFilter(Pattern.compile(search, Pattern.CASE_INSENSITIVE).pattern()));
                    } else {
                        ((TableRowSorter) table.getRowSorter()).setRowFilter(null);
                    }

                } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
                    table.requestFocus();
                    table.getSelectionModel().setSelectionInterval(0, 0);
                }

            }
        });

        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    viewEvent();
                }
            }
        });

        table.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {

                if (e.getKeyCode() == KeyEvent.VK_ENTER && table.getSelectedRow() > -1) {
                    viewEvent();
                }

                if (e.getKeyCode() == KeyEvent.VK_UP && table.getSelectedRow() == 0) {

                    SwingUtilities.invokeLater(() -> {
                        table.clearSelection();
                    });

                    filter.requestFocus();
                }
            }
        });

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(filter, BorderLayout.NORTH);
        panel.add(new JScrollPane(table), BorderLayout.CENTER);

        buttonBar = new JToolBar(JToolBar.HORIZONTAL);
        buttonBar.setFloatable(false);
        buttonBar.setLayout(new FlowLayout(FlowLayout.RIGHT));

        popup = new TablePopupMenu(table);

        addAction("Atualizar", Image.parse(Image.RELOAD), (ActionEvent e) -> {
            reload();
        });

        addAction("Incluir", Image.parse(Image.EDIT), (ActionEvent e) -> {
            try {
                createEvent(this.controller.getDeclaredConstructor().newInstance());
            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                JOptionPane.showMessageDialog(null, ex, "Exception", JOptionPane.ERROR_MESSAGE);
            }
        });

        addAction("Visualizar", Image.parse(Image.VIEW), (ActionEvent e) -> {
            viewEvent();
        });

        addAction("Apagar", Image.parse(Image.TRASH), (ActionEvent e) -> {
            deleteEvent();
        });

        addAction("Imprimir", Image.parse(Image.PRINT), (ActionEvent ae) -> {
            printEvent();
        });

        add(buttonBar, BorderLayout.NORTH);
        add(panel, BorderLayout.CENTER);

        SwingUtilities.invokeLater(() -> {
            filter.requestFocus();
        });
    }

    public TablePanelController addAction(String label, ImageIcon icon, ActionListener action) {

        popup.addMenuItem(label, icon, action);

        JButton button = new JButton(label, icon);
        button.addActionListener(action);

        buttonBar.add(button);

        return this;
    }

    public Table getTable() {
        return table;
    }

    public JFormattedTextField getFilter() {
        return filter;
    }

    protected FormPanelController create(T obj) throws Exception {
        return getForm(obj);
    }

    protected FormPanelController view(T obj) throws Exception {
        return getForm(obj);
    }

    protected boolean delete(T obj) throws Exception {
        return obj.delete();
    }

    protected File print(T obj) throws Exception {

        if (obj == null) {

            PDF pdf = new PDF();

            return pdf.parse(null, table);

        } else {

            Daruma p = new Daruma();

            p.head();
            p.linhaCorte();

            p.tamanhoFonte(2);
            p.negrito("Dados cadastro");
            p.novaLinha();
            p.novaLinha();

            for (Field field : obj.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Column.class)) {
                    field.setAccessible(true);
                    Object value = field.get(obj);
                    p.leftLine(Daruma.bold(Utils.ascii(obj.getLabel(field.getName()), false)) + ": " + (value == null ? "" : value));
                }
            }

            p.saltarLinhas(5);

            return null;
        }
    }

    private void onFormPanelClosed(FormPanelController form) {

        if (form.getFrame() != null) {

            form.getFrame().setVisible(true);

            form.getFrame().addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent e) {
                    if (reloadOnClose) {
                        reload();
                    }
                }
            });
        }
    }

    private void reload() {

        new PleaseWaitDialog<Boolean>() {

            @Override
            public Boolean exec() throws Exception {
                table.removeAllRows();
                table.setModel(((Model) controller.newInstance()).getTableModel());
                return true;
            }

            @Override
            public void end(Boolean result) throws Exception {
            }

        }.start();
    }

    private T getSelectedObject() throws Exception {

        if (table.getSelectedRow() == -1) {
            throw new Exception("Nenhum registro selecionado.");
        }

        int index = table.getRowSorter().convertRowIndexToModel(table.getSelectedRow());

        return (T) ((TableModel) table.getModel()).getData().get(index);
    }

    private void deleteEvent() {

        T obj;

        try {
            obj = getSelectedObject();
        } catch (Exception ex) {
            OptionPane.error(ex);
            return;
        }

        if (OptionPane.confirm("Deseja realmente excluír registro [" + obj + "]?")) {

            new PleaseWaitDialog<Boolean>() {

                @Override
                public Boolean exec() throws Exception {
                    return delete(obj);
                }

                @Override
                public void end(Boolean result) throws Exception {
                    if (result) {
                        OptionPane.success("Registro excluído com sucesso.");
                        reload();
                    }
                }

            }.start();
        }
    }

    private void createEvent(T obj) {

        new PleaseWaitDialog<FormPanelController>() {

            @Override
            public FormPanelController exec() throws Exception {
                return create(obj);
            }

            @Override
            public void end(FormPanelController result) {
                onFormPanelClosed(result);
            }

        }.start();
    }

    private void viewEvent() {

        T obj;

        try {
            obj = getSelectedObject();
        } catch (Exception ex) {
            OptionPane.error(ex);
            return;
        }

        new PleaseWaitDialog<FormPanelController>() {

            @Override
            public FormPanelController exec() throws Exception {
                return view(obj);
            }

            @Override
            public void end(FormPanelController result) {
                onFormPanelClosed(result);
            }

        }.start();
    }

    private void printEvent() {

        T object = null;

        try {
            object = getSelectedObject();
        } catch (Exception ex) {
        }

        T obj = object;

        new PleaseWaitDialog<File>() {

            @Override
            public File exec() throws Exception {
                return TablePanelController.this.print(obj);
            }

            @Override
            public void end(File result) {
                if (result != null) {
                    try {
                        Desktop.getDesktop().open(result);
                        result.deleteOnExit();
                    } catch (Exception ex) {
                        OptionPane.error(ex);
                    }
                }
            }

        }.start();
    }

}
