#region Using directives

using System;
using System.Collections.Generic;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Text;
using System.Windows.Forms;

#endregion

namespace PunkMan.Game
{
    /// <summary>
    /// The entity manager
    /// </summary>
    public class EntityManager
    {
        /// <summary>
        /// Game owner object
        /// </summary>
        private Game owner;

        /// <summary>
        /// Direct3D device
        /// </summary>
        private Device dev;
        /// <summary>
        /// Player object
        /// </summary>
        private Player player;
        /// <summary>
        /// Monster list
        /// </summary>
        private Monster[] monsters;
        /// <summary>
        /// Current map
        /// </summary>
        private Map map;

        /// <summary>
        /// Current map
        /// </summary>
        /// <value></value>
        public Map Map
        { get { return map; } }
        /// <summary>
        /// Current player
        /// </summary>
        /// <value></value>
        public Player Player
        { get { return player; } }
        /// <summary>
        /// Monster list
        /// </summary>
        /// <value></value>
        public Monster[] Monsters
        { get { return monsters; } }

        // monster model info
        /// <summary>
        /// Monster mesh (shared between all monsters)
        /// </summary>
        private Mesh monsterMesh;
        /// <summary>
        /// Monster material (shared between all monsters)
        /// </summary>
        private Material[] monsterMaterial;
        /// <summary>
        /// Monster texture (shared between all monsters)
        /// </summary>
        private Texture[] monsterTexture;
        /// <summary>
        /// Monster model path (shared between all monsters)
        /// </summary>
        private string monsterFileName;

        // bottle models info
        /// <summary>
        /// Rum bottle mesh
        /// </summary>
        private Mesh rumMesh;
        /// <summary>
        /// Rum bottle materials
        /// </summary>
        private Material[] rumMaterial;
        /// <summary>
        /// Rum bottle textures
        /// </summary>
        private Texture[] rumTexture;
        /// <summary>
        /// Rum bottle path
        /// </summary>
        private string rumFileName;
        /// <summary>
        /// Absinth bottle mesh
        /// </summary>
        private Mesh absinthMesh;
        /// <summary>
        /// Absinth bottle materials
        /// </summary>
        private Material[] absinthMaterial;
        /// <summary>
        /// Absinth bottle textures
        /// </summary>
        private Texture[] absinthTexture;
        /// <summary>
        /// Absinth bottle model path
        /// </summary>
        private string absinthFileName;

        // end mark model info
        /// <summary>
        /// Incomplete mark mesh
        /// </summary>
        private Mesh markMesh;
        /// <summary>
        /// Incomplete mark materials
        /// </summary>
        private Material[] markMaterial;
        /// <summary>
        /// Incomplete mark textures
        /// </summary>
        private Texture[] markTexture;
        /// <summary>
        /// Incomplete mark model path
        /// </summary>
        private string markFileName;
        /// <summary>
        /// Complete mark mesh
        /// </summary>
        private Mesh markMesh2;
        /// <summary>
        /// Complete mark materials
        /// </summary>
        private Material[] markMaterial2;
        /// <summary>
        /// Complete mark textures
        /// </summary>
        private Texture[] markTexture2;
        /// <summary>
        /// Complete mark model path
        /// </summary>
        private string markFileName2;

        /// <summary>
        /// Create monster model
        /// </summary>
        /// <param name="filename">model path</param>
        private void CreateMonster(string filename)
        {
            monsterFileName = filename;
            dev.DeviceReset += new EventHandler(this.OnResetDeviceMonster);
            OnResetDeviceMonster(dev, null);
        }

        /// <summary>
        /// Load model
        /// </summary>
        /// <param name="fileName">model path</param>
        /// <param name="mesh">model mesh</param>
        /// <param name="mat">model materials</param>
        /// <param name="tex">model textures</param>
        private void LoadModel(string fileName, out Mesh mesh, out Material[] mat, out Texture[] tex)
        {
            int fileNameLength = fileName.Substring(fileName.LastIndexOf('\\') + 1).Length;
            string path = fileName.Substring(0, fileName.Length - fileNameLength);

            ExtendedMaterial[] materials;

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

        /// <summary>
        /// Device reset resurection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnResetDeviceMonster(object sender, EventArgs args)
        {
            LoadModel(monsterFileName, out monsterMesh, out monsterMaterial, out monsterTexture);
        }

        /// <summary>
        /// Load bottles model
        /// </summary>
        /// <param name="rum">rum model path</param>
        /// <param name="absinth">absinth model path</param>
        private void CreateBottles(string rum, string absinth)
        {
            rumFileName = rum;
            absinthFileName = absinth;
            dev.DeviceReset += new EventHandler(this.OnResetDeviceBottles);
            OnResetDeviceBottles(dev, null);
        }

        /// <summary>
        /// Device reset resurection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnResetDeviceBottles(object sender, EventArgs args)
        {
            LoadModel(rumFileName, out rumMesh, out rumMaterial, out rumTexture);
            LoadModel(absinthFileName, out absinthMesh, out absinthMaterial, out absinthTexture);
        }

        /// <summary>
        /// End mark position
        /// </summary>
        private Vector3 endMarkPosition;
        /// <summary>
        /// Load end mark models
        /// </summary>
        /// <param name="fileName">incomplete mark model path</param>
        /// <param name="fileName2">complete mark model path</param>
        private void CreateEndMark(string fileName, string fileName2)
        {
            markFileName = fileName;
            markFileName2 = fileName2;
            dev.DeviceReset += new EventHandler(OnResetDeviceEndMark);
            OnResetDeviceEndMark(dev, null);
            //endMarkPosition = Map.Convert(map.FindEndPoint());
        }
        /// <summary>
        /// Device reset resurection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnResetDeviceEndMark(object sender, EventArgs args)
        {
            LoadModel(markFileName, out markMesh, out markMaterial, out markTexture);
            LoadModel(markFileName2, out markMesh2, out markMaterial2, out markTexture2);
        }

        /// <summary>
        /// Monster/Player Attack event handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="x">map position</param>
        /// <param name="y">map position</param>
        private void OnAttack(object sender, int x, int y)
        {
            Game.LogFile.WriteLine("Attack");
            if (player.Delirium)
            {
                // kill monster
                ((Monster)sender).Killed = true;
                Game.LogFile.WriteLine("Monster killed");
                owner.SFXPlay(SoundFX.SFX.Killing);
                player.PlayerKillMonster();
            }
            else
            {
                owner.SFXPlay(SoundFX.SFX.Killed);
                owner.PlayerDeath();
            }
        }

        /// <summary>
        /// Mighty constructor
        /// </summary>
        /// <param name="owner">game owner object</param>
        /// <param name="player">current player</param>
        /// <param name="map">current map</param>
        /// <param name="dev">Direct3D device</param>
        public EntityManager(Game owner, Player player, Map map, Device dev)
        {
            this.owner = owner;
            this.dev = dev;
            CreateMonster(Game.GetDataFilePath(@"models\benga.x"));
            CreateBottles(Game.GetDataFilePath(@"models\rum.x"),
                Game.GetDataFilePath(@"models\absinth.x"));
            CreateEndMark(Game.GetDataFilePath(@"models\incompleteMark.x"),
                Game.GetDataFilePath(@"models\completeMark.x"));
            Clear(player, map);
            
        }

        /// <summary>
        /// Clear entity manager state
        /// </summary>
        /// <param name="player">player to clear</param>
        /// <param name="map">map to clear</param>
        public void Clear(Player player, Map map)
        {
            Clear(player);
            Clear(map);
            Clear(map.MonsterCount());
        }

        /// <summary>
        /// Clear entity manager
        /// </summary>
        /// <param name="player"></param>
        public void Clear(Player player)
        {
            this.player = player;
        }

        /// <summary>
        /// Clear entity manager
        /// </summary>
        /// <param name="monsterCount"></param>
        public void Clear(uint monsterCount)
        {
            player.Position = Map.Convert(map.FindStartPoint());
            player.Moving = false;
            

            this.monsters = new Monster[monsterCount];
            for (uint i = 0; i < monsterCount; i++) 
                this.monsters[i] = null;
            int n = 0;
            for (int x = 0; x < map.DataWidth; x++)
                for (int y = 0; y < map.DataHeight; y++)
                {
                    if (map.Data[x, y].Entity == MapCell.EntityType.Monster)
                    {
                        monsters[n] = new Monster(dev, map, monsterMesh, monsterMaterial, 
                            monsterTexture, player);
                        monsters[n].MapPosition = new Point(x, y);
                        monsters[n].OnAttack += new Game.GameEventHandler(OnAttack);
                        n++;
                    }
                }


        }

        /// <summary>
        /// Clear entity manager
        /// </summary>
        /// <param name="map"></param>
        public void Clear(Map map)
        {
            this.map = map;
            endMarkPosition = Map.Convert(map.FindEndPoint());
        }

        /// <summary>
        /// can player move this direction
        /// </summary>
        /// <param name="x">direction</param>
        /// <param name="y">direction</param>
        /// <returns>can move?</returns>
        public bool CanPlayerMove(int x, int y)
        {
            if (map[new Point(x,y)] == null || !map.Data[x, y].Walkable) 
                return false;
            /*foreach (Entity e in monsters)
            {
                if (e.Position.X
            }*/
            return true;
        }

        /// <summary>
        /// Draw all monsters
        /// </summary>
        public void RenderMonsters()
        {
            foreach (Monster e in monsters) if (e != null) e.Render();
        }


        /// <summary>
        /// Draw all bottles
        /// </summary>
        public void RenderBottles()
        {
            Matrix oldWorld = dev.Transform.World;
            Matrix rot = Matrix.RotationY(Game.Time());
            for (int x = 0; x < map.DataWidth; x++)
                for (int y = 0; y < map.DataHeight; y++)
                {
                    Mesh mesh = null;
                    Material[] mat = null;
                    Texture[] tex = null;
                    bool draw = false;
                    if (map.Data[x, y].Entity == MapCell.EntityType.Absinth)
                    {
                        mesh = absinthMesh;
                        mat = absinthMaterial;
                        tex = absinthTexture;
                        draw = true;
                    }
                    else if (map.Data[x, y].Entity == MapCell.EntityType.Rum)
                    {
                        mesh = rumMesh;
                        mat = rumMaterial;
                        tex = rumTexture;
                        draw = true;
                    }
                    if (draw)
                    {
                        Vector3 pos = Map.Convert(new Point(x, y));
                        dev.Transform.World = Matrix.Multiply(rot, Matrix.Translation(pos));
                        for (int i = 0; i < mat.Length; i++)
                        {
                            dev.SetTexture(0, tex[i]);
                            //dev.Material = mat[i];
                            mesh.DrawSubset(i);
                        }
                    }
                }
            dev.Transform.World = oldWorld;
        }

        /// <summary>
        /// Draw end mark
        /// </summary>
        public void RenderEndMark()
        {
            Matrix oldWorld = dev.Transform.World;
            dev.Transform.World = Matrix.Translation(endMarkPosition);
            bool b = map.HasItem();

            Mesh mesh = (b) ? markMesh : markMesh2;
            Material[] mat = (b) ? markMaterial : markMaterial2;
            Texture[] tex = (b) ? markTexture : markTexture2;

            for (int i = 0; i < mat.Length; i++)
            {
                //dev.Material = markMaterial[i];
                if (tex[i] != null)
                    dev.SetTexture(0, tex[i]);
                mesh.DrawSubset(i);
            }
        }
    }
}
