/*
 * NPlot - A charting library for .NET
 * 
 * Marker.cs
 * Copyright (C) 2003-2006 Matt Howlett and others.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of NPlot nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System.Drawing;
using System.Drawing.Drawing2D;

namespace NPlot
{
    /// <summary>
    /// Encapsulates functionality relating to markers used by the PointPlot class.
    /// </summary>
    public class Marker
    {
        /// <summary>
        /// Enumeration of all different types of marker.
        /// </summary>
        public enum MarkerType
        {
            /// <summary>
            /// A simple cross marker (x).
            /// </summary>
            Cross1,

            /// <summary>
            /// Another simple cross marker (+).
            /// </summary>
            Cross2,

            /// <summary>
            /// A circle marker.
            /// </summary>
            Circle,

            /// <summary>
            /// A square marker.
            /// </summary>
            Square,

            /// <summary>
            /// A triangle marker (upwards).
            /// </summary>
            Triangle,

            /// <summary>
            /// A triangle marker (upwards).
            /// </summary>
            TriangleUp,

            /// <summary>
            /// A triangle marker (upwards).
            /// </summary>
            TriangleDown,

            /// <summary>
            /// A diamond,
            /// </summary>
            Diamond,

            /// <summary>
            /// A filled circle
            /// </summary>
            FilledCircle,

            /// <summary>
            /// A filled square
            /// </summary>
            FilledSquare,

            /// <summary>
            /// A filled triangle
            /// </summary>
            FilledTriangle,

            /// <summary>
            /// A small flag (up)
            /// </summary>
            Flag,

            /// <summary>
            /// A small flag (up)
            /// </summary>
            FlagUp,

            /// <summary>
            /// A small flag (down)
            /// </summary>
            FlagDown,

            /// <summary>
            /// No marker
            /// </summary>
            None
        }

        private Brush brush_ = new SolidBrush(Color.Black);
        private bool filled_;
        private int h_;
        private MarkerType markerType_;
        private Pen pen_ = new Pen(Color.Black);
        private int size_;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Marker()
        {
            markerType_ = MarkerType.Square;
            Size = 4;
            filled_ = false;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="markertype">The marker type.</param>
        public Marker(MarkerType markertype)
        {
            markerType_ = markertype;
            Size = 4;
            filled_ = false;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="markertype">The marker type.</param>
        /// <param name="size">The marker size.</param>
        public Marker(MarkerType markertype, int size)
        {
            markerType_ = markertype;
            Size = size;
            filled_ = false;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="markertype">The marker type.</param>
        /// <param name="size">The marker size.</param>
        /// <param name="color">The marker color.</param>
        public Marker(MarkerType markertype, int size, Color color)
        {
            markerType_ = markertype;
            Size = size;
            Color = color;
            filled_ = false;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="markertype">The marker type.</param>
        /// <param name="size">The marker size.</param>
        /// <param name="pen">The marker Pen.</param>
        public Marker(MarkerType markertype, int size, Pen pen)
        {
            markerType_ = markertype;
            Size = size;
            Pen = pen;
            filled_ = false;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="markertype">The marker type.</param>
        /// <param name="size">The marker size.</param>
        /// <param name="pen">The marker Pen.</param>
        /// <param name="fill">The fill flag.</param>
        public Marker(MarkerType markertype, int size, Pen pen, bool fill)
        {
            markerType_ = markertype;
            Size = size;
            Pen = pen;
            filled_ = fill;
        }

        /// <summary>
        /// The type of marker.
        /// </summary>
        public MarkerType Type
        {
            get { return markerType_; }
            set { markerType_ = value; }
        }

        /// <summary>
        /// Whether or not to draw a dropline.
        /// </summary>
        public bool DropLine { get; set; }

        /// <summary>
        /// The marker size.
        /// </summary>
        public int Size
        {
            get { return size_; }
            set
            {
                size_ = value;
                h_ = size_/2;
            }
        }

        /// <summary>
        /// The brush used to fill the marker.
        /// </summary>
        public Brush FillBrush
        {
            get { return brush_; }
            set { brush_ = value; }
        }

        /// <summary>
        /// Fill with color.
        /// </summary>
        public bool Filled
        {
            get { return filled_; }
            set { filled_ = value; }
        }

        /// <summary>
        /// Sets the pen color and fill brush to be solid with the specified color.
        /// </summary>
        public Color Color
        {
            set
            {
                pen_.Color = value;
                brush_ = new SolidBrush(value);
            }
            get { return pen_.Color; }
        }

        /// <summary>
        /// The Pen used to draw the marker.
        /// </summary>
        public Pen Pen
        {
            set { pen_ = value; }
            get { return pen_; }
        }

        /// <summary>
        /// Draws the marker at the given position
        /// </summary>
        /// <param name="g">The graphics surface on which to draw.</param>
        /// <param name="x">The [physical] x position to draw the marker.</param>
        /// <param name="y">The [physical] y position to draw the marker.</param>
        public void Draw(Graphics g, int x, int y)
        {
            switch (markerType_)
            {
                case MarkerType.Cross1:
                    g.DrawLine(pen_, x - h_, y + h_, x + h_, y - h_);
                    g.DrawLine(pen_, x + h_, y + h_, x - h_, y - h_);
                    break;

                case MarkerType.Cross2:
                    g.DrawLine(pen_, x, y - h_, x, y + h_);
                    g.DrawLine(pen_, x - h_, y, x + h_, y);
                    break;

                case MarkerType.Circle:
                    g.DrawEllipse(pen_, x - h_, y - h_, size_, size_);
                    if (filled_)
                    {
                        g.FillEllipse(brush_, x - h_, y - h_, size_, size_);
                    }
                    break;

                case MarkerType.Square:
                    g.DrawRectangle(pen_, x - h_, y - h_, size_, size_);
                    if (filled_)
                    {
                        g.FillRectangle(brush_, x - h_, y - h_, size_, size_);
                    }
                    break;

                case MarkerType.Triangle:
                case MarkerType.TriangleDown:
                    {
                        Point p1 = new Point(x - h_, y - h_);
                        Point p2 = new Point(x, y + h_);
                        Point p3 = new Point(x + h_, y - h_);
                        Point[] pts = new Point[3] {p1, p2, p3};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        if (filled_)
                        {
                            g.FillPath(brush_, gp);
                        }
                        break;
                    }
                case MarkerType.TriangleUp:
                    {
                        Point p1 = new Point(x - h_, y + h_);
                        Point p2 = new Point(x, y - h_);
                        Point p3 = new Point(x + h_, y + h_);
                        Point[] pts = new Point[3] {p1, p2, p3};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        if (filled_)
                        {
                            g.FillPath(brush_, gp);
                        }
                        break;
                    }
                case MarkerType.FilledCircle:
                    g.DrawEllipse(pen_, x - h_, y - h_, size_, size_);
                    g.FillEllipse(brush_, x - h_, y - h_, size_, size_);
                    break;

                case MarkerType.FilledSquare:
                    g.DrawRectangle(pen_, x - h_, y - h_, size_, size_);
                    g.FillRectangle(brush_, x - h_, y - h_, size_, size_);
                    break;

                case MarkerType.FilledTriangle:
                    {
                        Point p1 = new Point(x - h_, y - h_);
                        Point p2 = new Point(x, y + h_);
                        Point p3 = new Point(x + h_, y - h_);
                        Point[] pts = new Point[3] {p1, p2, p3};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        g.FillPath(brush_, gp);
                        break;
                    }
                case MarkerType.Diamond:
                    {
                        Point p1 = new Point(x - h_, y);
                        Point p2 = new Point(x, y - h_);
                        Point p3 = new Point(x + h_, y);
                        Point p4 = new Point(x, y + h_);
                        Point[] pts = new Point[4] {p1, p2, p3, p4};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        if (filled_)
                        {
                            g.FillPath(brush_, gp);
                        }
                        break;
                    }
                case MarkerType.Flag:
                case MarkerType.FlagUp:
                    {
                        Point p1 = new Point(x, y);
                        Point p2 = new Point(x, y - size_);
                        Point p3 = new Point(x + size_, y - size_ + size_/3);
                        Point p4 = new Point(x, y - size_ + 2*size_/3);
                        g.DrawLine(pen_, p1, p2);
                        Point[] pts = new Point[3] {p2, p3, p4};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        if (filled_)
                        {
                            g.FillPath(brush_, gp);
                        }
                        break;
                    }
                case MarkerType.FlagDown:
                    {
                        Point p1 = new Point(x, y);
                        Point p2 = new Point(x, y + size_);
                        Point p3 = new Point(x + size_, y + size_ - size_/3);
                        Point p4 = new Point(x, y + size_ - 2*size_/3);
                        g.DrawLine(pen_, p1, p2);
                        Point[] pts = new Point[3] {p2, p3, p4};
                        GraphicsPath gp = new GraphicsPath();
                        gp.AddPolygon(pts);
                        g.DrawPath(pen_, gp);
                        if (filled_)
                        {
                            g.FillPath(brush_, gp);
                        }
                        break;
                    }
                case MarkerType.None:
                    break;
            }
        }
    }
}