package net.sourceforge.ganttproject.gui.taskproperties;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.action.CancelAction;
import net.sourceforge.ganttproject.action.OkAction;
import net.sourceforge.ganttproject.gui.GPPropertySheet;
import net.sourceforge.ganttproject.gui.HierarchicalPropertySheetBuilderImpl;
import net.sourceforge.ganttproject.gui.IPropertySheetBuilder;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.gui.UIFacade.DialogInitHook;
import net.sourceforge.ganttproject.gui.options.model.CLOBOption;
import net.sourceforge.ganttproject.gui.options.model.DefaultStringOption;
import net.sourceforge.ganttproject.gui.options.model.GPOption;
import net.sourceforge.ganttproject.gui.options.model.GPOptionGroup;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.plugins.PluginManager;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskMutator;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingConstants;
import org.flexdock.docking.DockingManager;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.defaults.AbstractDockable;
import org.flexdock.docking.defaults.DefaultDockingPort;
import org.flexdock.docking.state.PersistenceException;
import org.flexdock.perspective.Perspective;
import org.flexdock.perspective.PerspectiveManager;
import org.flexdock.perspective.persist.OutputStreamPersistenceHandler;

public class GPTaskPropertySheet implements GPPropertySheet {
    private final UIFacade myUIFacade;
    
    private Task myTask;
    private TaskMutator myTaskMutator;
    private GPOptionGroup mySchedulePropertiesGroup;
    private boolean areDefaultNotes;
    private JTextArea myNotesArea;
    private final IGanttProject myProject;
    private final IPropertySheetBuilder myBuilder;
    private IPropertySheetComponent[] myComponents;
    private final LayoutOption myLayoutOption;
    private final GPOptionGroup myOptionGroup;
    private DefaultDockingPort myDockingPort;
    private Perspective myPerspective;
    
    class LayoutOption extends DefaultStringOption implements OutputStreamPersistenceHandler.StreamFactory, CLOBOption {
        public LayoutOption() {
            super("taskProperties.layout", "");
        }
        @Override
        public String getPersistentValue() {
            return getValue();
        }
        @Override
        public void loadPersistentValue(String value) {
            if (value==null || "".equals(value.trim())) {
                return;
            }
            setValue(value, true);
            try {
                PerspectiveManager.getInstance().load("task-properties");
                myPerspective = PerspectiveManager.getInstance().getPerspective("task-properties");
                if (myPerspective==null) {
                    myPerspective = new Perspective("task-properties", "task-properties");
                    PerspectiveManager.getInstance().add(myPerspective, true);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (PersistenceException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public InputStream createInputStream(String persistenceKey) {
            return new ByteArrayInputStream(getValue().getBytes());
        }
        
        public OutputStream createOutputStream(String persistenceKey) {
            final ByteArrayOutputStream stream = new ByteArrayOutputStream(1024) {
                @Override
                public void close() throws IOException {
                    super.close();
                    LayoutOption.this.setValue(toString("utf-8"), true);
                    System.err.println(LayoutOption.this.getValue());
                }
            };
            return stream;
        }
        
        
    }
    
    public GPTaskPropertySheet(IGanttProject project, UIFacade uiFacade) {
        myProject = project;
        myUIFacade = uiFacade;
        myComponents = (IPropertySheetComponent[]) PluginManager.getExtensions(
                IPropertySheetComponent.EXTENSION_POINT_ID, IPropertySheetComponent.class);
        myBuilder = new HierarchicalPropertySheetBuilderImpl();
        myLayoutOption = new LayoutOption();
        myOptionGroup = new GPOptionGroup("taskProperties", new GPOption[] {myLayoutOption});
        myDockingPort = new DefaultDockingPort("task-properties");
        PerspectiveManager.setPersistenceHandler(new OutputStreamPersistenceHandler(myLayoutOption));
    }
    

    public GPOptionGroup getOptions() {
        return myOptionGroup;
    }
    
    public void show(Task task) {
        if (myPerspective==null) {
            myPerspective = new Perspective("task-properties", "task-properties");
            PerspectiveManager.getInstance().add(myPerspective, true);
        }
        PerspectiveManager.getInstance().setCurrentPerspective(myPerspective.getPersistentId());
        final Action[] actions = new Action[] {
            new OkAction() {
                public void actionPerformed(ActionEvent e) {
                    onCommit();
                }
            }, 
            new CancelAction(){
                public void actionPerformed(ActionEvent e) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            myUIFacade.getUndoManager().undo();
                        }
                    });
                }
            }
        };
        myTask = task;
        myTaskMutator = task.createMutator();
        try {
          myUIFacade.getUndoManager().undoableEdit("Edit task properties", new Runnable() {
            public void run() {
                myUIFacade.showDialog(getContent(), actions, "Task properties", new DialogInitHook() {
                    public void onInit(JDialog dialog) {
                        if (!"".equals(myLayoutOption.getValue())) {
                            try {
                                myPerspective.load(myDockingPort);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }                
                    }
                  });
            }
          });
          PerspectiveManager.getInstance().store("task-properties", myDockingPort);
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (PersistenceException e1) {
            e1.printStackTrace();
        }
    }
    
    protected void onCancel() {
        for (IPropertySheetComponent nextComponent : myComponents) {
            nextComponent.onRollback();
        }
        myTaskMutator.rollback();
    }
    protected void onCommit() {
        for (IPropertySheetComponent nextComponent : myComponents) {
            nextComponent.onCommit();
        }
        myTaskMutator.commit();
        
    }

    private HashMap<IPropertySheetComponent, Dockable> myComponent2wrapper = new HashMap<IPropertySheetComponent, Dockable>();

    private JPanel myNotesPanel;
    private Component getContent() {
        {
            for (IPropertySheetComponent nextComponent : myComponents) {
                nextComponent.init(myTask, myTaskMutator, myProject, myUIFacade);            	
            }
            Dockable first = null;
            for (final IPropertySheetComponent nextComponent : myComponents) {
                System.err.println("next component="+nextComponent.getTitle());
                final JComponent nextUIComponent = (JComponent) nextComponent.getAdapter(JComponent.class);
                nextUIComponent.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
                Dockable nextDockable = myComponent2wrapper.get(nextComponent);
                if (nextDockable == null) {
                    final JPanel newWrapper = new JPanel(new BorderLayout());
                    nextDockable = DockingManager.registerDockable(new AbstractDockable(nextComponent.getTitle()) {
                        @Override
                        public Component getComponent() {
                            return newWrapper;
                        }
                    });
                    nextDockable.getDockingProperties().setDockableDesc(nextComponent.getTitle());
                    DockingManager.dock(nextDockable, (DockingPort)myDockingPort, DockingConstants.CENTER_REGION);
                    myComponent2wrapper.put(nextComponent, nextDockable);
                }
                JComponent wrapper = (JComponent) nextDockable.getComponent();
                wrapper.removeAll();
                wrapper.add(nextUIComponent, BorderLayout.CENTER);            
                if (first==null) {
                    first = nextDockable;
                }
            }
            if (first!=null) {
                first.getDockingProperties().setActive(true);
            }
        }
        {
            final int columns = 20;
            String notes = myTask.getNotes();
            if (notes==null || "".equals(notes.trim())) {
                notes = GanttLanguage.getInstance().getText("option.taskProperties.notes.defaultValue");
                areDefaultNotes = true;
            }
            myNotesArea = new JTextArea(notes);
            myNotesArea.setColumns(columns);
            myNotesArea.addFocusListener(new FocusAdapter() {
                @Override
                public void focusGained(FocusEvent e) {
                    super.focusGained(e);
                    if (areDefaultNotes) {
                        myNotesArea.setText("");
                        areDefaultNotes = false;
                    }
                }
                
            });
            myNotesArea.setBorder(BorderFactory.createLoweredBevelBorder());
            
            if (myNotesPanel == null) {
                myNotesPanel = new JPanel(new BorderLayout());
                myNotesPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
                Dockable notesDockable = DockingManager.registerDockable(new AbstractDockable("notes") {
                    @Override
                    public Component getComponent() {
                        return myNotesPanel;
                    }
                });
                notesDockable.getDockingProperties().setDockableDesc("Notes");
                DockingManager.dock(notesDockable, (DockingPort)myDockingPort, DockingConstants.EAST_REGION);
            } 
            else {
                myNotesPanel.removeAll();
            }
            myNotesPanel.add(new JScrollPane(myNotesArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
        }
        return myDockingPort;
    }
    
}
