using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nft.framework.drawing;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using System.Threading;
using System.Drawing;
#region arias
using SysColor = System.Drawing.Color;
using XnaColor = Microsoft.Xna.Framework.Color;
using SysRect = System.Drawing.Rectangle;
using XnaRect = Microsoft.Xna.Framework.Rectangle;
using Point = System.Drawing.Point;
using DefaultEffect = StockEffects.DefaultEffect;
#endregion

namespace nft.xna
{
    public class TerrainTemplate : ITerrainPlateTemplate
    {
        protected VertexBuffer vertexBuffer;
        protected readonly Vector3 brightness;
        protected readonly int triangles;

        public TerrainTemplate(float brightness, int triangles_in_vertices) {
            this.brightness = new Vector3(brightness, brightness, brightness);
            this.triangles = triangles_in_vertices;
        }

        public XnaColor Brightness {
            get { return new XnaColor(brightness); }
        }

        public virtual void Draw(DefaultEffect effect, TerrainPlate plate) {
            GraphicsDevice gd = effect.GraphicsDevice;
            effect.DiffuseColor = brightness;
            gd.SetVertexBuffer(vertexBuffer);
            XnaFilter filter = (XnaFilter)plate.Effect;
            if (filter != null)
                filter.BeforeDraw(effect, plate);
            foreach (var pass in effect.CurrentTechnique.Passes) {
                pass.Apply();
                gd.DrawPrimitives(PrimitiveType.TriangleList, 0, triangles);
            }
            if (filter != null)
                filter.AfterDraw(effect, plate);
        }

        #region IDisposable 定型
        public virtual void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(Boolean disposing)
        {
            if (disposing && vertexBuffer!=null)
            {
                vertexBuffer.Dispose();
                vertexBuffer = null;
            }
        }

        public bool IsDisposed
        { get { return vertexBuffer == null; } }

        ~TerrainTemplate()
        {
            Dispose(false);
        }
        #endregion
    }

    public class TexturedTerrainTemplate : TerrainTemplate
    {
        protected XnaTexture texture;

        public TexturedTerrainTemplate(XnaTexture texture, Vector3[] vertices, Vector2[] texture_pos, float brightness)
            : base(brightness, vertices.Length/3) {
            this.texture = texture;
            this.texture.ImageSrc.AddRef();
            int n = vertices.Length;
            VertexPositionTexture[] v = new VertexPositionTexture[n];
            for (int i = 0; i < n; i++) {
                v[n] = new VertexPositionTexture(vertices[i], texture_pos[i]);
            }
            this.vertexBuffer = new VertexBuffer(XnaGraphicManager.GraphicsDevice, typeof(VertexPositionTexture), n, BufferUsage.None);
            this.vertexBuffer.SetData<VertexPositionTexture>(v);
        }

        public Texture2D Texture {
            get {
                return texture.ImageSrc.Texture2D;
            }
        }

        protected override void Dispose(bool disposing) {
            base.Dispose(disposing);
            if (disposing && texture != null) {
                texture.ImageSrc.ReleaseRef();
                texture = null;
            }
        }

        public override void Draw(DefaultEffect effect, TerrainPlate plate) {
            effect.Texture = this.Texture;
            //effect.SetTextureRegion(this.region);
            base.Draw(effect, plate);
            //((DefaultEffect)effect).SetTextureRegion(new Microsoft.Xna.Framework.Rectangle());
        }
    }

    public class PlainTerrainTemplate : TerrainTemplate
    {
        public PlainTerrainTemplate(XnaColor baseColor, Vector3[] vertices, float brightness)
            : base(brightness, vertices.Length / 3) {
            int n = vertices.Length;
            VertexPositionColor[] v = new VertexPositionColor[n];
            for (int i = 0; i < n; i++) {
                v[i] = new VertexPositionColor(vertices[i], baseColor);
            }
            this.vertexBuffer = new VertexBuffer(XnaGraphicManager.GraphicsDevice, typeof(VertexPositionColor), n, BufferUsage.None);
            this.vertexBuffer.SetData<VertexPositionColor>(v);
        }

        public override void Draw(DefaultEffect effect, TerrainPlate plate) {
            ((DefaultEffect)effect).VertexColorEnabled = true;
            base.Draw(effect, plate);
            ((DefaultEffect)effect).VertexColorEnabled = false;
        }
    }

    public class TerrainPlate : ITerrainPlate, IDrawable
    {
        protected UInt32 _id = 0;
        protected object _tag;
        protected XnaFilter filter;
        protected float _bias;
        private Vector3 pos;
        protected TerrainTemplate template;

        internal TerrainPlate() {
        }

        internal void Init(UInt32 id, TerrainTemplate templ){
            this._id = id;
            this.template = templ;
            this.filter = null;
            this._bias = 0f;
            this.pos = Vector3.Zero;
            this._tag = null;
        }

        public TerrainPlate(UInt32 id, TerrainTemplate templ)
        {
            this._id = id;
            this.template = templ;
        }

        public Vector3 Position3D
        {
            get { return pos; }
            set {
                if (pos.Equals(value)) return;
                pos = value;
            }
        }

        #region I3DObject implementation
        public UInt32 ID {
            get { return _id; }
            set { _id = value; }
        }

        public object Tag {
            get {
                return _tag;
            }
            set {
                _tag = value;
            }
        }
        public IEffectFilter Effect {
            get {
                return filter;
            }
            set {
                filter = (XnaFilter)value;
            }
        }
        public float DepthBias {
            get { return _bias; }
            set { _bias = value; }
        }
        #endregion

        public virtual void RenderSelf(XnaSurface surface, DefaultEffect effect) {
            // 平行移動を適用
            Matrix wback = effect.World;
            effect.World = Matrix.CreateTranslation(Position3D);
            effect.HitTestID = this.ID;
            if (_bias > 0f) {
                RasterizerState state = new RasterizerState();
                state.DepthBias = this._bias;
                surface.GraphicsDevice.RasterizerState = state;
            } else {
                surface.GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
            }
            template.Draw(effect, this);
        }

        static protected Vector3 ConvertIsometric(float l, float r, float h)
        {
            return new Vector3(r, h, l);
        }

        static protected XnaRect CalcClipRect(Vector2 offset, Vector3 size)
        {
            float width = size.X + size.Z;
            float height = size.Y + width / 2;
            XnaRect rct = new XnaRect((int)offset.X, (int)offset.Y, (int)width, (int)height);
            return rct;
        }

        #region ITerrainPlate implementation
        public ITerrainPlateTemplate Template{
            get { return template; }
        }

        public PointF3D Location
        {
            get
            {
                return XnaUtil.ToPointF(pos);
            }
            set
            {
                pos = XnaUtil.ToVector3(value);
            }
        }
        #endregion

        #region IDisposable 定型
        public virtual void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(Boolean disposing)
        {
            template = null;
            filter = null;
        }

        public bool IsDisposed
        { get { return template == null; } }

        ~TerrainPlate()
        {
            Dispose(false);
        }
        #endregion
    }

}
