using System;
using System.Collections.Generic;
using System.Text;
using nft.framework.drawing;
using System.Collections;
using System.Drawing;
using System.IO;
using nft.core.geometry;
using nft.framework;
using System.Diagnostics;
using System.Windows.Forms;
using nft.framework.plugin;
using nft.core;

namespace nft.contributions.terrain {
    public interface IGroundPlateSet {
        ITerrainPlate Get(Location loc, GroundPolygon polygon, Point3DV hint);
        void Release(ITerrainPlate p);
    }

    public interface ICliffPlateSet
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="polygon"></param>
        /// <param name="extHeight">height to expand in unit</param>
        /// <param name="hint"></param>
        /// <returns></returns>
        ITerrainPlate Get(CliffPolygon polygon, int extHeight, Point3DV hint);
        void Release(ITerrainPlate p);
    }

    public class TemplateHolder
    {
        internal readonly ITerrainPlateTemplate Template;
        internal int refcount;
        public TemplateHolder(ITerrainPlateTemplate t) {
            Template = t;
            refcount = 0;
        }

        public ITerrainPlateTemplate AddRef(){
            refcount++;
            return Template;
        }

        public bool DeleteRef() {
            refcount--;
            if (refcount <= 0) {
                return true;
            } else {
                return false;
            }
        }
    }

    public abstract class TerrainPlateSet<T,U> where U:ITerrainPolygon //,IGroundPlateSet
    {
        protected IDictionary<T, TemplateHolder> templates = new Dictionary<T, TemplateHolder>();
        protected IDictionary<ITerrainPlateTemplate, T> reverseDir = new Dictionary<ITerrainPlateTemplate, T>();

        protected abstract T ToHashKey(U polygon, Point3DV hint);

        protected abstract ITerrainPlateTemplate CreateTemplate(U polygon, Point3DV hint);

        public virtual ITerrainPlate Get(Location loc, U polygon, Point3DV hint) {
            TemplateHolder holder;
            ITerrainPlateTemplate templ;
            T key = ToHashKey(polygon, hint);
            if (!templates.TryGetValue(key, out holder)) {
                templ = CreateTemplate(polygon, hint);
                holder = Add(key, templ);
            }
            templ = holder.AddRef();
            return GraphicManager.CreateTerrainPlate(loc, templ);
        }

        protected TemplateHolder Add(T key, ITerrainPlateTemplate templ) {
            reverseDir[templ] = key;
            TemplateHolder holder = new TemplateHolder(templ);
            templates.Add(key, holder);
            return holder;
        }

        public void Release(ITerrainPlate plate) {
            T key;
            if(reverseDir.TryGetValue(plate.Template, out key)){
                TemplateHolder holder = templates[key];
                if(holder.DeleteRef()){
                    holder.Template.Dispose();
                    reverseDir.Remove(holder.Template);
                    templates.Remove(key);
                }
            }
        }

        public void ReleaseUnused() {
            List<KeyValuePair<T, TemplateHolder>> work = new List<KeyValuePair<T, TemplateHolder>>();
            foreach (KeyValuePair<T, TemplateHolder> pair in templates) {
                TemplateHolder th = pair.Value;
                if (th.refcount == 0) {
                    work.Add(pair);
                }
            }
            foreach (KeyValuePair<T, TemplateHolder> pair in work) {
                templates.Remove(pair.Key);
            }
        }

        protected static GraphicManagerEx GraphicManager {
            get {
                return GraphicManagerEx.GraphicManager;
            }
        }
    }

    public abstract class CliffPlateSet<T> : ICliffPlateSet
    {
        protected IDictionary<T, TemplateHolder> templates = new Dictionary<T, TemplateHolder>();
        protected IDictionary<ITerrainPlateTemplate, T> reverseDir = new Dictionary<ITerrainPlateTemplate, T>();

        protected abstract T ToHashKey(CliffPolygon polygon, int height, Point3DV hint);

        protected abstract ITerrainPlateTemplate CreateTemplate(CliffPolygon polygon, int extHeight, Point3DV hint);

        public ITerrainPlate Get(CliffPolygon polygon, int extHeight, Point3DV hint) {
            TemplateHolder holder;
            ITerrainPlateTemplate templ;
            if (polygon == null) {
                Debug.WriteLine("null polygon at:"+hint);
                return null;
            }
            T key = ToHashKey(polygon, extHeight, hint);
            if (!templates.TryGetValue(key, out holder)) {
                templ = CreateTemplate(polygon, extHeight, hint);
                holder = Add(key, templ);
            }
            templ = holder.AddRef();
            return GraphicManager.CreateTerrainPlate(UInt32.MaxValue, templ);
        }

        protected TemplateHolder Add(T key, ITerrainPlateTemplate templ) {
            reverseDir[templ] = key;
            TemplateHolder holder = new TemplateHolder(templ);
            templates.Add(key, holder);
            return holder;
        }

        public void Release(ITerrainPlate plate) {
            T key;
            if (reverseDir.TryGetValue(plate.Template, out key)) {
                TemplateHolder holder = templates[key];
                if (holder.DeleteRef()) {
                    holder.Template.Dispose();
                    reverseDir.Remove(holder.Template);
                    templates.Remove(key);
                }
            }
        }

        protected abstract bool IsHeightOver(KeyValuePair<T, TemplateHolder> pair, int height);

        public virtual void ReleaseUnused(int over_height) {
            List<KeyValuePair<T, TemplateHolder>> work = new List<KeyValuePair<T, TemplateHolder>>();
            if (over_height > 0) {
                foreach (KeyValuePair<T, TemplateHolder> pair in templates) {
                    TemplateHolder th = pair.Value;
                    if (th.refcount == 0 && IsHeightOver(pair, over_height)) {
                        work.Add(pair);
                    }
                }
            } else {
                foreach (KeyValuePair<T, TemplateHolder> pair in templates) {
                    TemplateHolder th = pair.Value;
                    if (th.refcount == 0) {
                        work.Add(pair);
                    }
                }
            }
            foreach (KeyValuePair<T, TemplateHolder> pair in work) {
                templates.Remove(pair.Key);
            }
        }

        protected static GraphicManagerEx GraphicManager {
            get {
                return GraphicManagerEx.GraphicManager;
            }
        }
    }
}
