/*
 * Created on 05.04.2005
 */
package net.sourceforge.ganttproject;

import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.AbstractTableModel;
import net.sourceforge.ganttproject.action.GPAction;
import net.sourceforge.ganttproject.calendar.GPCalendar;
import net.sourceforge.ganttproject.calendar.IntervalManager;
import net.sourceforge.ganttproject.chart.Chart;
import net.sourceforge.ganttproject.chart.ChartModelImpl;
import net.sourceforge.ganttproject.chart.ChartSelection;
import net.sourceforge.ganttproject.chart.ChartSelectionListener;
import net.sourceforge.ganttproject.chart.overview.OverviewPaneAction;
import net.sourceforge.ganttproject.document.Document;
import net.sourceforge.ganttproject.document.DocumentCreator;
import net.sourceforge.ganttproject.document.DocumentManager;
import net.sourceforge.ganttproject.gui.GPPropertySheet;
import net.sourceforge.ganttproject.gui.GanttStatusBar;
import net.sourceforge.ganttproject.gui.GanttTabbedPane;
import net.sourceforge.ganttproject.gui.ProjectUIFacade;
import net.sourceforge.ganttproject.gui.ProjectUIFacadeImpl;
import net.sourceforge.ganttproject.gui.TableHeaderUIFacade;
import net.sourceforge.ganttproject.gui.UIConfiguration;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.gui.UIFacade.DialogInitHook;
import net.sourceforge.ganttproject.gui.options.model.GPOptionChangeListener;
import net.sourceforge.ganttproject.gui.scrolling.ScrollingManager;
import net.sourceforge.ganttproject.gui.taskproperties.GPTaskPropertySheet;
import net.sourceforge.ganttproject.gui.zoom.ZoomManager;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.parser.ParserFactory;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.roles.RoleManager;
import net.sourceforge.ganttproject.task.CustomColumnsManager;
import net.sourceforge.ganttproject.task.CustomColumnsStorage;
import net.sourceforge.ganttproject.task.TaskCategoryManager;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.time.TimeUnitStack;
import net.sourceforge.ganttproject.time.gregorian.GPTimeUnitStack;
import net.sourceforge.ganttproject.undo.GPUndoManager;
import net.sourceforge.ganttproject.undo.UndoManagerImpl;

import org.eclipse.core.runtime.IAdaptable;
import org.jdesktop.swingx.calendar.DatePickerFormatter;

/**
 * This class is designed to be a GanttProject-after-refactorings. I am going to
 * refactor GanttProject in order to make true view communicating with other
 * views through interfaces. This class is intentionally package local to
 * prevent using it in other packages (use interfaces rather than concrete
 * implementations!)
 * 
 * @author dbarashev
 */
abstract class GanttProjectBase extends JFrame implements IGanttProject, UIFacade {
    static {
        //UIManager.getDefaults().put("TreeUI", )
    }
    private final ViewManagerImpl myViewManager;
    private final List<ProjectEventListener> myModifiedStateChangeListeners = new ArrayList<ProjectEventListener>();
    private final UIFacadeImpl myUIFacade;
    private final GanttStatusBar statusBar;
    private final GPTimeUnitStack myTimeUnitStack;
    private final ProjectUIFacadeImpl myProjectUIFacade;
    private final DocumentManager myDocumentManager;
    private final GanttTabbedPane myTabPane;
    private final GPUndoManager myUndoManager;
    private final TaskCategoryManager myTaskCategoryManager;
    private final CustomColumnsManager myTaskCustomColumnManager;
    private final CustomColumnsStorage myTaskCustomColumnStorage;
    
    protected GanttProjectBase() {
        super("Gantt Chart");
        myTaskCategoryManager = new TaskCategoryManager();
        statusBar = new GanttStatusBar(this);
        myTabPane = new GanttTabbedPane(); 
        myViewManager = new ViewManagerImpl(myTabPane);
        addProjectEventListener(myViewManager);
        myTimeUnitStack = new GPTimeUnitStack(getLanguage());
        myTimeUnitStack.getCalendar().setBusinessHours(
                getLanguage().getBusinessHoursStart(), 
                getLanguage().getBusinessHoursEnd());
        DatePickerFormatter.setDateFormats(myTimeUnitStack.getDateFormats());
        myUIFacade =new UIFacadeImpl(this, statusBar, getProject(), (UIFacade)this);
        myDocumentManager = new DocumentCreator(this, getUIFacade(),  null) {
            protected ParserFactory getParserFactory() {
                return GanttProjectBase.this.getParserFactory();
            }

            protected TableHeaderUIFacade getVisibleFields() {
                return getUIFacade().getTaskTree().getVisibleFields();
            }
            
        };        
        myUndoManager = new UndoManagerImpl((IGanttProject) this,
                null, myDocumentManager) {
            protected ParserFactory getParserFactory() {
                return GanttProjectBase.this.getParserFactory();
            }            
        };
        Mediator.registerUndoManager(myUndoManager);
        myProjectUIFacade = new ProjectUIFacadeImpl(myUIFacade, myDocumentManager, myUndoManager);
        myTaskCustomColumnStorage = new CustomColumnsStorage();
        myTaskCustomColumnManager = new CustomColumnsManager(myTaskCustomColumnStorage);
    }


    private GanttLanguage getLanguage() {
        return GanttLanguage.getInstance();
    }

    public void addProjectEventListener(ProjectEventListener listener) {
        myModifiedStateChangeListeners.add(listener);
    }
    public void removeProjectEventListener(ProjectEventListener listener) {
        myModifiedStateChangeListeners.remove(listener);
    }
    
    protected void fireProjectModified(boolean isModified){
        for (int i=0; i<myModifiedStateChangeListeners.size(); i++) {
            ProjectEventListener next = (ProjectEventListener) myModifiedStateChangeListeners.get(i);
            try {
                if (isModified) {
                    next.projectModified();
                }
                else {
                    next.projectSaved();
                }
            }
            catch(Exception e) {
                showErrorDialog(e);
            }
        }
    }
    
    protected void fireProjectClosed() {
        for (int i=0; i<myModifiedStateChangeListeners.size(); i++) {
            ProjectEventListener next = (ProjectEventListener) myModifiedStateChangeListeners.get(i);
            next.projectClosed();
        }        
    }
    
    protected void fireProjectWillBeOpened() {
        for (ProjectEventListener listener : myModifiedStateChangeListeners) {
            listener.projectWillBeOpened();
        }
    }
    
    protected void fireProjectOpened() {
        for (ProjectEventListener listener : myModifiedStateChangeListeners) {
            listener.projectOpened();
        }
    }
    
    // ////////////////////////////////////////////////////////////////
    // UIFacade
    public ProjectUIFacade getProjectUIFacade() {
        return myProjectUIFacade;
    }
    
    public UIFacade getUIFacade() {
        return myUIFacade;
    }

    public Frame getMainFrame() {
        return myUIFacade.getMainFrame();
    }

    public ScrollingManager getScrollingManager() {
        return myUIFacade.getScrollingManager();
    }
    
    public ZoomManager getZoomManager() {
        return myUIFacade.getZoomManager();
    }
    
    public GPUndoManager getUndoManager() {
        return myUndoManager;
    }
    public void setStatusText(String text) {
        myUIFacade.setStatusText(text);
    }
    
    public void showDialog(Component content, Action[] actions) {
        myUIFacade.showDialog(content,actions);
    }

    public void showDialog(Component content, Action[] actions, String title) {
        myUIFacade.showDialog(content,actions,title);
    }
    
    public void showDialog(Component content, Action[] buttonActions, String title, DialogInitHook initHook) {
        myUIFacade.showDialog(content, buttonActions, title, initHook);
    }


    public void showUndecoratedDialog(Component content, Action[] actions) {
        myUIFacade.showUndecoratedDialog(content, actions);
    }
    public void showPopupMenu(Component invoker, Component content, int x, int y) {
        myUIFacade.showPopupMenu(invoker, content, x, y);
    }
    public UIFacade.Choice showConfirmationDialog(String message, String title) {
        return myUIFacade.showConfirmationDialog(message, title);
    }

    public void showErrorDialog(String message) {
        myUIFacade.showErrorDialog(message);
    }

    public void showErrorDialog(Throwable e) {
        myUIFacade.showErrorDialog(e);
    }

    public void showInformationDialog(String message) {
        myUIFacade.showInformationDialog(message);
    }
    
    public void logErrorMessage(Exception e) {
        myUIFacade.logErrorMessage(e);
    }
    public void showPopupMenu(Component invoker, Action[] actions, int x, int y) {
        myUIFacade.showPopupMenu(invoker, actions, x, y);
    }

//    public void changeWorkingDirectory(File directory) {
//        myUIFacade.changeWorkingDirectory(directory);
//    }

    public void setWorkbenchTitle(String title) {
        myUIFacade.setWorkbenchTitle(title);
    }

    protected GPViewManager getViewManager() {
        return myViewManager;
    }

    private class ViewManagerImpl implements GPViewManager, ProjectEventListener {
        private GanttTabbedPane myTabs;
        private List myViews = new ArrayList();
        private GPViewImpl mySelectedView;
        ViewManagerImpl(GanttTabbedPane tabs) {
            myTabs = tabs;
            myTabs.addChangeListener(new ChangeListener() {

                public void stateChanged(ChangeEvent e) {
                    GPViewImpl selectedView = (GPViewImpl) myTabs.getSelectedUserObject();
                    if (mySelectedView==selectedView) {
                        return;
                    }
                    if (mySelectedView!=null) {
                        mySelectedView.setActive(false);
                    }
                    mySelectedView = selectedView;
                    mySelectedView.setActive(true);
                }
            });
        }

        public GPView createView(IAdaptable adaptable, Icon icon) {
            GPView view = new GPViewImpl(this, myTabs, (Container) adaptable
                    .getAdapter(Container.class), (Chart)adaptable.getAdapter(Chart.class), icon);
            myViews.add(view);
            return view;
        }
        public Action getCopyAction() {
            return myCopyAction;
        }

        public Action getCutAction() {
            return myCutAction;
        }

        public Action getPasteAction() {
            return myPasteAction;
        }
        

        ////////////////////////////////////////////
        //ProjectEventListener
        public void projectModified() {
        }

        public void projectSaved() {
        }

        public void projectClosed() {
            for (int i=0; i<myViews.size(); i++) {
                GPViewImpl nextView = (GPViewImpl) myViews.get(i);
                nextView.reset();
            }
        }
        public void projectWillBeOpened() {
        }
        public void projectOpened() {
        }

        private void updateActions() {
            ChartSelection selection = mySelectedView.myChart.getSelection();
            myCopyAction.setEnabled(false==selection.isEmpty());
            myCutAction.setEnabled(false==selection.isEmpty() && selection.isDeletable().isOK());
        }
        
        private final GPAction myCopyAction = new GPAction() {
            {
                putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK));
            }
            protected String getIconFilePrefix() {
                return "copy_";
            }
            public void actionPerformed(ActionEvent e) {
                mySelectedView.myChart.getSelection().startCopyClipboardTransaction();
            }
            protected String getLocalizedName() {
                return getI18n("copy");
            }            	
        };
        private final GPAction myCutAction = new GPAction() {
            {
                putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.CTRL_DOWN_MASK));
            }
            protected String getIconFilePrefix() {
                return "cut_";
            }
            public void actionPerformed(ActionEvent e) {
                mySelectedView.myChart.getSelection().startMoveClipboardTransaction();
            }
            protected String getLocalizedName() {
                return getI18n("cut");
            }            	
        };
        private final GPAction myPasteAction = new GPAction() {
            {
                putValue(Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK));
            }
            protected String getIconFilePrefix() {
                return "paste_";
            }
            public void actionPerformed(ActionEvent e) {
                ChartSelection selection = mySelectedView.myChart.getSelection();
                mySelectedView.myChart.paste(selection);
                selection.commitClipboardTransaction();
            }
            
            protected String getLocalizedName() {
                return getI18n("paste");
            }            	
        };
    }

    private class GPViewImpl implements GPView, ChartSelectionListener {
        private final GanttTabbedPane myTabs;

        private int myIndex;

        private Container myComponent;

        private boolean isVisible;

        private final Icon myIcon;

        private final Chart myChart;

        private final ViewManagerImpl myManager;

        GPViewImpl(ViewManagerImpl manager, GanttTabbedPane tabs, Container component, Chart chart, Icon icon) {
            myManager = manager;
            myTabs = tabs;
            myComponent = component;
            myIcon = icon;
            myChart = chart;
            assert myChart!=null;
        }

        public void setActive(boolean active) {
            if (active) {
                myChart.addSelectionListener(this);
            }
            else {
                myChart.removeSelectionListener(this);
            }
        }

        public void reset() {
            myChart.reset();
        }

        public void setVisible(boolean isVisible) {
            String tabName = "New view";
            //JScrollPane jsp = new JScrollPane();
            if (isVisible) {
                myChart.setTaskManager(GanttProjectBase.this.getTaskManager());
                tabName = myChart.getName();
                //jsp.getViewport().add(myComponent);

                myTabs.addTab(tabName, myIcon, myComponent, tabName, this);
                myTabs.setSelectedComponent(myComponent);
                myIndex = myTabs.getSelectedIndex();

            } else {
                myTabs.remove(myIndex);
            }
            this.isVisible = isVisible;
        }

        public boolean isVisible() {
            return isVisible;
        }

        public void selectionChanged() {
            myManager.updateActions();
        }

    }

    protected static class RowHeightAligner implements GPOptionChangeListener {
        private ChartModelImpl myGanttViewModel;

        private GanttTree2 myTreeView;

        // TODO: 1.12 refactor and get rid of using concrete implementations of
        // gantt view model
        // and tree view
        public RowHeightAligner(GanttTree2 treeView,
                ChartModelImpl ganttViewModel) {
            myGanttViewModel = ganttViewModel;
            myTreeView = treeView;
        }

        public void optionsChanged() {
            myTreeView.getTable().setRowHeight(myGanttViewModel.setRowHeight());
            AbstractTableModel model = (AbstractTableModel) myTreeView.getTable().getModel();
            model.fireTableStructureChanged();
            myTreeView.updateUI();
        }

    }

    public GanttTabbedPane getTabs() {
        return myTabPane;
    }

    public IGanttProject getProject() {
        return this;
    }
    public TimeUnitStack getTimeUnitStack() {
        return myTimeUnitStack;
    }
    public GPCalendar getActiveCalendar() {
        return myTimeUnitStack.getCalendar();
    }
    public CustomColumnsManager getTaskCustomColumnManager() {
        return myTaskCustomColumnManager;
    }
    
    public CustomColumnsStorage getCustomColumnsStorage() {
        return myTaskCustomColumnStorage;
    }
    
    public GPPropertySheet getTaskPropertySheet() {
        return myUIFacade.getTaskPropertySheet();
    }


    public abstract String getProjectName();

    public abstract void setProjectName(String projectName);

    public abstract String getDescription();

    public abstract void setDescription(String description);

    public abstract String getOrganization();

    public abstract void setOrganization(String organization);

    public abstract String getWebLink();

    public abstract void setWebLink(String webLink);

    //public abstract Task newTask();

    public abstract GanttLanguage getI18n();

    public abstract UIConfiguration getUIConfiguration();

    public abstract HumanResourceManager getHumanResourceManager();

    public abstract RoleManager getRoleManager();

    public abstract TaskManager getTaskManager();

    public TaskCategoryManager getTaskCategoryManager(){
        return myTaskCategoryManager;
    }
    
    public abstract IntervalManager getIntervalManager();

    public abstract TaskContainmentHierarchyFacade getTaskContainment();

    public abstract void setModified();

    public abstract void close();

    public abstract Document getDocument();

    protected GanttStatusBar getStatusBar() {
        return statusBar;
    }

    public DocumentManager getDocumentManager() {
        return myDocumentManager;
    }
    
    public abstract ParserFactory getParserFactory();

}
