using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GhostArena4
{
    
    public class EntityManager //: DrawableGameComponent
    {
        ContentManager _content;

        Texture2D _ghostTexture;
        Texture2D _playerTexture;

        Color[][] _ghostData;
        Color[][] _playerData;

        List<Entity> _entities;

        SpriteBatch _sb;

        int _score;

        private float _ghostSpawnTimer;
        private Vector2[] _ghostSpawnPoints;
        private Direction[] _ghostSpawnDirections;
        private Random _rnd;

        private Vector2 _ghostOrigin;
        private Vector2 _playerOrigin;

        private Game _game;

        public int Score
        {
            get { return _score; }
        }

        public EntityManager(Game game)
            //: base(game)
        {
            _entities= new List<Entity>();
            _score = 0;
            _ghostSpawnTimer = 0.0f;
            _ghostSpawnPoints = new Vector2[10];
            _ghostSpawnDirections = new Direction[10];
            _rnd = new Random();

            _ghostOrigin = new Vector2(16, 16);
            _playerOrigin = new Vector2(32, 32);

            _playerData = new Color[4][];
            _ghostData = new Color[4][];

            _game = game;
        }

        //public override void Initialize()
        public void Initialize()
        {

            _content = new ContentManager(_game.Services);
            _content.RootDirectory = "Content";

            IGraphicsDeviceService graphicsservice = (IGraphicsDeviceService)_game.Services.GetService(typeof(IGraphicsDeviceService));

            _sb = new SpriteBatch(graphicsservice.GraphicsDevice);

            _ghostTexture = _content.Load<Texture2D>("ghost");
            _playerTexture = _content.Load<Texture2D>("player");

            for (int i = 0; i < 4; i++)
            {
                _playerData[i] = new Color[64 * 64];
                _playerTexture.GetData(0, new Rectangle(i, 0, 64, 64), _playerData[i], 0, 64 * 64);

                _ghostData[i] = new Color[32 * 32];
                _ghostTexture.GetData(0, new Rectangle(i, 0, 32, 32), _ghostData[i], 0, 32 * 32);
            }

            //player starts in the middle of the screen
            Entity entity = new Entity(EntityType.Player, new Vector2((_game.Window.ClientBounds.Width / 2) - ((_playerTexture.Width / 4) / 2), (_game.Window.ClientBounds.Height / 2 - _playerTexture.Height / 2) + 60), Direction.North, 4, _playerTexture.Width, _game);

            _entities.Add(entity);

            _ghostSpawnPoints[0] = new Vector2(240, Game1.LevelTop);
            _ghostSpawnPoints[1] = new Vector2(496, Game1.LevelTop);
            _ghostSpawnPoints[2] = new Vector2(752, Game1.LevelTop);
            _ghostSpawnPoints[3] = new Vector2(1023,284);
            _ghostSpawnPoints[4] = new Vector2(1023,516);
            _ghostSpawnPoints[5] = new Vector2(752,776);
            _ghostSpawnPoints[6] = new Vector2(496,776);
            _ghostSpawnPoints[7] = new Vector2(240,776);
            _ghostSpawnPoints[8] = new Vector2(0,516);
            _ghostSpawnPoints[9] = new Vector2(0,284);

            _ghostSpawnDirections[0]=Direction.South;
            _ghostSpawnDirections[1]=Direction.South;
            _ghostSpawnDirections[2]=Direction.South;
            _ghostSpawnDirections[3]=Direction.West;
            _ghostSpawnDirections[4]=Direction.West;
            _ghostSpawnDirections[5]=Direction.North;
            _ghostSpawnDirections[6]=Direction.North;
            _ghostSpawnDirections[7]=Direction.North;
            _ghostSpawnDirections[8]=Direction.East;
            _ghostSpawnDirections[9]=Direction.East;

            //start with 4 ghosts initially
            for (int i = 0; i < 4; i++)
                SpawnGhost();

        }

        //protected override void Dispose(bool disposing)
        protected void Dispose()
        {
            //base.Dispose(disposing);

            if (_content != null)
                _content.Unload();
            
            _entities.Clear();
        }

        //public override void Update(GameTime gameTime)
        public void Update(GameTime gameTime)
        {
            //base.Update(gameTime);

            //get the ScreenManager and check for paused state
            ScreenManager screenManager = (ScreenManager)_game.Components[0];

            if (screenManager.GetScreens()[0].IsActive)
            {
                //check for ghost spawn
                _ghostSpawnTimer += gameTime.ElapsedGameTime.Milliseconds;

                if (_ghostSpawnTimer >= 3000.0f)
                {
                    //get random location
                    SpawnGhost();
                    _ghostSpawnTimer -= 3000.0f;
                }

                foreach (Entity entity in _entities)
                    entity.Update(gameTime, _entities[0].Location);

                for (int i = _entities.Count - 1; i > 0; i--)
                {
                    if (CheckEntityCollision(_entities[i]))
                    {
                        //drain life
                        _entities[0].DrainLife();
                        _entities.Remove(_entities[i]);

                        if (_entities[0].Health == 0)
                            ((Game1)_game).soundbank.PlayCue("dying");

                        if (_entities.Count == 1)
                            break;
                    }
                }


                for (int i = _entities.Count - 1; i > 0; i--)
                {
                    if (_entities[0].CheckBulletCollision(_entities[i]))
                    {
                        _score++;
                        _entities.Remove(_entities[i]);

                        if (_entities.Count == 1)
                            break;
                    }
                }
           }
        }

        //public override void Draw(GameTime gameTime)
        public void Draw(GameTime gameTime)
        {
            //base.Draw(gameTime);

            _sb.Begin();

            Vector2 origin;
            float rot;

            foreach (Entity entity in _entities)
            {
                rot = (int)entity.ShootDirection * Game1.DEGREES_TO_RADIANS;

                switch (entity.Type)
                {
                    case EntityType.Ghost:
                    {
                        origin = new Vector2(_ghostTexture.Width / entity.NumFrames / 2, _ghostTexture.Height / 2);
                        _sb.Draw(_ghostTexture, new Rectangle((int)entity.Location.X, (int)entity.Location.Y, entity.FrameWidth, _ghostTexture.Height), new Rectangle(entity.CurFrameX, 0, entity.FrameWidth, _ghostTexture.Height), Color.White, rot, origin, SpriteEffects.None, 0.0f);
                        break;
                    }
                    case EntityType.Player:
                    {
                        origin = new Vector2(_playerTexture.Width / entity.NumFrames / 2, _playerTexture.Height / 2);
                        _sb.Draw(_playerTexture, new Rectangle((int)entity.Location.X, (int)entity.Location.Y, entity.FrameWidth, _playerTexture.Height), new Rectangle(entity.CurFrameX, 0, entity.FrameWidth, _playerTexture.Height), Color.White, rot, origin, SpriteEffects.None, 0.0f);

                        entity.DrawBullets(_sb);

                        break;
                    }
                }
            }
            
            _sb.End();

        }

        public void SetPlayerDirection(Direction dir)
        {
            if (_entities.Count > 0)
            {
                _entities[0].ShootDirection = dir;
            }
        }

        public void StopPlayer()
        {
            if (_entities.Count > 0)
            {
                _entities[0].Speed = 0.0f;
            }
        }

        public Direction GetPlayerDirection()
        {
            if (_entities.Count > 0)
            {
                return _entities[0].ShootDirection;
            }
            else
                return Direction.North;
        }

        public int GetPlayerHealth()
        {
            if (_entities.Count > 0)
            {
                return _entities[0].Health;
            }
            else
                return 0;
        }

        public bool IsPlayerDead()
        {
            if (_entities.Count > 0)
            {
                return _entities[0].State == EntityState.Dead;
            }
            else
                return true;

        }

        public void MovePlayer(Direction dir)
        {
            if (_entities.Count > 0)
            {
                _entities[0].MoveDirection = dir;
                _entities[0].Speed = 2.0f;
            }
        }

        public void PlayerFire()
        {
            if (_entities.Count > 0)
            {
                _entities[0].SpawnBullet();
            }
        }

        private void SpawnGhost()
        {
            int num = _rnd.Next(0, 10);
            _entities.Add(new Entity(EntityType.Ghost, _ghostSpawnPoints[num], _ghostSpawnDirections[num], 4, _ghostTexture.Width, _game));
            ((Game1)_game).soundbank.PlayCue("wail");
        }


        public bool CheckEntityCollision(Entity entity)
        {

            Rectangle playerRect;
            Rectangle ghostRect;

            Matrix matrix1, matrix2;

            matrix1 = Matrix.CreateTranslation(new Vector3(-_ghostOrigin, 0.0f)) *
                Matrix.CreateRotationZ(GetRotationFromDirection(entity.ShootDirection)) * 
                Matrix.CreateTranslation(new Vector3(entity.Location.X, entity.Location.Y, 0.0f));

            ghostRect = new Rectangle((int)entity.Location.X, (int)entity.Location.Y, 32, 32);

            matrix2 = Matrix.CreateTranslation(new Vector3(-_playerOrigin, 0.0f)) *
                Matrix.CreateRotationZ(GetRotationFromDirection(_entities[0].ShootDirection)) *
                Matrix.CreateTranslation(new Vector3(_entities[0].Location.X, _entities[0].Location.Y, 0.0f));

            playerRect = new Rectangle((int)_entities[0].Location.X, (int)_entities[0].Location.Y, 64, 64);

            //check for collision using rects first since per-pixel is costly
            if (ghostRect.Intersects(playerRect))
            {
                if (Intersect(matrix1, matrix2, 32, 64, 32, 64, _ghostData[entity.CurFrame], _playerData[_entities[0].CurFrame]))
                    return true;
            }

            return false;
        }

        private float GetRotationFromDirection(Direction dir)
        {
            float ret = 0.0f;

            switch (dir)
            {
                case Direction.North:

                    ret = -1.570796f;

                    break;

                case Direction.NorthEast:

                    ret = -0.785398f;
                    break;

                case Direction.East:

                    ret = 0.0f;
                    break;

                case Direction.SouthEast:

                    ret = 0.785398f;
                    break;

                case Direction.South:

                    ret = 1.570796f;
                    break;

                case Direction.SouthWest:

                    ret = 2.356194f;
                    break;

                case Direction.West:

                    ret = 3.141593f;
                    break;

                case Direction.NorthWest:

                    ret = -2.356194f;
                    break;
            }

            return ret;
        }

        private bool Intersect(Matrix matrix1, Matrix matrix2, int spriteWidth1, int spriteWidth2, int spriteHeight1, int spriteHeight2, Color[] data1, Color[] data2)
        {
            Matrix transform = matrix1 * Matrix.Invert(matrix2);

            Vector2 rowX = Vector2.TransformNormal(Vector2.UnitX, transform);
            Vector2 rowY = Vector2.TransformNormal(Vector2.UnitY, transform);

            Vector2 yPos = Vector2.Transform(Vector2.Zero, transform);

            for (int i = 0; i < spriteHeight1; i++)
            {
                Vector2 pos = yPos;

                for (int j = 0; j < spriteWidth1; j++)
                {
                    int i2 = (int)Math.Round(pos.X);
                    int j2 = (int)Math.Round(pos.Y);

                    if (0 <= i2 && i2 < spriteWidth2 && 0 <= j2 && j2 < spriteHeight2)
                    {
                        Color color1 = data1[i + j * spriteWidth1];
                        Color color2 = data2[i2 + j2 * spriteWidth2];

                        if (color1.A != 0 && color2.A != 0)
                            return true;
                    }
                    pos += rowX;
                }
                yPos += rowY;
            }

            return false;
        }
    }
}
