/*******************************************************************************
 * Copyright (c) 2008-2016 SWTChart project. All rights reserved. 
 * 
 * This code is distributed under the terms of the Eclipse Public License v1.0
 * which is available at http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.swtchart.internal.axis;

import java.util.ArrayList;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.swtchart.Chart;
import org.swtchart.IAxis.Position;
import org.swtchart.internal.ChartLayoutData;

/**
 * Axis tick marks.
 */
public class AxisTickMarks implements PaintListener {

    /** the chart */
    private Chart chart;

    /** the axis */
    private Axis axis;

    /** the foreground color */
    private Color foreground;

    /** the width hint of tick marks area */
    private int widthHint;

    /** the height hint of tick marks area */
    private int heightHint;

    /** the bounds of tick marks area */
    private Rectangle bounds;

    /** the line width */
    protected static final int LINE_WIDTH = 1;

    /** the tick length */
    public static final int TICK_LENGTH = 5;

    /** the default foreground */
    private static final int DEFAULT_FOREGROUND = SWT.COLOR_BLUE;

    /**
     * Constructor.
     * 
     * @param chart
     *            the chart
     * @param axis
     *            the axis
     */
    public AxisTickMarks(Chart chart, Axis axis) {
        this.chart = chart;
        this.axis = axis;

        foreground = Display.getDefault().getSystemColor(DEFAULT_FOREGROUND);
        chart.addPaintListener(this);
    }

    /**
     * Sets the foreground color.
     * 
     * @param color
     *            the foreground color
     */
    public void setForeground(Color color) {
        if (color == null) {
            foreground = Display.getDefault()
                    .getSystemColor(DEFAULT_FOREGROUND);
        } else {
            foreground = color;
        }
    }

    /**
     * Gets the foreground color.
     * 
     * @return the foreground color
     */
    protected Color getForeground() {
        if (foreground.isDisposed()) {
            foreground = Display.getDefault()
                    .getSystemColor(DEFAULT_FOREGROUND);
        }
        return foreground;
    }

    /**
     * Gets the associated axis.
     * 
     * @return the axis
     */
    public Axis getAxis() {
        return axis;
    }

    /**
     * Updates tick marks layout.
     */
    protected void updateLayoutData() {
        widthHint = SWT.DEFAULT;
        heightHint = SWT.DEFAULT;
        if (!axis.getTick().isVisible()) {
            widthHint = 0;
            heightHint = 0;
        } else {
            if (axis.isHorizontalAxis()) {
                heightHint = Axis.MARGIN + TICK_LENGTH;
            } else {
                widthHint = TICK_LENGTH + Axis.MARGIN;
            }
        }
    }

    /**
     * Gets the layout data.
     * 
     * @return the layout data
     */
    public ChartLayoutData getLayoutData() {
        return new ChartLayoutData(widthHint, heightHint);
    }

    /**
     * Sets the bounds on chart panel.
     * 
     * @param x
     *            the x coordinate
     * @param y
     *            the y coordinate
     * @param width
     *            the width
     * @param height
     *            the height
     */
    public void setBounds(int x, int y, int width, int height) {
        bounds = new Rectangle(x, y, width, height);
    }

    /**
     * Gets the bounds on chart panel.
     * 
     * @return the bounds on chart panel
     */
    protected Rectangle getBounds() {
        return bounds;
    }

    /**
     * Disposes the resources.
     */
    protected void dispose() {
        if (!chart.isDisposed()) {
            chart.removePaintListener(this);
        }
    }

    /*
     * @see PaintListener#paintControl(PaintEvent)
     */
    public void paintControl(PaintEvent e) {
        ArrayList<Integer> tickLabelPositions = axis.getTick()
                .getAxisTickLabels().getTickLabelPositions();
        Color oldBackground = e.gc.getBackground();
        e.gc.setBackground(chart.getBackground());
        Color oldForeground = e.gc.getForeground();
        e.gc.setForeground(getForeground());
        Rectangle oldClipping = e.gc.getClipping();
        e.gc.setClipping(bounds);

        if (axis.isHorizontalAxis()) {
            drawXTickMarks(e.gc, tickLabelPositions, axis.getPosition());
        } else {
            drawYTickMarks(e.gc, tickLabelPositions, axis.getPosition());
        }

        e.gc.setClipping(oldClipping);
        e.gc.setBackground(oldBackground);
        e.gc.setForeground(oldForeground);
    }

    /**
     * Draw the X tick marks.
     * 
     * @param tickLabelPositions
     *            the tick label positions
     * @param position
     *            the axis position
     * @param gc
     *            the graphics context
     */
    private void drawXTickMarks(GC gc, ArrayList<Integer> tickLabelPositions,
            Position position) {

        // draw tick marks
        gc.setLineStyle(SWT.LINE_SOLID);
                if (axis.isValidCategoryAxis()) {
                        if (tickLabelPositions.size() > 1) {
                                int step = tickLabelPositions.get(1).intValue()
                                                - tickLabelPositions.get(0).intValue();
                                for (int i = 0; i < tickLabelPositions.size() + 1; i++) {
                                        int x;
                                        if (i < tickLabelPositions.size()) {
                                                x = (int) (tickLabelPositions.get(i).intValue() - step / 2d);
                                        } else {
                                                x = (int) (tickLabelPositions.get(i - 1).intValue() + step / 2d);
                                        }
                                        int y = 0;
                                        if (position == Position.Secondary) {
                                                y = bounds.height - 1 - LINE_WIDTH - TICK_LENGTH;
                                        }
                                        gc.drawLine(bounds.x + x, bounds.y + y, bounds.x + x,
                                                        bounds.y + y + TICK_LENGTH);
                                }
            }
        } else {
            for (int i = 0; i < tickLabelPositions.size(); i++) {
                int x = tickLabelPositions.get(i);
                int y = 0;
                if (position == Position.Secondary) {
                    y = bounds.height - 1 - LINE_WIDTH - TICK_LENGTH;
                }
                gc.drawLine(bounds.x + x, bounds.y + y, bounds.x + x, bounds.y
                        + y + TICK_LENGTH);
            }
        }

        // draw axis line
        if (position == Position.Primary) {
            gc.drawLine(bounds.x, bounds.y, bounds.x + bounds.width - 1,
                    bounds.y);
        } else {
            gc.drawLine(bounds.x, bounds.y + bounds.height - 1, bounds.x
                    + bounds.width - 1, bounds.y + bounds.height - 1);
        }
    }

    /**
     * Draw the Y tick marks.
     * 
     * @param tickLabelPositions
     *            the tick label positions
     * @param position
     *            the axis position
     * @param gc
     *            the graphics context
     */
    private void drawYTickMarks(GC gc, ArrayList<Integer> tickLabelPositions,
            Position position) {

        // draw tick marks
        gc.setLineStyle(SWT.LINE_SOLID);
        if (axis.isValidCategoryAxis()) {
            if (tickLabelPositions.size() > 1) {
                int step = tickLabelPositions.get(1).intValue()
                        - tickLabelPositions.get(0).intValue();
                                for (int i = 0; i < tickLabelPositions.size() + 1; i++) {
                                        int x = 0;
                                        int y;
                                        if (i < tickLabelPositions.size()) {
                                                y = (int) (tickLabelPositions.get(i).intValue() - step / 2d);
                                        } else {
                                                y = (int) (tickLabelPositions.get(i - 1).intValue() + step / 2d);
                                        }

                    if (position == Position.Primary) {
                        x = bounds.width - 1 - LINE_WIDTH - TICK_LENGTH;
                    } else {
                        x = LINE_WIDTH;
                    }
                    gc.drawLine(bounds.x + x, bounds.y + y, bounds.x + x
                            + TICK_LENGTH, bounds.y + y);
                }
            }
        } else {
            int y = 0;
            for (int i = 0; i < tickLabelPositions.size(); i++) {
                int x = 0;
                if (position == Position.Primary) {
                    x = bounds.width - 1 - LINE_WIDTH - TICK_LENGTH;
                } else {
                    x = LINE_WIDTH;
                }
                y = bounds.height - 1 - tickLabelPositions.get(i);
                gc.drawLine(bounds.x + x, bounds.y + y, bounds.x + x
                        + TICK_LENGTH, bounds.y + y);
            }
        }

        // draw axis line
        if (position == Position.Primary) {
            gc.drawLine(bounds.x + bounds.width - 1, bounds.y, bounds.x
                    + bounds.width - 1, bounds.y + bounds.height - 1);
        } else {
            gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height
                    - 1);
        }
    }
}
