#region Using directives

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using PunkMan.Shared;

#endregion

namespace PunkMan.Game
{
    /// <summary>
    /// Player and Monster base class
    /// </summary>
    public class Entity
    {
        /// <summary>
        /// Move direction type
        /// </summary>
        public enum MoveDirection
        {
            /// <summary>
            /// Up move
            /// </summary>
            Up = 0, 
            /// <summary>
            /// Up move
            /// </summary>
            Down = 2, 
            /// <summary>
            /// Left move
            /// </summary>
            Left = 3, 
            /// <summary>
            /// Right move
            /// </summary>
            Right = 1
        }

        /// <summary>
        /// file name of .x model
        /// </summary>
        private string fileName;
        /// <summary>
        /// Direct3D device
        /// </summary>
        protected Device dev;
        /// <summary>
        /// model mesh
        /// </summary>
        protected Mesh model;
        /// <summary>
        /// model mesh
        /// </summary>
        /// <value></value>
        public Mesh Model
        {
            get{ return model; }
            set { model = value; }
        }

        /// <summary>
        /// model materials
        /// </summary>
        protected Material[] materialList;
        /// <summary>
        /// model textures
        /// </summary>
        protected Texture[] textureList;
        
        /// <summary>
        /// position of entity
        /// </summary>
        protected Vector3 position;
        /// <summary>
        /// position of entity
        /// </summary>
        /// <value></value>
        public Vector3 Position
        {
            get { return position; }
            set { position = value; mapPosition = Map.Convert(value); }
        }

        /// <summary>
        /// is entity moving?
        /// </summary>
        protected bool moving = false;
        /// <summary>
        /// is entity moving?
        /// </summary>
        /// <value></value>
        public bool Moving
        {
            get { return moving; }
            set { moving = value; }
        }

        /// <summary>
        /// postion in map coordinate
        /// </summary>
        protected Point mapPosition;
        /// <summary>
        /// postion in map coordinate
        /// </summary>
        protected Point nextMapPosition;
        /// <summary>
        /// direction of movment
        /// </summary>
        protected Vector3 direction;
        /// <summary>
        /// direction of movment
        /// </summary>
        /// <value></value>
        public Vector3 Direction
        {
            get { return direction; }
        }
        /// <summary>
        /// direction of movement in map coordinate
        /// </summary>
        protected MoveDirection moveDirection;

        /// <summary>
        /// position of entitiy in map coordinate
        /// </summary>
        /// <value></value>
        public Point MapPosition
        {
            get { return mapPosition; }
            set { mapPosition = value; position = Map.Convert(value); }
        }

        /// <summary>
        /// speed of movement
        /// </summary>
        protected float speed = 6.0f;
        /// <summary>
        /// speed of movement
        /// </summary>
        /// <value></value>
        public float Speed
        {
            get { return speed; }
            set { speed = value; }
        }


        /// <summary>
        /// mighty constructor
        /// </summary>
        /// <param name="dev">Direct3D device</param>
        /// <param name="fileName">path to .x model</param>
        public Entity(Device dev, string fileName)
        {
            this.dev = dev;
            this.fileName = fileName;
            dev.DeviceReset += new EventHandler(this.OnResetDevice);
            OnResetDevice(dev, null);

        }

        /// <summary>
        /// Mighty constructor
        /// </summary>
        /// <param name="dev">Direct3D device</param>
        /// <param name="mesh">entity mesh</param>
        /// <param name="mats">entity materials</param>
        /// <param name="texs">entity textures</param>
        public Entity(Device dev, Mesh mesh, Material[] mats, Texture[] texs)
        {
            this.dev = dev;
            this.model = mesh;
            this.materialList = mats;
            this.textureList = texs;
        }

        /// <summary>
        /// Device reset resurection
        /// </summary>
        /// <param name="sender">D3D device</param>
        /// <param name="e"></param>
        public void OnResetDevice(object sender, EventArgs e)
        {
            int fileNameLength = fileName.Substring(fileName.LastIndexOf('\\')+1).Length;
            string path = fileName.Substring(0, fileName.Length - fileNameLength);

            Device dev = (Device)sender;
            ExtendedMaterial[] materials;
            model = Mesh.FromFile(fileName, MeshFlags.SystemMemory, dev, out materials);
            textureList = new Texture[materials.Length];
            materialList = new Material[materials.Length];
            for (int i = 0; i < textureList.Length; i++)
            {
                materialList[i] = materials[i].Material3D;
                if (materials[i].TextureFilename != null)
                    textureList[i] = TextureLoader.FromFile(dev, path + materials[i].TextureFilename);
                else textureList[i] = null;
            }

        }

        /// <summary>
        /// draw entity
        /// </summary>
        public virtual void Render()
        {
            Update();
            Matrix oldWorld = dev.Transform.World;
            float rot = 0;
            switch (moveDirection)
            {
                case MoveDirection.Down:
                    break;
                case MoveDirection.Up:
                    rot = (float)Math.PI;
                    break;
                case MoveDirection.Left:
                    rot = (float)Math.PI * 0.5f;
                    break;
                case MoveDirection.Right:
                    rot = (float)Math.PI * 1.5f;
                    break;
            }
            
            dev.Transform.World = Matrix.Multiply(Matrix.RotationY(rot),Matrix.Translation(position));
            //dev.Transform.World = Matrix.Translation(position);



            for (int i = 0; i < materialList.Length; i++)
            {
                dev.SetTexture(0, textureList[i]);
                model.DrawSubset(i);
            }



            //AllocateHierarchy allocH = All
            //AnimationRootFrame rootFrame = Mesh.LoadHierarchyFromFile(fileName, MeshFlags.SystemMemory, dev,
            //   AllocateHierarchy, null);

           dev.Transform.World = oldWorld;

       }

        /// <summary>
        /// time of last update
        /// </summary>
        protected float lastTime = -1;
        /// <summary>
        /// update entity state
        /// </summary>
        protected virtual void Update()
        {
            if (!moving) return;
            if (lastTime > 0)
            {
                //Game.LogFile.WriteLine("Position: " + position.ToString());
                float delta = Math.Abs(Game.Time() - lastTime);
                Vector3 v = direction;
                v.Multiply(delta * speed);
                position.Add(v);

                Vector3 np = Map.Convert(nextMapPosition);
                float dis = Math.Abs(np.X - position.X) + Math.Abs(np.Z - position.Z);

                if (dis < v.Length())
                {
                    mapPosition = nextMapPosition;
                    position = Map.Convert(mapPosition);
                    moving = false;
                    lastTime = 0;
                    return;
                }

            }
            lastTime = Game.Time();

        }

        /// <summary>
        /// start entity movement
        /// </summary>
        /// <param name="dir">direction of movement</param>
        /// <returns>was this movement started</returns>
        public bool MoveTo(MoveDirection dir)
        {
            if (moving) return false;

            switch (dir)
            {
                case MoveDirection.Up:
                    direction = new Vector3(0, 0, 1);
                    nextMapPosition.X = mapPosition.X;
                    nextMapPosition.Y = mapPosition.Y + 1;
                    break;
                case MoveDirection.Down:
                    direction = new Vector3(0, 0, -1);
                    nextMapPosition.X = mapPosition.X;
                    nextMapPosition.Y = mapPosition.Y - 1;
                    break;
                case MoveDirection.Left:
                    direction = new Vector3(-1, 0, 0);
                    nextMapPosition.X = mapPosition.X - 1;
                    nextMapPosition.Y = mapPosition.Y;
                    break;
                case MoveDirection.Right:
                    direction = new Vector3(1, 0, 0);
                    nextMapPosition.X = mapPosition.X + 1;
                    nextMapPosition.Y = mapPosition.Y;
                    break;
            }

            moveDirection = dir;
            moving = true;
            return true;
        }
    }
}
