/**
 *
 * The MIT License
 *
 * Copyright (c) 2011 the original author or authors.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.googlecode.charts4j.parameters;

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.List;
import java.util.Locale;

import com.googlecode.charts4j.Color;
import com.googlecode.charts4j.Data;
import com.googlecode.charts4j.Marker;
import com.googlecode.charts4j.Priority;
import com.googlecode.charts4j.ShapeMarker;
import com.googlecode.charts4j.TextMarker;
import com.googlecode.charts4j.collect.Lists;

/**
 * Class for building chart markers parameter string for the Google Chart API.
 *
 * @author Julien Chastang (julien.c.chastang at gmail dot com)
 */
final class ChartMarkersParameter extends AbstractParameter {

    /** The Google Chart API chart markers parameter. */
    private static final String           URL_PARAMETER_KEY = "chm";

    /** The markers. */
    private final List<GoogleChartMarker> markers           = Lists.newLinkedList();

    /**
     * Adds the fill area marker.
     *
     * @param fillAreaType
     *            the fill area type
     * @param color
     *            the color
     * @param startLineIndex
     *            the start line index
     * @param endLineIndex
     *            the end line index
     */
    void addFillAreaMarker(final FillAreaType fillAreaType, final Color color, final int startLineIndex, final int endLineIndex) {
        markers.add(new FillAreaMarker(fillAreaType, color, startLineIndex, endLineIndex));
    }

    /**
     * Adds the line style marker.
     *
     * @param color
     *            the color
     * @param dataSetIndex
     *            the data set index
     * @param dataPoint
     *            the data point
     * @param size
     *            the size
     * @param priority
     *            the priority
     */
    void addLineStyleMarker(final Color color, final int dataSetIndex, final int dataPoint, final int size, final Priority priority) {
        markers.add(new LineStyleMarker(color, dataSetIndex, dataPoint, size, priority));
    }

    /**
     * Adds the marker.
     *
     * @param marker
     *            the marker
     * @param dataSetIndex
     *            the data set index
     * @param startIndex
     *            The start index for the marker range.
     * @param endIndex
     *            The end index for the marker range.
     * @param n
     *            Marker on every n-th data point.
     */
    void addMarker(final Marker marker, final int dataSetIndex, final int startIndex, final int endIndex, final int n) {
        if (marker instanceof ShapeMarker) {
            markers.add(new ShapeMarkerParam((ShapeMarker) marker, dataSetIndex, startIndex, endIndex, n));
        } else if (marker instanceof TextMarker) {
            markers.add(new TextMarkerParam((TextMarker) marker, dataSetIndex, startIndex, endIndex, n));
        }
    }

    /**
     * Add marker to each point on the plot.
     *
     * @param marker
     *            the marker
     * @param dataSetIndex
     *            the data set index
     */
    void addMarkers(final Marker marker, final int dataSetIndex) {
        //-1 adds the same shape marker to each point on the plot.
        addMarker(marker, dataSetIndex, -1, 0, 0);
    }


    /**
     * Adds the vertical range marker.
     *
     * @param color
     *            the color
     * @param startPoint
     *            the start point
     * @param endPoint
     *            the end point
     */
    void addVerticalRangeMarker(final Color color, final double startPoint, final double endPoint) {
        markers.add(new RangeMarker(RangeType.VERTICAL, color, startPoint, endPoint));
    }

    /**
     * Adds the horizontal range marker.
     *
     * @param color
     *            the color
     * @param startPoint
     *            the start point
     * @param endPoint
     *            the end point
     */
    void addHorizontalRangeMarker(final Color color, final double startPoint, final double endPoint) {
        markers.add(new RangeMarker(RangeType.HORIZONTAL, color, startPoint, endPoint));
    }

    /**
     * Adds the free marker.
     *
     * @param marker
     *            the marker
     * @param xPos
     *            the x pos
     * @param yPos
     *            the y pos
     */
    void addFreeMarker(final Marker marker, final double xPos, final double yPos) {
        markers.add(new FreeMarker(marker, xPos, yPos));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getKey() {
        return URL_PARAMETER_KEY;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getValue() {
        final StringBuilder sb = new StringBuilder();
        int cnt = 0;
        for (GoogleChartMarker m : markers) {
            sb.append(cnt++ > 0 ? "|" : "").append(m);
        }
        return !markers.isEmpty() ? sb.toString() : "";
    }

    /**
     * The GoogleChartMarker tag interface.
     */
    private interface GoogleChartMarker {
    }

    /**
     * The Class GMarker.
     */
    private static class GMarker implements GoogleChartMarker {

        /** The marker. */
        private final String   marker;

        /** The color. */
        private final Color    color;

        /** The data set index. */
        private final int      dataSetIndex;

        /** The start index. */
        private final int      startIndex;

        /** The end index. */
        private final int      endIndex;

        /** The periodicity. */
        private final int      n;

        /** The size. */
        private final int      size;

        /** The priority. */
        private final Priority priority;

        /**
         * Instantiates a new g marker.
         *
         * @param marker
         *            the marker
         * @param color
         *            the color
         * @param dataSetIndex
         *            the data set index
         * @param startIndex
         *            The start index for the marker range.
         * @param endIndex
         *            The end index for the marker range.
         * @param n
         *            Marker on every n-th data point.
         * @param size
         *            the size
         * @param priority
         *            the priority
         */
        private GMarker(final String marker, final Color color, final int dataSetIndex, final int startIndex, final int endIndex, final int n, final int size, final Priority priority) {
            this.marker = marker;
            this.color = color;
            this.dataSetIndex = dataSetIndex;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.n = n;
            this.size = size;
            this.priority = priority;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            final String returnString;
            if (startIndex >= 0 && endIndex == startIndex + 1) {
                returnString =  marker + "," + color + "," + dataSetIndex + "," + startIndex + "," + size + "," + priority;
            }
            else if (startIndex == -1) {
                returnString =  marker + "," + color + "," + dataSetIndex + "," + startIndex + "," + size + "," + priority;
            }
            else {//(endIndex - 1)  ---> The GCA is end point inclusive.
                returnString =  marker + "," + color + "," + dataSetIndex + "," + startIndex + ":" + (endIndex - 1) + ":" + n + "," + size + "," + priority;
            }
            return returnString;
        }
    }

    /**
     * The Class LineStyleMarker.
     */
    private static final class LineStyleMarker extends GMarker implements GoogleChartMarker {

        /**
         * Instantiates a new line style marker.
         *
         * @param color
         *            the color
         * @param dataSetIndex
         *            the data set index
         * @param dataPoint
         *            the data point
         * @param size
         *            the size
         * @param priority
         *            the priority
         */
        private LineStyleMarker(final Color color, final int dataSetIndex, final int dataPoint, final int size, final Priority priority) {
            super("D", color, dataSetIndex, dataPoint, dataPoint + 1, 1, size, priority);
        }
    }

    /**
     * The Class ShapeMarkerParam.
     */
    private static final class ShapeMarkerParam extends GMarker implements GoogleChartMarker {

        /**
         * Instantiates a new shape marker param.
         *
         * @param shapeMarker
         *            the shape marker
         * @param dataSetIndex
         *            the data set index
         * @param startIndex
         *            The start index for the marker range.
         * @param endIndex
         *            The end index for the marker range.
         * @param n
         *            Marker on every n-th data point.
         */
        private ShapeMarkerParam(final ShapeMarker shapeMarker, final int dataSetIndex, final int startIndex, final int endIndex, final int n) {
            super(shapeMarker.getShape().toString(), shapeMarker.getColor(), dataSetIndex, startIndex, endIndex, n, shapeMarker.getSize(), shapeMarker.getPriority());
        }
    }

    /**
     * The Class TextMarkerParam.
     */
    private static final class TextMarkerParam extends GMarker implements GoogleChartMarker {

        /**
         * Instantiates a new text marker param.
         *
         * @param textMarker
         *            the text marker
         * @param dataSetIndex
         *            the data set index
         * @param startIndex
         *            The start index for the marker range.
         * @param endIndex
         *            The end index for the marker range.
         * @param n
         *            Marker on every n-th data point.
         */
        private TextMarkerParam(final TextMarker textMarker, final int dataSetIndex, final int startIndex, final int endIndex, final int n) {
            super((textMarker.isFlagged() ? "f" : "t") + ParameterUtil.utf8Encode(textMarker.getText()), textMarker.getColor(), dataSetIndex, startIndex, endIndex, n, textMarker.getSize(), textMarker.getPriority());
        }
    }

    /**
     * The Class FillAreaMarker.
     */
    private static final class FillAreaMarker implements GoogleChartMarker {

        /** The fill area type. */
        private final FillAreaType fillAreaType;

        /** The color. */
        private final Color        color;

        /** The start line index. */
        private final int          startLineIndex;

        /** The end line index. */
        private final int          endLineIndex;

        /**
         * Instantiates a new fill area marker.
         *
         * @param fillAreaType
         *            the fill area type
         * @param color
         *            the color
         * @param startLineIndex
         *            the start line index
         * @param endLineIndex
         *            the end line index
         */
        private FillAreaMarker(final FillAreaType fillAreaType, final Color color, final int startLineIndex, final int endLineIndex) {
            this.fillAreaType = fillAreaType;
            this.color = color;
            this.startLineIndex = startLineIndex;
            this.endLineIndex = endLineIndex;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return fillAreaType + "," + color + "," + startLineIndex + "," + endLineIndex + ",0";
        }
    }

    /**
     * The Class RangeMarker.
     */
    private static final class RangeMarker implements GoogleChartMarker {

        /** The range type. */
        private final RangeType rangeType;

        /** The color. */
        private final Color     color;

        /** The start point. */
        private final double    startPoint;

        /** The end point. */
        private final double    endPoint;

        /** The decimal formatter. */
        private final DecimalFormat   decimalFormatter = new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.US));

        /**
         * Instantiates a new range marker.
         *
         * @param rangeType
         *            the range type
         * @param color
         *            the color
         * @param startPoint
         *            the start point
         * @param endPoint
         *            the end point
         */
        private RangeMarker(final RangeType rangeType, final Color color, final double startPoint, final double endPoint) {
            this.rangeType = rangeType;
            this.color = color;
            this.startPoint = startPoint;
            this.endPoint = endPoint;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return rangeType + "," + color + ",0," + decimalFormatter.format(startPoint) + "," + decimalFormatter.format(endPoint);
        }
    }

    /**
     * Private static inner class to encapsulate a FreeMarker.
     */
    private static class FreeMarker implements GoogleChartMarker {

        /** The marker. */
        private final Marker marker;

        /** The x pos. */
        private final double xPos;

        /** The y pos. */
        private final double yPos;

        /**
         * Instantiates a new free marker.
         *
         * @param marker
         *            the marker
         * @param xPos
         *            the x pos
         * @param yPos
         *            the y pos
         */
        private FreeMarker(final Marker marker, final double xPos, final double yPos) {
            this.marker = marker;
            this.xPos = xPos;
            this.yPos = yPos;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            final String s;
            if (marker instanceof TextMarker) {
                final TextMarker m = (TextMarker) marker;
                s = "@" + (m.isFlagged() ? "f" : "t") + m.getText() + "," + m.getColor() + ",0," + xPos / Data.MAX_VALUE + ":" + yPos / Data.MAX_VALUE  + "," + m.getSize()  + "," + m.getPriority();
            } else if (marker instanceof ShapeMarker) {
                final ShapeMarker m = (ShapeMarker) marker;
                s = "@" + m.getShape().toString() + "," + m.getColor() + ",0," + xPos / Data.MAX_VALUE + ":" + yPos / Data.MAX_VALUE  + "," + m.getSize() + "," + m.getPriority();
            } else {
                s = "";
            }
            return s;
        }
    }
}
