package edu.sc.seis.gee.task;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Iterator;
import java.util.Map;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import org.apache.log4j.Category;
import edu.iris.Fissures.model.MicroSecondDate;
import edu.sc.seis.fissuresUtil.display.BasicSeismogramDisplay;
import edu.sc.seis.fissuresUtil.display.NamedColor;
import edu.sc.seis.fissuresUtil.display.PickZoneDisplay;
import edu.sc.seis.fissuresUtil.display.SeismogramDisplay;
import edu.sc.seis.fissuresUtil.display.drawable.Selection;
import edu.sc.seis.fissuresUtil.display.mouse.SDMouseAdapter;
import edu.sc.seis.fissuresUtil.display.mouse.SDMouseEvent;
import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler;
import edu.sc.seis.fissuresUtil.time.MicroSecondTimeRange;
import edu.sc.seis.fissuresUtil.xml.DataSetSeismogram;
import edu.sc.seis.gee.CommonAccess;
import edu.sc.seis.gee.configurator.ConfigurationException;

public class RangeSelectionTask extends SDMouseAdapter implements Task{
    public void configure(Map params) throws ConfigurationException {
        this.params = params;
        String parentColId = (String)params.get("colSeisDisp");
        TaskAction parentColTaskAction = CommonAccess.getCommonAccess().getTaskAction(parentColId);
        parentColSeis = (ColumnSeismogramTask)parentColTaskAction.getTask();
        String subColId = (String)params.get("subColSeis");
        childColTaskAction = CommonAccess.getCommonAccess().getTaskAction(subColId);
        bg = ButtonGroupManager.get((String)params.get(BUTTON_GROUP_ID));
    }

    public void invoke() { GlobalToolBar.setActive(this); }

    public void destroy(){
        childColTaskAction.reset();
        childColSeis = null;
    }

    public void mouseDragged(SDMouseEvent currME) {
        if(pressedME == null || currME.getX() == pressedME.getX()) return;
        ButtonModel bm = bg.getSelection();
        if (!bm.getActionCommand().equals(params.get(TASK_ID))) return;
        if(rangeSelectableComponent(currME)){
            BasicSeismogramDisplay currDisp = (BasicSeismogramDisplay)currME.getDisplay();
            MicroSecondDate mouseTime = currME.getTime();
            if(currentSelection != null){
                if(Math.abs(mouseTime.getTime() - currentSelection.getEnd().getTime()) <
                   Math.abs(mouseTime.getTime() - currentSelection.getBegin().getTime())){
                    currentSelection.setEnd(mouseTime);
                }else{
                    currentSelection.setBegin(mouseTime);
                }
            }else{
                MicroSecondTimeRange selectionRange = new MicroSecondTimeRange(currME.getTime(pressedME.getX()), mouseTime);
                currentSelection = makeNewSelection(currDisp, selectionRange);
            }
            currDisp.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
        }
    }

    public void mousePressed(SDMouseEvent me) {
        pressedME = me;
        Selection selected = getSelection(me);
        if(selected != null){
            currentSelection = selected;
            me.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
        }
    }

    public void mouseReleased(SDMouseEvent me) {
        if(currentSelection != null){
            if(currentSelection.isRemoveable()){
                currentSelection.remove();
            }else if(newSelection){
                addSelectionToDisplay(currentSelection);
                newSelection = false;
            }
            currentSelection = null;
        }
    }

    private void addSelectionToDisplay(Selection creator){
        if(childColSeis == null){
            try{
                childColSeis = (ColumnSeismogramTask)childColTaskAction.getTask();
                childColTaskAction.checkDisplayLocation();
                childColTaskAction.getJFrame().addWindowListener(new WindowAdapter(){
                            public void windowClosed(WindowEvent e){
                                parentColSeis.clearSelections();
                                childColTaskAction.reset();
                                childColSeis = null;
                            }
                        });
                creator.setChild(childColSeis.getDisplay());
            }catch(ConfigurationException e){
                GlobalExceptionHandler.handle("Difficulty getting sub-ColumnSeismogramTask for pick zone",e);
            }
        }
        DataSetSeismogram[] creatorSeismos = creator.getSeismograms();
        DataSetSeismogram[] newSeismos = new DataSetSeismogram[creatorSeismos.length];
        for(int i = 0; i < creatorSeismos.length; i++){
            newSeismos[i] = (DataSetSeismogram)creatorSeismos[i].clone();
        }
        SeismogramDisplay display = childColSeis.getDisplay();
        if(display instanceof PickZoneDisplay){
            childColSeis.makeSeismogramDisplayVisible();
            PickZoneDisplay pzd = (PickZoneDisplay)display;
            pzd.add(newSeismos, creator.getTimeConfig(), creator.getColor());
            childColSeis.checkTravelTimes();
        }
        //makes amp scale correctly.... pick displays show up with an unexpanded amplitude.
        //remove when the real bug causing the amplitude not to scale when created is fixed
        creator.getTimeConfig().shaleTime(0, 1);
        try {
            childColTaskAction.checkDisplayLocation();
        } catch (ConfigurationException e) {}
        selectionCount++;
    }

    private Selection makeNewSelection(BasicSeismogramDisplay currDisp,
                                       MicroSecondTimeRange timeRange){
        return makeNewSelection(currDisp, timeRange.getBeginTime(), timeRange.getEndTime());
    }

    private Selection makeNewSelection(BasicSeismogramDisplay currDisp,
                                       MicroSecondDate selectionBegin,
                                       MicroSecondDate selectionEnd) {
        Selection currentSelection = new Selection(new MicroSecondTimeRange(selectionBegin,
                                                                            selectionEnd),
                                                   currDisp,
                                                   selectionColors[selectionCount%selectionColors.length]);
        currDisp.addSelection(currentSelection);
        newSelection = true;
        return currentSelection;
    }

    /**
     * Returns the selection that best matches the given mouse location. Note
     * that a selection only happens at the edges. A mouse location witihin
     * the center of a Selection will not select it.
     */
    public Selection getSelection(SDMouseEvent me) {
        if( ! (rangeSelectableComponent(me)))return null;
        BasicSeismogramDisplay currDisp = (BasicSeismogramDisplay) me.getDisplay();
        Iterator e = currDisp.iterator(Selection.class);
        while(e.hasNext()){
            Selection curr = ((Selection)e.next());
            float selBeginX = me.getPixel(curr.getBegin());
            float selEndX = me.getPixel(curr.getEnd());
            if(Math.abs(selBeginX - me.getX()) <= 3 ||
               Math.abs(selEndX - me.getX()) <= 3)   return curr;
        }
        return null;
    }

    public void mouseMoved(SDMouseEvent me){
        if(rangeSelectableComponent(me)){
            BasicSeismogramDisplay currDisp = (BasicSeismogramDisplay)me.getDisplay();
            Selection over = getSelection(me);
            if ( over != null && over!= currentSelection) {
                if(currentSelection != null){
                    currentSelection.remove();
                    currentSelection = null;
                    newSelection = false;
                }
                me.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
            } else{
                MicroSecondTimeRange selRange = new MicroSecondTimeRange(me.getTime(me.getX()-5),
                                                                         me.getTime(me.getX()+5));
                if(currentSelection != null && currDisp == prevDisp){
                    currentSelection.setTime(selRange);
                }else{
                    if(currentSelection != null){
                        currentSelection.remove();
                    }
                    currentSelection = makeNewSelection(currDisp,
                                                        selRange);
                }
                me.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
            }
            prevDisp = currDisp;
        }
    }

    public void mouseExited(SDMouseEvent me){
        removeCurrentSelection();
    }

    private void removeCurrentSelection(){
        if(currentSelection != null){
            currentSelection.remove();
            currentSelection = null;
        }
        newSelection = false;
    }

    private boolean rangeSelectableComponent(SDMouseEvent me){
        if(me.getDisplay() instanceof BasicSeismogramDisplay){
            BasicSeismogramDisplay disp = (BasicSeismogramDisplay)me.getDisplay();
            if(disp.getParentDisplay() == parentColSeis.getDisplay()){
                return true;
            }
        }
        return false;
    }

    protected Selection currentSelection;

    private BasicSeismogramDisplay prevDisp;

    protected boolean newSelection;

    private Map params;

    private MouseEvent pressedME;

    private ButtonGroup bg;

    private Color[] selectionColors = { new NamedColor(Color.RED, 64, "red"),
            new NamedColor(Color.YELLOW.darker(), 64, "yellow"),
            new NamedColor(Color.GREEN.darker(), 64, "green"),
            new NamedColor(Color.BLUE, 64, "blue")};

    private static Category logger = Category.getInstance(RangeSelectionTask.class.getName());

    private TaskAction childColTaskAction;

    private ColumnSeismogramTask childColSeis, parentColSeis;

    private int selectionCount = 0;
}// RangeSelectionTask
