package com.dlsc.flexgantt.showcase.demo.commands;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.TimeZone;

import javax.swing.AbstractListModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import net.miginfocom.swing.MigLayout;

import com.dlsc.flexgantt.command.AbstractCommand;
import com.dlsc.flexgantt.command.CommandStackEvent;
import com.dlsc.flexgantt.command.DefaultCommandStack;
import com.dlsc.flexgantt.command.ICommand;
import com.dlsc.flexgantt.command.ICommandStackListener;
import com.dlsc.flexgantt.command.IProgressMonitor;
import com.dlsc.flexgantt.command.layer.DefaultChangeTimelineObjectTimeSpanCommand;
import com.dlsc.flexgantt.command.layer.DefaultDragAndDropCommand;
import com.dlsc.flexgantt.showcase.AbstractDemo;
import com.dlsc.flexgantt.showcase.DemoControlsPanel;
import com.dlsc.flexgantt.model.dateline.DatelineModelException;
import com.dlsc.flexgantt.model.gantt.GanttChartModelEvent;
import com.dlsc.flexgantt.model.gantt.IGanttChartModelListener;
import com.dlsc.flexgantt.swing.AbstractGanttChart;
import com.dlsc.flexgantt.swing.GanttChart;
import com.dlsc.flexgantt.swing.GanttChartFrame;
import com.dlsc.flexgantt.swing.ICommandInterceptor;
import com.dlsc.flexgantt.swing.action.gantt.RedoAction;
import com.dlsc.flexgantt.swing.action.gantt.UndoAction;
import com.dlsc.flexgantt.util.GridLineMode;

public class CommandsDemo extends AbstractDemo {

    public CommandsDemo() {
        super("Commands");
    }

    @Override
    public String[] getSourceCodeFileNames() {
        return new String[] { "CommandsDemo", "CommandsModel" };
    }

    @Override
    public void run(boolean embedded) {
        final GanttChart gc = createGanttChart();

        try {
            gc.getDatelineModel().setTimeZone(TimeZone.getTimeZone("GMT-8:00"));
        } catch (DatelineModelException e) {
            e.printStackTrace();
        }

        gc.setModel(new CommandsModel());
        gc.resetToPreferredSizes();
        gc.getTreeTable().setRootVisible(false);
        gc.setGridLineMode(GridLineMode.COMBINED_GRID_LINES);

        GanttChartFrame frame = createFrame("Commands", gc, embedded);

        JPanel commandStackPanel = new JPanel();
        commandStackPanel.setOpaque(false);
        commandStackPanel.setBorder(createTitledBorder("Command Stack"));
        commandStackPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow][grow]","[grow][]"));
        final AbstractListModel model = new AbstractListModel() {

            @Override
            public Object getElementAt(int index) {
                DefaultCommandStack stack = (DefaultCommandStack) gc
                        .getCommandStack();
                return stack.getExecutedCommands().get(index);
            }

            @Override
            public int getSize() {
                DefaultCommandStack stack = (DefaultCommandStack) gc
                        .getCommandStack();
                return stack.getExecutedCommands().size();
            }
        };

        final JList list = new JList(model);
        list.setVisibleRowCount(8);

        JScrollPane scrollPane = new JScrollPane(list);
        scrollPane.setPreferredSize(new Dimension(150, 200));

        gc.getCommandStack().addCommandStackListener(
                new ICommandStackListener() {
                    @Override
                    public void commandStackChanged(CommandStackEvent e) {
                        for (ListDataListener l : model.getListDataListeners()) {
                            l.contentsChanged(new ListDataEvent(list,
                                    ListDataEvent.CONTENTS_CHANGED, 0, model
                                            .getSize()));
                        }
                    }
                });

        list.setCellRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList jl,
                    Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {
                JLabel label = (JLabel) super.getListCellRendererComponent(jl,
                        value, index, isSelected, cellHasFocus);
                ICommand cmd = (ICommand) value;
                label.setText(cmd.getName());
                return label;
            }
        });

        JButton undoButton = new JButton(new UndoAction<GanttChart>(gc));
        JButton redoButton = new JButton(new RedoAction<GanttChart>(gc));
                
        commandStackPanel.add(scrollPane, "spanx 2, wrap, grow");
        commandStackPanel.add(undoButton);
        commandStackPanel.add(redoButton);
        
        final JDialog dialog = new JDialog();
        dialog.setTitle("Commandstack Events Log");
        dialog.setModal(false);
        dialog.setSize(700, 300);
        JPanel textPanel = new JPanel();
        textPanel.setLayout(new BorderLayout());
        final JTextArea area = new JTextArea(10, 10);
        area.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void removeUpdate(DocumentEvent e) {
                if (!dialog.isVisible()) {
                    dialog.setVisible(true);
                }
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                if (!dialog.isVisible()) {
                    dialog.setVisible(true);
                }
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                if (!dialog.isVisible()) {
                    dialog.setVisible(true);
                }
            }
        });

        dialog.add(BorderLayout.CENTER, textPanel);

        textPanel.add(BorderLayout.CENTER, new JScrollPane(area));
        gc.getCommandStack().addCommandStackListener(
                new ICommandStackListener() {
                    @Override
                    public void commandStackChanged(CommandStackEvent e) {
                        area.append(e.toString());
                        area.append("\n");
                        area.setCaretPosition(area.getText().length());
                    }
                });
        gc.getModel().addGanttChartModelListener(
                new IGanttChartModelListener() {
                    @Override
                    public void ganttChartChanged(final GanttChartModelEvent e) {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                area.append(e.toString());
                                area.append("\n");
                                area.setCaretPosition(area.getText().length());
                            }
                        });
                    }
                });

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new MigLayout("insets 2 2 2 2, wrap 1", "[grow]",
                "[][]"));
        buttonPanel.setBorder(createTitledBorder("Actions"));

        JButton button1 = new JButton("Not Undoable Command");
        button1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(gc,
                        "Running a command that is not undoable\n"
                                + "clears the command stack", "Information",
                        JOptionPane.INFORMATION_MESSAGE);
                gc.commandExecute(new AbstractCommand("Not Undoable") {
                    @Override
                    public void executeCommand(IProgressMonitor monitor) {
                    }

                    @Override
                    public void undoCommand(IProgressMonitor monitor) {
                    }

                    @Override
                    public boolean isUndoable() {
                        return false;
                    }
                });
            }
        });
        buttonPanel.add(button1, "grow");

        JButton button2 = new JButton("Long Running Command");
        button2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                gc.commandExecute(new AbstractCommand("Long Running") {
                    @Override
                    public void executeCommand(IProgressMonitor monitor) {
                        monitor.beginTask(
                                "Command running in separate thread (verify UI is responsive)",
                                100);
                        for (int i = 0; i < 100; i++) {
                            if (!monitor.isCanceled()) {
                                monitor.worked(1);
                                monitor.subTask(i + " / " + 100);
                                try {
                                    Thread.sleep(100);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            } else {
                                break;
                            }
                        }
                    }

                    @Override
                    public void undoCommand(IProgressMonitor monitor) {
                    }
                });
            }
        });
        buttonPanel.add(button2, "grow");

        final JCheckBox button3 = new JCheckBox("Use Command Interceptors");
        button3.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (button3.isSelected()) {
                    gc.setCommandInterceptor(DefaultDragAndDropCommand.class,
                            new ICommandInterceptor() {
                                @Override
                                public boolean intercept(AbstractGanttChart gc,
                                        ICommand cmd) {
                                    int answer = JOptionPane
                                            .showConfirmDialog(
                                                    gc,
                                                    "Are you sure you want to change the node?",
                                                    "Drag & Drop",
                                                    JOptionPane.YES_NO_OPTION);
                                    return answer == JOptionPane.OK_OPTION;
                                }
                            });

                    gc.setCommandInterceptor(
                            DefaultChangeTimelineObjectTimeSpanCommand.class,
                            new ICommandInterceptor() {
                                @Override
                                public boolean intercept(AbstractGanttChart gc,
                                        ICommand cmd) {
                                    int answer = JOptionPane
                                            .showConfirmDialog(
                                                    gc,
                                                    "Are you sure you want to change the time span?",
                                                    "Changing time span",
                                                    JOptionPane.YES_NO_OPTION);
                                    return answer == JOptionPane.OK_OPTION;
                                }
                            });
                } else {
                    gc.setCommandInterceptor(DefaultDragAndDropCommand.class,
                            null);
                    gc.setCommandInterceptor(
                            DefaultChangeTimelineObjectTimeSpanCommand.class,
                            null);

                }
            }
        });
        buttonPanel.add(button3);

        final JCheckBox button4 = new JCheckBox("Enable Dragging");
        button4.setSelected(gc.getLayerContainer().isDraggingEnabled());
        button4.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                gc.getLayerContainer().setDraggingEnabled(button4.isSelected());
            }
        });
        buttonPanel.add(button4);

        JPanel controls = new DemoControlsPanel(this);
        controls.setLayout(new MigLayout("insets 0 0 0 0, wrap 1", "[grow]",
                "[]"));
        controls.add(buttonPanel, "grow");
        controls.add(commandStackPanel, "grow");

        frame.add(BorderLayout.EAST, controls);
        frame.center();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new CommandsDemo());
    }
}
