#region Written by Tamas Budavari (budavari@jhu.edu)
/**
 * SphericalLib.Patch
 * 
 * SphericalLib is a .NET library to deal with geometry on the unit sphere.
 * It is intented to be used for complex survey footprint descriptions in
 * standalone applications as well as in web services and perhaps Yukon.
 * 
 * Written by Tamas Budavari (budavari@pha.jhu.edu)
 * See bottom of file for revision history
 * 
 * Current revision:
 *   ID:          $Id: Patch.cs,v 1.1.1.1 2006/11/14 02:56:39 gupchup Exp $
 *   Revision:    $Revision: 1.1.1.1 $
 *   Date:        $Date: 2006/11/14 02:56:39 $
 */
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
#endregion

namespace Spherical
{
    /// <summary>
    /// Patch class holds a list of arcs and the bounding circle as well as the area
    /// </summary>
    [CLSCompliant(true)]
    [Serializable]
    public class Patch
    {
        #region Private members

        private Arc[] arcList;
        private Halfspace? mec;
        private double? area;
        private double? length;
        private double tolerance;

        #endregion
        #region Public properties

        public double Tolerance
        {
            get { return tolerance; }
            set { tolerance = value; }
        }

        public ReadOnlyCollection<Arc> ArcList
        {
            get { return Array.AsReadOnly<Arc>(arcList); }
        }
        
        public Halfspace Mec
        {
            get
            {
                if (mec == null)
                {
                    // derive bounding circle
                    this.mec = Arc.MinimalEnclosingCircle(this.arcList);
                }
                return mec.Value;
            }
        }

 
        public double Area
        {
            get
            {
                if (area == null)
                {                    
                    // add up area and length
                    Patch.CalcAreaLength(this.arcList, this.Mec.Vector, this.tolerance,
                        out this.area, out this.length);
                }
                return area.Value;
            }
        }

        public double Length
        {
            get
            {
                if (length == null)
                {
                    // add up area and length
                    Patch.CalcAreaLength(this.arcList, this.Mec.Vector, this.tolerance,
                        out this.area, out this.length);
                }
                return length.Value;
            }
        }

        /// <summary>
        /// Area ratio of patch and minimal enclosing circle is the filling factor
        /// </summary>
        public double MecFillingFactor
        {
            get { return area.Value / (2 * Math.PI * (1 - Mec.Cos0) * Const.SquareRadian2SquareDegree); }
        }

        #endregion
        #region Constructors

        /// <summary>
        /// Create patch from a list of arcs, derive area and bounding circle.
        /// It checks if arcs are really connected if requested.
        /// </summary>
        /// <param name="list"></param>
        public Patch(Arc[] list)
        {
            if (list.Length < 1)
                throw new ArgumentException("Patch(list): empty arclist");
            this.arcList = list;
            this.tolerance = Const.Tolerance;
            this.area = null;
            this.length = null;
        }

        /// <summary>
        /// Create patch from a list of arcs, derive area and bounding circle.
        /// It checks if arcs are really connected if requested.
        /// </summary>
        /// <param name="list"></param>
        public Patch(Arc[] list, double tolerance)
        {
            if (list.Length < 1)
                throw new ArgumentException("Patch(list,tol): empty arclist");
            this.arcList = list;
            this.tolerance = tolerance;
            this.area = null;
            this.length = null;
        }

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="p"></param>
        public Patch(Patch p)
        {
            this.mec = p.mec;
            this.area = p.area;
            this.length = p.length;
            this.tolerance = p.tolerance; 
            this.arcList = new Arc[p.arcList.Length];
            for (int i = 0; i < this.arcList.Length; i++)
                arcList[i] = new Arc(p.arcList[i]);
        }

        #endregion
        #region Area and length

        /// <summary>
        /// Measure area of patch using the given pivot point
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public double CalcArea(Cartesian c, double tolerance)
        {
            return Patch.CalcArea(arcList, c, tolerance);
        }

        /// <summary>
        /// Measure area of patch using the given pivot point
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public double CalcArea(Cartesian c)
        {
            return Patch.CalcArea(arcList, c, this.tolerance);
        }

        /// <summary>
        /// Measure area of patch using the given pivot point
        /// </summary>
        /// 
        /// <param name="p"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        private static double CalcArea(Arc[] list, Cartesian c, double tolerance)
        {
            double area = 0;
            // if one arc -> whole cap area 
            if (list.Length == 1)
            {
                Arc a0 = list[0];
                area += a0.Area;
                double cos0 = a0.Circle.Cos0;
                area += 2 * Math.PI * (1 - Math.Abs(cos0))
                    * Const.SquareRadian2SquareDegree;
                if (cos0 < 0) area *= -1;
            }
            // otherwise add up triangles
            else
            {
                foreach (Arc e in list)
                {
                    area += e.CalcArea(c, tolerance);
                }
            }
            return area;
        }

        /// <summary>
        /// Measure area of patch using the given pivot point and add up length
        /// </summary>
        /// <param name="p"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        private static void CalcAreaLength(Arc[] list, Cartesian c, double tolerance,
            out double? area, out double? length)
        {
            area = 0;
            length = 0;

            // if one arc -> whole cap
            if (list.Length == 1)
            {
                Arc a0 = list[0];

                if (a0.Area > double.Epsilon || a0.Length > double.Epsilon)
                    throw new ApplicationException
                        ("Patch.CalcAreaLength(): one arc w/ area!=0 or length!=0");

                double cos0 = a0.Circle.Cos0;
                area = 2 * Math.PI * (1 - Math.Abs(cos0)) * Const.SquareRadian2SquareDegree;
                length = 360 * Math.Sqrt(1 - cos0 * cos0);
                if (cos0 < 0) area *= -1;
            }

            // otherwise add up triangles and lengths
            else
            {
                foreach (Arc e in list)
                {
                    area += e.CalcArea(c,tolerance);
                    length += e.Length;
                }
            }
        }

        #endregion
        #region Clone and verify

        /// <summary>
        /// Return a clone
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static Patch Clone(Patch p)
        {
            return new Patch(p);
        }

        /// <summary>
        /// Check if arcs are really connected
        /// </summary>
        /// <returns></returns>
        public bool VerifyOkay()
        {
            for (int k = 1; k < arcList.Length; k++)
                if (arcList[k - 1].Point2 != arcList[k].Point1)
                    return false;
            if (arcList[arcList.Length - 1].Point2 != arcList[0].Point1)
                return false;
            return true;
        }

        #endregion
        #region ToString and revision

        /// <summary>
        /// String representation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string r = String.Format("Patch:\n - Area is {0} square degrees", area);
            r += String.Format("\n - Bounding Circle: {0}", mec);
            r += String.Format("\n - Arcs:");
            foreach (Arc a in arcList)
                r += String.Format("\n {0}", a);
            return r;
        }

        /// <summary>
        /// Revision from CVS
        /// </summary>
        public static string Revision
        {
            get { return "$Revision: 1.1.1.1 $"; }
        }

        #endregion
    }
}

#region Revision History
/* Revision History

        $Log: Patch.cs,v $
        Revision 1.1.1.1  2006/11/14 02:56:39  gupchup
        no message

        Revision 1.6  2006/05/22 18:55:54  budavari
        BinaryFormatter now works with Region and co

        Revision 1.5  2006/04/19 16:51:01  budavari
        EVerything is now CLS compliant

        Revision 1.4  2006/03/09 20:30:07  budavari
        Equality checks for value types

        Revision 1.3  2006/02/28 16:26:59  budavari
        Major revision: public properties

        Revision 1.2  2006/02/10 21:28:56  budavari
        Patch now has length

        Revision 1.1  2006/01/24 15:56:05  budavari
        Re-structured directory layout to accommodate more projects

        Revision 1.19  2006/01/20 16:54:02  budavari
        Make SuperConvex from Patch (inc MEC & circles of arcs)

        Revision 1.18  2006/01/19 18:26:55  budavari
        Filling factor for MEC of Patch done

        Revision 1.17  2005/12/20 02:13:15  budavari
        Copy constructor and static Clone() added

        Revision 1.16  2005/12/04 16:50:55  budavari
        Small features and fixes. Also added Outline class - not function, yet

        Revision 1.15  2005/12/02 14:40:13  budavari
        Okay... struct Area is gone - not needed really (I think ;-)

        Revision 1.14  2005/12/01 22:34:57  budavari
        Area struct added and fully functional

        Revision 1.13  2005/12/01 19:02:20  budavari
        Middle of adding an Area class

        Revision 1.12  2005/11/30 20:50:28  budavari
        Cosmetics... #region specifications etc added

        Revision 1.11  2005/11/30 15:51:10  budavari
        Doc added

        Revision 1.10  2005/11/30 15:32:37  budavari
        Header changes

        Revision 1.9  2005/11/29 15:19:21  budavari
        All public fields in Patch for now

        Revision 1.8  2005/11/24 16:27:16  budavari
        Region areas compared to SQL code and releated fixes of degenerate arcs.
        Looking good except maybe for one patch (convex 0) of the 'footprint',
        which has 2 degenerate triangles and patch area changes when shifting
        pivot point.

        Revision 1.7  2005/11/22 14:46:00  budavari
        Area calculation fix

        Revision 1.6  2005/11/21 19:03:25  budavari
        Removed irrelevant if case - need a better fix for area

        Revision 1.5  2005/11/21 18:03:35  budavari
        Area debug is next...

        Revision 1.4  2005/11/18 05:58:09  budavari
        Working on boolean operations on regions...

        Revision 1.3  2005/11/17 02:00:52  budavari
        Ooops... forgot to add back the Patch class, which is now functional by the way.

*/
#endregion