/*
 * NPlot - A charting library for .NET
 * 
 * Grid.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.Collections;
using System.Drawing;

namespace NPlot
{
    /// <summary>
    /// Encapsulates a Grid IDrawable object. Instances of this  to a PlotSurface2D
    /// instance to produce a grid.
    /// </summary>
    public class Grid : IDrawable
    {
        /// <summary>
        /// </summary>
        public enum GridType
        {
            /// <summary>
            /// No grid.
            /// </summary>
            None = 0,

            /// <summary>
            /// Coarse grid. Lines at large tick positions only.
            /// </summary>
            Coarse = 1,

            /// <summary>
            /// Fine grid. Lines at both large and small tick positions.
            /// </summary>
            Fine = 2
        }

        private GridType horizontalGridType_;
        private Pen minorGridPen_;
        private GridType verticalGridType_;

        /// <summary>
        /// Default constructor
        /// </summary>
        public Grid()
        {
            minorGridPen_ = new Pen(Color.LightGray);
            float[] pattern = {1.0f, 2.0f};
            minorGridPen_.DashPattern = pattern;

            MajorGridPen = new Pen(Color.LightGray);

            horizontalGridType_ = GridType.Coarse;

            verticalGridType_ = GridType.Coarse;
        }

        /// <summary>
        /// Specifies the horizontal grid type (none, coarse or fine).
        /// </summary>
        public GridType HorizontalGridType
        {
            get { return horizontalGridType_; }
            set { horizontalGridType_ = value; }
        }

        /// <summary>
        /// Specifies the vertical grid type (none, coarse, or fine).
        /// </summary>
        public GridType VerticalGridType
        {
            get { return verticalGridType_; }
            set { verticalGridType_ = value; }
        }

        /// <summary>
        /// The pen used to draw major (coarse) grid lines.
        /// </summary>
        public Pen MajorGridPen { get; set; }

        /// <summary>
        /// The pen used to draw minor (fine) grid lines.
        /// </summary>
        public Pen MinorGridPen
        {
            get { return minorGridPen_; }
            set { minorGridPen_ = value; }
        }

        /// <summary>
        /// Draws the grid
        /// </summary>
        /// <param name="g">The graphics surface on which to draw</param>
        /// <param name="xAxis">The physical x axis to draw horizontal lines parallel to.</param>
        /// <param name="yAxis">The physical y axis to draw vertical lines parallel to.</param>
        public void Draw(Graphics g, PhysicalAxis xAxis, PhysicalAxis yAxis)
        {
            ArrayList xLargePositions = null;
            ArrayList yLargePositions = null;
            ArrayList xSmallPositions = null;
            ArrayList ySmallPositions = null;

            if (horizontalGridType_ != GridType.None)
            {
                xAxis.Axis.WorldTickPositions_FirstPass(xAxis.PhysicalMin, xAxis.PhysicalMax, out xLargePositions, out xSmallPositions);
                DrawGridLines(g, xAxis, yAxis, xLargePositions, true, MajorGridPen);
            }

            if (verticalGridType_ != GridType.None)
            {
                yAxis.Axis.WorldTickPositions_FirstPass(yAxis.PhysicalMin, yAxis.PhysicalMax, out yLargePositions, out ySmallPositions);
                DrawGridLines(g, yAxis, xAxis, yLargePositions, false, MajorGridPen);
            }

            if (horizontalGridType_ == GridType.Fine)
            {
                xAxis.Axis.WorldTickPositions_SecondPass(xAxis.PhysicalMin, xAxis.PhysicalMax, xLargePositions, ref xSmallPositions);
                DrawGridLines(g, xAxis, yAxis, xSmallPositions, true, MinorGridPen);
            }

            if (verticalGridType_ == GridType.Fine)
            {
                yAxis.Axis.WorldTickPositions_SecondPass(yAxis.PhysicalMin, yAxis.PhysicalMax, yLargePositions, ref ySmallPositions);
                DrawGridLines(g, yAxis, xAxis, ySmallPositions, false, MinorGridPen);
            }
        }

        /// <summary>
        /// Does all the work in drawing grid lines.
        /// </summary>
        /// <param name="g">The graphics surface on which to render.</param>
        /// <param name="axis">TODO</param>
        /// <param name="orthogonalAxis">TODO</param>
        /// <param name="a">the list of world values to draw grid lines at.</param>
        /// <param name="horizontal">true if want horizontal lines, false otherwise.</param>
        /// <param name="p">the pen to use to draw the grid lines.</param>
        private void DrawGridLines(
            Graphics g, PhysicalAxis axis, PhysicalAxis orthogonalAxis,
            ArrayList a, bool horizontal, Pen p)
        {
            for (int i = 0; i < a.Count; ++i)
            {
                PointF p1 = axis.WorldToPhysical((double) a[i], true);
                PointF p2 = p1;
                PointF p3 = orthogonalAxis.PhysicalMax;
                PointF p4 = orthogonalAxis.PhysicalMin;
                if (horizontal)
                {
                    p1.Y = p4.Y;
                    p2.Y = p3.Y;
                }
                else
                {
                    p1.X = p4.X;
                    p2.X = p3.X;
                }
                // note: casting all drawing was necessary for sane display. why?
                g.DrawLine(p, (int) p1.X, (int) p1.Y, (int) p2.X, (int) p2.Y);
            }
        }
    }
}