package net.sourceforge.ganttproject.datafilter.ui;

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.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.jdesktop.swingx.JXHyperlink;

import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.datafilter.ExportFilterAction;
import net.sourceforge.ganttproject.datafilter.FilterOptionsDialogAction;
import net.sourceforge.ganttproject.datafilter.TaskGroup;
import net.sourceforge.ganttproject.datafilter.task.FilteredTasks;
import net.sourceforge.ganttproject.datafilter.task.TaskFilterFactory;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.language.GanttLanguage.Event;
import net.sourceforge.ganttproject.language.GanttLanguage.Listener;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.task.TaskSelectionManager;


/**
 * Implementation of filter panel user interface - GUI. 
 * 
 * Created 12.2007
 * 
 * @author Joanna Muras 
 */
public class FilterUIImpl implements FilterUI {

    /** Instance of appropriate task filter */
    private FilteredTasks mySelectedFilter;
    /** Filter task tree - filtered tasks */
    private FilterTree myFilterTree;
    private final TaskManager myTaskManager;
    private TaskSelectionManager myFilterSelectionManager;
    private JPanel myFilterPanel;
    private final IGanttProject myProject; 
    private final UIFacade myFacade;
    private final List<FilteredTasks> myFilters = new ArrayList<FilteredTasks>();
    private final FilterOptionsDialogAction myFilterOptionsAction;
    private final ExportFilterAction myExportFilterAction;
    /**
     * Create filter panel, filter tree and appropriate filter model
     * @param task manager
     */
    public FilterUIImpl(IGanttProject project, UIFacade facade, TaskManager taskManager, TaskSelectionManager selectionManager) {

        myProject = project; 
        myFacade = facade;
        myTaskManager = taskManager;
        myFilterSelectionManager = new TaskSelectionManager();
        /* Create filter task tree */
        myFilterTree = new FilterTree(project, facade, selectionManager, myFilterSelectionManager);

        TaskFilterFactory[] filterFactories = TaskFilterFactory.Access.getExtensions();
        for (TaskFilterFactory factory : filterFactories) {
            FilteredTasks fileredTasks = factory.createFilter(project);
            /* Add listeners on task properties/structure change */
            myFilters.add(fileredTasks);
        }

        for (FilteredTasks filter : myFilters) {
            project.getParserFactory().addSaver(filter.getSaver());
            project.getParserFactory().addTagHandler(filter.getLoader());
        }
        mySelectedFilter = getFilter(0);
        myFilterOptionsAction = new FilterOptionsDialogAction(facade);
        myFilterOptionsAction.setFilter(mySelectedFilter);
        
        myExportFilterAction = new ExportFilterAction(facade, project);
        /* Add action listeners on selected filter */
        addActionListeners(mySelectedFilter);

        /* Add listeners on language change */
        GanttLanguage.getInstance().addListener(new Listener () {
            public void languageChanged(Event event) {
                refreshFilterComponent();
            }
        });

        /* Set model for filter task tree - use filter created by filter factory */
        setModel(mySelectedFilter.getTaskGroups());
        /* Set options for filters - use filter created by filter factory */
        //myFilterTree.setFilterOptionsAction(mySelectedFilter.getSettings());
        /* Set auto fix list - use filter created by filter factory */
        myFilterTree.setAutoFixList(mySelectedFilter.getAutoFixList());
        /* Set auto export filter action - list of task in filter */
    }

    /**
     * Create filter panel, filter tree and filter choice box
     * @return filter panel
     */
    public JPanel createFilterComponent() {
        /* Creates filter panel */
        myFilterPanel = new JPanel(new BorderLayout());
        myFilterPanel.setSize(new Dimension(250,600));
        /* Add task tree to filter panel */
        myFilterPanel.add(createFilterScrollPane(), BorderLayout.CENTER);
        /* Add ComboBox with filter choice to filter panel */
        myFilterPanel.add(createFilterChoiceBox(0), BorderLayout.NORTH);
        return myFilterPanel;
    }

    /**
     * Refresh comboBox with filter labels and filter tree with task groups
     */
    private void refreshFilterComponent() {

        /* Get existing comboBox selected index and replace existing comboBox with one in proper language */
        for ( Component component: myFilterPanel.getComponents() ){
            if (component instanceof JComboBox) {
                myFilterPanel.remove(component);
                JComboBox comboBox = (JComboBox)component;
                int selectedIndex = comboBox.getSelectedIndex();
                /* Add ComboBox with filter choice to filter panel */
                myFilterPanel.add(createFilterChoiceBox(selectedIndex), BorderLayout.NORTH);
            }
        }
        /* Refresh filter tree */
        setModel(mySelectedFilter.getTaskGroups());
    }

    /**
     * Function performed on change of filter - ComboBox. 
     */
    public void onChangingFilter(FilteredTasks newFilter) {

        /* Remove action listeners on previous filter */
        removeActionListeners(mySelectedFilter);
        mySelectedFilter = newFilter;
        /* Add action listeners on selected filter */
        addActionListeners(mySelectedFilter);

        /* Filter all tasks according to the groups defined in constructor */
        TaskGroup[] taskGroups = mySelectedFilter.getTaskGroups();
        for (int i=0; i<taskGroups.length; i++) {
            taskGroups[i].filterTasks(); 
        }
        /* Set model for filter task tree - use filter created by filter factory */
        setModel(mySelectedFilter.getTaskGroups());
        /* Set options for filters - use filter created by filter factory */
        //myFilterTree.setFilterOptionsAction(newFilter.getSettings());
        myFilterOptionsAction.setFilter(newFilter);
        /* Update list of auto fix options */
        mySelectedFilter.updateAutoFixList();
        /* Set auto fix list - use filter created by filter factory */
        myFilterTree.setAutoFixList(mySelectedFilter.getAutoFixList());
        /* Set auto export filter action - list of task in filter */
        myExportFilterAction.setFilter(mySelectedFilter);
    }

    /**
     * Add listeners on task structure/value change
     */
    private void addActionListeners(FilteredTasks filteredTasks) {
        /* Add listener to tasks in tree table - task change */
        myTaskManager.addTaskListener(filteredTasks);
        /* Add listeners on tasks change */
        filteredTasks.addFilteringEventListener(myFilterTree);
    }

    /**
     * Remove all listeners associated with current version of filter
     */
    private void removeActionListeners(FilteredTasks filteredTasks) {
        /* Remove listener to tasks in tree table - task change */
        myTaskManager.removeTaskListener(filteredTasks);
        /* Remove listeners on tasks change */
        filteredTasks.removeFilteringEventListener(myFilterTree);
    }

    /** 
     * Create appropriate instance of filter: 
     * @param type of filter
     * 0 - EndDateFilteredTasks (default)
     * 1 - PriorityFilteredTasks
     * @return appropriate instance of task filter
     */
    private FilteredTasks getFilter(int filterIndex) {
        if (filterIndex>=0 && filterIndex<myFilters.size()) {
            return myFilters.get(filterIndex);
        }
        throw new IllegalArgumentException();
    }

    /**
     * Set model for filter tree
     * @param groups of tasks
     */
    private void setModel(TaskGroup[] taskGroup) {
        /* Set model for filter task tree  - use filter created by filter factory */
        myFilterTree.setModel(taskGroup);
    }

    /**
     * Create combo box for choice of filter
     * @param list of filter names
     * @return combo box with names of filters
     */
    private JComponent createFilterChoiceBox(int selectedIndex) {
        /* ComboBox with choice of filters to apply */
        List<String> filterNames = new ArrayList<String>();
        for (FilteredTasks filter : myFilters) {
            filterNames.add(filter.getName());
        }
        final JComboBox filterChoiceBox = new JComboBox(filterNames.toArray());
        /* Select default index - EndDateFilteredTasks */
        filterChoiceBox.setSelectedIndex(selectedIndex);
        /* Add action listener on filter change */
        
        filterChoiceBox.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                /* Get selection of filter */
                int selectedItem = filterChoiceBox.getSelectedIndex();
                FilteredTasks newFilter = getFilter(selectedItem);
                onChangingFilter(newFilter);
            }
        });
        JPanel result = new JPanel(new BorderLayout());
        result.setBorder(BorderFactory.createEmptyBorder(5, 2, 5, 2));
        result.add(filterChoiceBox, BorderLayout.CENTER);
        
        JPanel menuPanel = new JPanel();
        menuPanel.add(new JXHyperlink(myFilterOptionsAction));
        menuPanel.add(new JXHyperlink(myExportFilterAction));
        
        JPanel menuPanelHolder = new JPanel(new BorderLayout());
        menuPanelHolder.add(menuPanel, BorderLayout.WEST);
        result.add(menuPanelHolder, BorderLayout.SOUTH);
        return result;
    }

    /**
     * Create scroll panel with filter tree
     * @return scrol panel
     */
    private JScrollPane createFilterScrollPane() {
        /* Create scroll panel for filter task tree */
        JScrollPane filterScrollPane = new JScrollPane(myFilterTree);
        return filterScrollPane;
    }
}