using System;
using System.Collections.Generic;
using System.Text;
using Vortex;
using Vortex.Drawing;
using Vortex.Demo.Pacman.Units;
using System.Drawing;

namespace Vortex.Demo.Pacman {

    ///<summary>Pacman world states</summary>
    enum WorldState {
        LevelNumber,
        LevelComplete,
        Normal,
        GhostEated,
        PacmanDied,
        GameOver
    }
    
    /*
     *   NOTE: Pacman Game Will Be Refactored to use Scene Graph Next Release 
     */

    ///<summary>Pacman world class</summary>
    class World : IDisposable {

        WorldView m_View;
        WorldMap m_Map;
        LevelInfo m_LevelInfo;
        PlayerUnit m_Player;
        List<GhostUnit> m_Ghosts;
        ExplicitScore m_ExplicitScore;

        int m_LifeCount;
        int m_Scores;
        int m_LevelNumber;

        int m_EatHitCount;

        State<WorldState> m_State;

        public World() {
            m_Map = new WorldMap();
            m_Player = new PlayerUnit(Map);
            m_Player.OnGatherPoint = new GatherPointHandler(this.PacmanGathersPoint);

            m_View = new WorldView(this);
            m_LifeCount = 3;
        }

        public WorldMap Map {
            get { return m_Map; }
        }

        public PlayerUnit Player {
            get { return m_Player; }
        }

        public int Scores {
            get { return m_Scores; }
        }

        public int Level {
            get { return m_LevelNumber; }
        }

        public int LifeCount {
            get { return m_LifeCount; }
        }

        public State<WorldState> State {
            get { return m_State; }
        }

        public ExplicitScore ExplicitScore {
            get { return m_ExplicitScore; }
        }

        public IEnumerable<GhostUnit> Ghosts {
            get { return m_Ghosts; }
        }

        public void StartLevel(int levelNumber) {
            m_LevelInfo = new LevelInfo(1);
            m_Map.Fill(m_LevelInfo.Map);
            m_LevelNumber = levelNumber;
            //update speed with each new level
            Constants.DIFFICULTY_SPEED_MULTIPLICATOR = 1.0f + 0.1f * levelNumber;

            ResetLevel();
        }

        void ResetLevel() {
            //init player
            Player.SetLocation(new Vector2(m_LevelInfo.PlayerStartPosition));
            Player.SetDirection(UnitDirection.Left);
            Player.Run();

            //init ghosts
            int ghostCounter = 0;
            m_Ghosts = new List<GhostUnit>();
            foreach (Point ghostPosition in m_LevelInfo.GhostStartPositions) {
                GhostUnit unit = new GhostUnit(Map, Player, ghostCounter);
                unit.SetInitLocation(new Vector2(ghostPosition));
                unit.SetLocation(new Vector2(ghostPosition));
                unit.SetDirection(unit.GetDefaultDirection());
                m_Ghosts.Add(unit);
                ghostCounter++;
            }

            //set state
            m_State = new State<WorldState>(WorldState.LevelNumber, 2, WorldState.Normal);
        }

        private void PacmanGathersPoint(DotType pointType, Vector2 atPoint) {
            if (pointType == DotType.Small) {
                m_Scores += Constants.SCORE_PER_POINT;
                m_View.ExplodeWithSmallPoint();
            } if (pointType == DotType.Big) {
                m_Scores += Constants.SCORE_PER_BIG_POINT;
                m_EatHitCount = 0;
                ConfuseAllGhosts();
                m_View.ExplodeWithBigPoint();
            }
        }

        ///<summary>Confuses all ghosts for a specified period</summary>
        private void ConfuseAllGhosts() {
            foreach (GhostUnit unit in m_Ghosts) {
                if (!unit.Escaping) {
                    unit.Confuse(Constants.GHOST_CONFUSE_TIME);
                }
            }
        }


        public void Update(GameTime time) {
            m_State.Update(time.FrameTime);
            m_View.Update(time.FrameTime);

            if (m_State.Value == WorldState.Normal || m_State.Value == WorldState.GameOver) {

                if (m_State.Value != WorldState.GameOver) {
                    //if all dots are gathered
                    if (Map.DotCount < 1) {
                        m_State.Set(WorldState.LevelComplete, 2f, delegate(State<WorldState> state) {
                            StartLevel(Level + 1);
                        });
                    }

                    m_Player.Update(time.FrameTime);
                }
                
                foreach (GhostUnit unit in m_Ghosts) {
                    unit.Update(time.FrameTime);
                    //check for collision
                    if (!m_Player.Dead && unit.Location.Distance(m_Player.Location) < Constants.UNIT_COLLISION_DISTANCE) {
                        if (!unit.Escaping) {
                            if (unit.Confused) {
                                KillGhost(unit);
                                m_View.ExplodeWithBigPoint();
                            }
                            else {
                                Die();
                            }
                        }
                    }
                }
            }
        }

        private void KillGhost(GhostUnit unit) {
            m_State.Set(WorldState.GhostEated, 0.3f, WorldState.Normal, delegate(State<WorldState> state) {
                m_ExplicitScore = null;
            });

            int score = Constants.SCORES_PER_GHOST[m_EatHitCount++];
            m_Scores += score;
            m_ExplicitScore = new ExplicitScore(score, unit.Location);
            unit.BackToHome();
        }

        private void Die() {
            m_LifeCount--;
            Player.Kill();

            m_State.Set(WorldState.PacmanDied, 2, delegate(State<WorldState> state) {
                if (m_LifeCount < 1) {
                    m_State.Set(WorldState.GameOver);
                }
                else {
                    ResetLevel();
                }
            });
            m_View.StartPacmanDeathAnimation();
        }


        public void Render(Canvas2D graphics) {
            m_View.Render(graphics);
        }

        #region IDisposable Members

        public void Dispose() {
            m_View.Dispose();
        }

        #endregion
    }
}
