using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.Serialization;
using nft.framework.drawing;

using Geocon = nft.core.geometry.GeometricConstants;
using nft.contributions.terrain;
using System.IO;
using System.Drawing.Drawing2D;
using nft.framework;
using System.Drawing.Imaging;

namespace nft.core.geometry {
    public class HeightCutSlopePolygon : ITerrainPolygon, ISerializable {
        static Dictionary<uint, HeightCutSlopePolygon> stock = new Dictionary<uint, HeightCutSlopePolygon>(256);
        
        static HeightCutSlopePolygon() {
            initPatterns(Geocon.TerrainStepHeightInUnit, Geocon.TerrainMaxHeightInUnit);
        }
        #region initialize ID table
        private static void initPatterns(int step, int max) {
            for (int p1 = step*2; p1 <= max; p1 += step) {
                for (int p2 = 0; p2 <= p1; p2 += step) {
                    int hm = p1 > p2 ? p1 : p2;
                    for (int h = step; h < hm; h += step) {
                        RegisterIDMap(0, p1, p2, h);
                        RegisterIDMap(p1, 0, p2, h);
                        RegisterIDMap(p1, p2, 0, h);
                        if (p1 == p2) continue;
                        RegisterIDMap(0, p2, p1, h);
                        RegisterIDMap(p2, 0, p1, h);
                        RegisterIDMap(p2, p1, 0, h);
                    }
                }
            }
        }

        private static void RegisterIDMap(int p0, int p1, int p2, int h) {
            ushort gid;
            ushort mid;
            gid = GroundPolygon.PrivateMakePolygonID(p0, p1, p2, -1);
            mid = HeightCutMask.FindBestMatchMask(ViewDirection.FRONT,p2-h, p0-h, p1-h);
            if(mid==HeightCutMask.ID_FULL_MASK||mid==HeightCutMask.ID_NULL_MASK) return;
            uint idx = (uint)(h << 16) + gid;
            if (stock.ContainsKey(idx)) {
                Debug.WriteLine("Duplicate id="+idx+" p0="+p0+" p1="+p1+" p2="+p2+" h="+h);
            } else {
                stock.Add(idx, new HeightCutSlopePolygon(idx, mid));
            }
        }
        #endregion

        public readonly uint id;
        protected readonly ushort maskid;

        protected HeightCutSlopePolygon(uint id, ushort maskid) {
            this.id = id;
            this.maskid = maskid;
        }

        #region ITerrainPolygon メンバ

        public ushort ID {
            get { throw new NotSupportedException("Use 'ID32' property, instead."); }
        }

        public uint ID32 {
            get { return id; }
        }

        public ushort GloundPolygonID {
            get { return (ushort)(id & 0xffff); }
        }

        public int HeightCutLevel { get { return (int)id >> 16; } }

        public ushort UpperMaskID { get { return maskid; } }
        public ushort LowerMaskID { get { return HeightCutMask.MakeInvertedMaskID(maskid); } }

        internal protected GroundPolygon BasePolygon {
            get { return GroundPolygon.GetPolygon((ushort)(id & 0xffff)); } 
        }

        /// <summary>
        /// Returns 2D bounds rectangle which contains all verticis points.
        /// </summary>
        public Rectangle GetBounds(Scaler sc) {
            return BasePolygon.GetBounds(sc);
        }

        /// <summary>
        /// Calc verticis according to the ViewFactor. A bit heavy process.
        /// </summary>
        /// <param name="vf"></param>
        /// <returns></returns>
        public Point[] GetVerticis(Scaler sc) {
            return BasePolygon.GetVerticis(sc);
        }
        #endregion

        /// <summary>
        /// Get total count of stock polygon patterns.
        /// </summary>
        public static int StockCount {
            get { return stock.Count; }
        }

        public static Dictionary<uint, HeightCutSlopePolygon>.KeyCollection GetAllID() {
            return stock.Keys;
        }

        /// <summary>
        /// Try to get the polygon associated with an ID.
        /// returns null if the ID is not registerd.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static HeightCutSlopePolygon TryGetPolygon(uint id) {
            HeightCutSlopePolygon v;
            if (stock.TryGetValue(id, out v)) {
                return v;
            } else {
                return null;
            }
        }

        /// <summary>
        /// get the polygon associated with an ID.
        /// throw exception if the ID is not registerd.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static HeightCutSlopePolygon GetPolygon(uint id) {
            return stock[id];
        }

        public static HeightCutSlopePolygon GetPolygon(ushort ground_id, int heightcut) {
            uint id = (uint)((heightcut << 16) + ground_id);
            return stock[id];
        }

        // serialize this object by reference
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
            info.SetType(typeof(ReferenceImpl));
            info.AddValue("id", ID32);
        }

        [Serializable]
        internal sealed class ReferenceImpl : IObjectReference {
            private uint id = 0xffff;
            public object GetRealObject(StreamingContext context) {
                return stock[id];
            }
        }


        public Point3D[] GetVerticis3D()
        {
            throw new NotImplementedException();
        }
    }
}
