package cnp.ew.charts;

import java.awt.*;
import cnp.ew.displayer.*;
import cnp.ew.diagram.*;
import cnp.ew.util.*;
import cnp.ew.lightweight.*;

abstract public class CpAbstractAxisBasedChartLc extends CpAbstractChartLc
implements CpObserver
{

    CpAbstractChartBackdropDisplayer backdropDisplayer;
    CpValueAxisDisplayer valueAxisDisplayer;
    CpCategoryAxisDisplayer categoryAxisDisplayer;



    public CpAbstractAxisBasedChartLc()
    {
        super();
        chartDataModel = new CpChartDataModel();
    }


    public void setBackdropDisplayer(CpAbstractChartBackdropDisplayer newDisplayer)
    {
        backdropDisplayer = newDisplayer;
        backdropDisplayer.setValueAxisModel(valueAxisModel);
        backdropDisplayer.setChartDataModel(chartDataModel);
    }

    public void setValueAxisDisplayer(CpValueAxisDisplayer newDisplayer)
    {
        valueAxisDisplayer = newDisplayer;
        valueAxisDisplayer.setChartDataModel(chartDataModel);
        valueAxisDisplayer.setValueAxisModel(valueAxisModel);
    }

    public void setCategoryAxisDisplayer(CpCategoryAxisDisplayer newDisplayer)
    {
        categoryAxisDisplayer = newDisplayer;
        categoryAxisDisplayer.setChartDataModel(chartDataModel);
    }


    public void setChartDataModel(CpChartDataModel model)
    {
        super.setChartDataModel(model);
        valueAxisDisplayer.setChartDataModel(chartDataModel);
        categoryAxisDisplayer.setChartDataModel(chartDataModel);
        backdropDisplayer.setChartDataModel(chartDataModel);
    }

    public void layout()
    {
        super.layout();
        updateMaxAndStep();
    }

    public void update(CpObservable o, int facet, Object arg)
    {
        super.update(o, facet, arg);
        if (o == chartDataModel) {
            updateMaxAndStep();
        }
    }

    void updateMaxAndStep()
    {
        CpAbstractAxisDisplayer verticalAxisDisplayer = getVerticalAxisDisplayer();
        CpAbstractAxisDisplayer horizontalAxisDisplayer = getHorizontalAxisDisplayer();
        Rectangle bounds = getDisplayRect();

        int verticalTopTickOffset = verticalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int verticalBottomTickOffset = verticalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);
        int horizontalLeftTickOffset = horizontalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int horizontalRightTickOffset = horizontalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);
        int backdropTopOffset = backdropDisplayer.getTopAlignmentOffset();
        int backdropRightOffset = backdropDisplayer.getRightAlignmentOffset();

        int verticalAxisWidth = Math.max(verticalAxisDisplayer.preferredSize(this).width, horizontalLeftTickOffset);
        int horizontalAxisHeight = Math.max(horizontalAxisDisplayer.preferredSize(this).height, verticalBottomTickOffset);

        Rectangle innerGraphRect = new Rectangle(
            bounds.x + verticalAxisWidth,
            bounds.y + verticalTopTickOffset,
            bounds.width - verticalAxisWidth - horizontalRightTickOffset,
            bounds.height - horizontalAxisHeight - verticalTopTickOffset);

        valueAxisModel.autoRecalcMaxAndStep(
            valuesDisplayer.getMinimumValueRequiredForDisplay(),
            valueAxisDisplayer.getMaximumSteps(this, innerGraphRect)
        );
    }

    Rectangle getDisplayRect()
    {
        Rectangle clientRect = getClientRect();
        // Make sure these LC's never draw in a goofy way, by max -ing their
        // size against arbitrary values. Any overhang will be clipped.
        clientRect.width = Math.max(150, clientRect.width);
        clientRect.height = Math.max(75, clientRect.height);
        return clientRect;
    }

    public void paint(Graphics g, Rectangle clipRect)
    {

        CpAbstractAxisDisplayer verticalAxisDisplayer = getVerticalAxisDisplayer();
        CpAbstractAxisDisplayer horizontalAxisDisplayer = getHorizontalAxisDisplayer();
        Rectangle bounds = getDisplayRect();

        g.setColor(getBackground());
        g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);

        int verticalTopTickOffset = verticalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int verticalBottomTickOffset = verticalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);
        int horizontalLeftTickOffset = horizontalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int horizontalRightTickOffset = horizontalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);
        int backdropTopOffset = backdropDisplayer.getTopAlignmentOffset();
        int backdropRightOffset = backdropDisplayer.getRightAlignmentOffset();

        int verticalAxisWidth = Math.max(verticalAxisDisplayer.preferredSize(this).width, horizontalLeftTickOffset);
        int horizontalAxisHeight = Math.max(horizontalAxisDisplayer.preferredSize(this).height, verticalBottomTickOffset);

        Rectangle innerGraphRect = new Rectangle(
            bounds.x + verticalAxisWidth,
            bounds.y + verticalTopTickOffset,
            bounds.width - verticalAxisWidth - horizontalRightTickOffset,
            bounds.height - horizontalAxisHeight - verticalTopTickOffset);
        backdropDisplayer.paintIn(this, g, innerGraphRect);
        valuesDisplayer.paintIn(this, g, innerGraphRect);
        verticalAxisDisplayer.paintIn(
            this,
            g,
            bounds.x,
            bounds.y + backdropTopOffset,
            verticalAxisWidth,
            bounds.height - horizontalAxisHeight + verticalBottomTickOffset - backdropTopOffset + 1);  // TBD: questionable + 1...
        horizontalAxisDisplayer.paintIn(
            this,
            g,
            innerGraphRect.x,
            innerGraphRect.y + innerGraphRect.height,
            innerGraphRect.width - backdropRightOffset,
            bounds.height - innerGraphRect.height - verticalTopTickOffset);
    }

    Rectangle getInnerGraphRect()
    {

        CpAbstractAxisDisplayer verticalAxisDisplayer = getVerticalAxisDisplayer();
        CpAbstractAxisDisplayer horizontalAxisDisplayer = getHorizontalAxisDisplayer();
        Rectangle bounds = getDisplayRect();

        int verticalTopTickOffset = verticalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int verticalBottomTickOffset = verticalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);
        int horizontalLeftTickOffset = horizontalAxisDisplayer.getLeftOrTopTickAlignmentOffset(this);
        int horizontalRightTickOffset = horizontalAxisDisplayer.getRightOrBottomTickAlignmentOffset(this);

        int verticalAxisWidth = Math.max(verticalAxisDisplayer.preferredSize(this).width, horizontalLeftTickOffset);
        int horizontalAxisHeight = Math.max(horizontalAxisDisplayer.preferredSize(this).height, verticalBottomTickOffset);

        return new Rectangle(
            bounds.x + verticalAxisWidth,
            bounds.y + verticalTopTickOffset,
            bounds.width - verticalAxisWidth - horizontalRightTickOffset,
            bounds.height - horizontalAxisHeight - verticalTopTickOffset);
    }

    Point seriesAndCategoryForPoint(Point mouseLocation)
    {
        Rectangle innerGraphRect = getInnerGraphRect();
        if (mouseLocation.x < innerGraphRect.x || mouseLocation.y > innerGraphRect.y + innerGraphRect.height) {
            return null;
        }
        return valuesDisplayer.seriesAndCategoryForPoint(mouseLocation, innerGraphRect);
    }


    public abstract CpAbstractAxisDisplayer getVerticalAxisDisplayer();
    public abstract CpAbstractAxisDisplayer getHorizontalAxisDisplayer();
}
