using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using TestBoids.Graphic;
using Ika.Components;
using TestBoids.Scene;
using TestBoids.Entities;
using TestBoids.Sound;
using Ika.Graphics;
using TestBoids.Collisions;
using TestBoids.Gameplay;

namespace TestBoids
{

    public enum GameState
    {
        GW_MENU,
        GW_FADE, 
        GW_INTRO_FAST,
        GW_INTRO_SLOW,
        GW_GAME
    }

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class MyGame : Microsoft.Xna.Framework.Game
    {
        GameState gameState = GameState.GW_MENU;
        GraphicsDeviceManager graphics;
        BirdsRenderer m_renderer;
        StatsViewerComponent m_statsViewer;
        FramerateComponent m_framerate;
        RelativeKeyboardComponent m_keyboard;
        RelativeMouseComponent m_mouse;
        SoundManager m_soundManager;
        BoidManagerAlt m_boidManager;
        EntityFactory m_factory;
        SceneManager m_sceneManager;

        bool m_vsync = true;
        bool m_drawDebug = false;
        
        // used for state :p
        float m_GW_alphaLogo = 1;
        float m_GW_alphafade = 0.01f;
        float m_GW_cameraSpeed = 0.01f;
        Vector2 m_GW_frameLeft = new Vector2(32, 300);


        Texture2D m_frameTex, m_frameRightTex, m_frameLeftTex, m_titleTex;
        int m_curentLevel = 1;
        public int Level
        {
            get { return m_curentLevel; }
            set { m_curentLevel = value; }
        }

        LevelSpawner m_levelspawner;
        IntroEntity[] m_introEntity;
        LevelEntity[] m_level;

        public MyGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            Window.Title = "Birds";

            m_sceneManager = new SceneManager();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            m_renderer = new BirdsRenderer(this);
            m_statsViewer = new StatsViewerComponent(this);
            m_framerate = new FramerateComponent(this);
            m_factory = new EntityFactory(this);
            m_framerate.Visible = false;
            m_keyboard = new RelativeKeyboardComponent(this);
            m_mouse = new RelativeMouseComponent(this);
            m_soundManager = new SoundManager(this);
            m_boidManager = new BoidManagerAlt();
            m_boidManager.SceneManager = m_sceneManager;

            m_sceneManager.Factory = m_factory;

            Components.Add(m_renderer);
            Components.Add(m_statsViewer);
            Components.Add(m_framerate);
            Components.Add(m_keyboard);
            Components.Add(m_mouse);
            Components.Add(m_factory);
            Components.Add(m_soundManager);

            IsMouseVisible = true;

            graphics.SynchronizeWithVerticalRetrace = m_vsync;
            this.IsFixedTimeStep = m_vsync;
            graphics.ApplyChanges();

            m_frameTex = Content.Load<Texture2D>("Sprites/Frame");
            m_frameRightTex = Content.Load<Texture2D>("Sprites/FrameRight");
            m_frameLeftTex = Content.Load<Texture2D>("Sprites/FrameLeft");
            m_titleTex = Content.Load<Texture2D>("Sprites/titre");

            base.Initialize();

            Random r = new Random();

            m_level = new LevelEntity[6];
            for (int i = 0; i < m_level.Length; i++)
            {
                m_level[i] = m_factory.CreateLevel(new Vector2(1800 * (i + 1), 300), i + 1);
            }

            m_level[0].Position = new Vector2(790, 300);
            m_level[0].cTop = Color.DarkBlue;
            m_level[0].cBot = Color.LightBlue;
            m_level[1].cTop = Color.DarkGreen;
            m_level[1].cBot = Color.LightGreen;
            m_level[2].cTop = Color.Teal;
            m_level[2].cBot = Color.DarkGoldenrod;
            m_level[3].cTop = Color.LightCoral;
            m_level[3].cBot = Color.LightGreen;
            m_level[4].cTop = Color.WhiteSmoke;
            m_level[4].cBot = Color.Tomato;

            //m_level[4].Position += new Vector2(1000, 0);

            
            Boid b = (Boid)m_sceneManager.AddEntity(m_factory.CreateBoid(new Vector2(15, 290)));
            b.m_isPassive = false;
            b = (Boid)m_sceneManager.AddEntity(m_factory.CreateBoid(new Vector2(40, 320)));
            b.m_isPassive = false;
            b = (Boid)m_sceneManager.AddEntity(m_factory.CreateBoid(new Vector2(25, 340)));
            b.m_isPassive = false;


            m_levelspawner = new Level0();
            m_sceneManager.AddEntity(m_levelspawner);
        }
         
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            Random r = new Random();
            
            if (m_keyboard.isKeyPressed(Keys.D))
            {
             //  m_drawDebug = !m_drawDebug;
            }


            if (gameState == GameState.GW_GAME)
            {
                //if (Mouse.GetState().RightButton == ButtonState.Pressed)
                //{
                //    float offset = 5;

                //    for (int i = 0; i < 3; i++)
                //    {
                //        Vector2 pos = m_renderer.ScreenToWorld(new Vector2(m_mouse.State.X, m_mouse.State.Y));
                //        pos += new Vector2(r.Next(-20, 20) * offset, r.Next(-20, 20) * offset);

                //        Entity e = m_factory.CreateBoid(pos);

                //        m_sceneManager.AddEntity(e);
                //    }
                //}

                if (m_mouse.LeftButton)
                {
                    m_sceneManager.SelectedEntity = m_sceneManager.FindBoidInCell(m_renderer.ScreenToWorld(new Vector2(m_mouse.State.X, m_mouse.State.Y)));
                    // Changed !!!
                    if (m_sceneManager.SelectedEntity != null && m_sceneManager.SelectedEntity.m_isPassive)
                    {
                        m_sceneManager.SelectedEntity = null;
                    }
                    if (m_sceneManager.SelectedEntity != null && !m_sceneManager.SelectedEntity.Leader)
                    {
                        m_sceneManager.SelectedEntity.Leader = true;
                    }
                }

                if (Mouse.GetState().LeftButton == ButtonState.Pressed && m_sceneManager.SelectedEntity != null)
                {
                    if (m_mouse.Y != 0 && (Math.Abs(m_sceneManager.SelectedEntityGoal.Y) < Math.Abs(m_mouse.Y)
                        || Math.Sign(m_sceneManager.SelectedEntityGoal.Y) != Math.Sign(m_mouse.Y)))
                        m_sceneManager.SelectedEntityGoal = new Vector2(5, m_mouse.Y);
                    else if ((m_sceneManager.SelectedEntityGoal.Y > 0 && m_sceneManager.SelectedEntity.Position.Y > Mouse.GetState().Y) ||
                        (m_sceneManager.SelectedEntityGoal.Y < 0 && m_sceneManager.SelectedEntity.Position.Y < Mouse.GetState().Y))
                        m_sceneManager.SelectedEntityGoal = new Vector2();


                    m_sceneManager.UpdatePosition(m_sceneManager.SelectedEntity,
                        new Vector2(0,
                        Math.Sign(m_sceneManager.SelectedEntityGoal.Y) *
                        MathHelper.Min(Math.Abs(m_sceneManager.SelectedEntityGoal.Y - m_sceneManager.SelectedEntity.Position.Y), m_sceneManager.SelectedEntity.MaxSpeed / 5))
                        + m_sceneManager.SelectedEntity.Position);
                }
                else if (Mouse.GetState().LeftButton == ButtonState.Released)
                    m_sceneManager.SelectedEntity = null;

            }

            m_sceneManager.EnergyList.Clear();
            List<Entity> toDelete = new List<Entity>();
            foreach (Entity e in m_sceneManager)
            {
                if (m_renderer.WorldToScreen(e.Position).X < -50 || m_renderer.WorldToScreen(e.Position).Y < -50)
                {
                    toDelete.Add(e);                    
                }
                if(e is EnergyEntity)
                {
                    m_sceneManager.EnergyList.Add((EnergyEntity)e);
                }
                
            }

            int lastLevel = Level;
            foreach (Entity e in m_sceneManager)
            {
                if (e is BlockEntity)
                {
                    Boid bcoll = m_sceneManager.EntityCollideBoid(e);
                    if (bcoll != null)
                    {
                        if (bcoll.Leader)
                        {
                            List<Entity> lboid = m_sceneManager.FindInRadius(bcoll.Position, 60);
                            Boid newLeader = null;
                            float dist = 150, tmp;
                            foreach (Entity ent in lboid)
                            {
                                if (ent is Boid)
                                {
                                    Boid b = (Boid)ent;
                                    tmp = Vector2.Distance(b.Position, bcoll.Position);
                                    if (b.Leader && tmp > 0)
                                    {
                                        newLeader = null;
                                        break;
                                    }
                                    if (tmp < dist && tmp > 0)
                                    {
                                        newLeader = b;
                                        dist = Vector2.Distance(b.Position, bcoll.Position);
                                    }
                                }
                            }

                            if (newLeader != null)
                                newLeader.Leader = true;
                        }
                        bcoll.isAlive = false;
                        //toDelete.Add(bcoll);

                    }
                }
                // boid
                if (e is Boid)
                {

                    Vector2 result = m_boidManager.GetFinalSpeed((Boid)e);
                    // champ de force
                    foreach (EnergyEntity energy in m_sceneManager.EnergyList)
                    {
                        if (e.isCollideWith(energy)) result += energy.Power;
                    }

                    if (gameState == GameState.GW_GAME)
                    {
                        for (int i = 0; i < m_level.Length; i++)
                        {
                            if(m_sceneManager.WorldToScreen(m_level[i].Position).X < 700)
                            if (m_level[i].Level != ((Boid)e).Level && e.isCollideWith(m_level[i]))
                            {
                                if (Level < m_level[i].Level && Level < 5)
                                {
                                    Level = m_level[i].Level;
                                    m_factory.ChangeLevel(m_level[i].Level);                                    
                                }

                                m_factory.UpdateBoid((Boid)e);
                                ((Boid)e).Level = Level;
                            }
                        }
                    }
                    ((Boid)e).SetGoal(e.Position + result);
                }
            }

            /**********************************/
            /**********************************/
            //foreach (Entity e in m_sceneManager)
            //{
            //    /***************************************/
            //    /* Code pour gerer les champs de force */
            //    /***************************************/
            //    if (e is EnergyEntity)
            //    {
            //        Boid bcoll = m_sceneManager.EntityCollideBoid(e);
            //        if (bcoll != null)
            //        {
            //            bcoll.Speed += ((EnergyEntity)e).Power;
            //        }
            //    }
            //}
            /**********************************/
            /**********************************/

            if (lastLevel != Level)
            {
                if (Level == 2)
                {
                    toDelete.Add(m_levelspawner);
                    m_levelspawner = new Level2(12, 12.5f, 45.0f);
                    m_sceneManager.AddEntity(m_levelspawner);
                    m_soundManager.PlayCue("vent2");
                }
                if (Level == 3)
                {
                    toDelete.Add(m_levelspawner);
                    m_levelspawner = new Level3(9.5f, 25.0f);
                    m_sceneManager.AddEntity(m_levelspawner);
                    m_soundManager.PlayCue("sousmarin");
                }
                if (Level == 4)
                {
                    toDelete.Add(m_levelspawner);
                    m_levelspawner = new Level4(9.5f, 19.0f);
                    m_sceneManager.AddEntity(m_levelspawner);
                    m_soundManager.PlayCue("foret2");
                }
                if (Level == 5)
                {
                    toDelete.Add(m_levelspawner);
                    m_levelspawner = new Level5(19.5f, 35.0f);
                    m_sceneManager.AddEntity(m_levelspawner);
                    m_soundManager.PlayCue("vent");
                }
            }

            if (gameState == GameState.GW_GAME || gameState == GameState.GW_INTRO_SLOW || gameState == GameState.GW_INTRO_FAST)
            {
                m_renderer.CameraPosition += new Vector2(m_GW_cameraSpeed, 0); 
                m_sceneManager.CameraPosition = m_renderer.CameraPosition;
            }                     

            if (gameState == GameState.GW_MENU)
            {
                if (Keyboard.GetState().GetPressedKeys().Length == 1)
                    gameState = GameState.GW_FADE;
            }
            else if (gameState == GameState.GW_FADE)
            {
                m_GW_alphaLogo -= m_GW_alphafade;
                if (m_GW_alphaLogo <= -1)
                {
                    gameState = GameState.GW_INTRO_FAST;
                    m_introEntity = new IntroEntity[3];
                    m_introEntity[0] = m_factory.CreateIntroBoid(new Vector2(300, 500), new Vector2(1350, 240), 25.0f);
                    m_introEntity[1] = m_factory.CreateIntroBoid(new Vector2(320, 550), new Vector2(1370, 250), 24.0f);
                    m_introEntity[2] = m_factory.CreateIntroBoid(new Vector2(310, 520), new Vector2(1380, 235), 26.0f);

                    for (int i = 0; i < 3; i++)
                    {
                        m_sceneManager.AddEntity(m_introEntity[i]);
                    }                  
                }
            }
            else if (gameState == GameState.GW_INTRO_FAST)
            {
                m_GW_cameraSpeed += 0.04f;
                if (m_GW_cameraSpeed > 7.1f)
                {
                    gameState = GameState.GW_INTRO_SLOW;
                }
            }
            else if (gameState == GameState.GW_INTRO_SLOW)
            {
                m_GW_cameraSpeed -= 0.07f;
                for (int i = 0; i < 3; i++)
                {
                    m_introEntity[i].MaxSpeed -= 0.25f;
                }

                if (m_GW_cameraSpeed < 0.9f)
                {
                    gameState = GameState.GW_GAME;                    
                    
                    m_GW_cameraSpeed = 1.2f;
                    Boid b;
                    for (int i = 0; i < 3; i++)
                    {
                        b = (Boid)m_sceneManager.AddEntity(m_factory.CreateBoid(m_introEntity[i].Position));
                        if( i == 2)
                            b.Leader = true;
                        m_sceneManager.RemoveEntity(m_introEntity[i]);
                    }

                    toDelete.Add(m_levelspawner);
                    m_levelspawner = new Level1(10, 15.5f, 35.0f);
                    m_sceneManager.AddEntity(m_levelspawner);
                    m_soundManager.PlayCue("oiseaux");
                }
            }

            // Changed !!!
            foreach (Entity e in toDelete)
            {
                m_levelspawner.RemoveObstacle(e);
                m_sceneManager.RemoveEntity(e);
            }

            // Changed !!!
            int count = 0;
            foreach (Entity e in m_sceneManager)
            {
                if (e is Boid && ((Boid)e).m_isPassive == false)
                {
                    count++;
                }
            }
            //m_statsViewer.SetEntryValue("Birds", "" + count);


            m_sceneManager.Update(0.2f);

            // Changed !!!
            //m_statsViewer.SetEntryValue("Framerate", "" + m_framerate.Framerate);
            //m_statsViewer.SetEntryValue("Level", "" + Level);
            //m_statsViewer.SetEntryValue("Level Timer", "" + (int)m_levelspawner.Timer);
            //m_statsViewer.SetEntryValue("Entity count", "" + gameState);
            //m_statsViewer.SetEntryValue("EnergyList", "" + m_sceneManager.EnergyList.Count);

            base.Update(gameTime);
        }



        void DrawGrid()
        {
            Point screen = new Point(GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight);
            for (float i = 0; i < screen.X; )
            {
                m_renderer.DrawLine(new Vector2(i, 0), new Vector2(i, screen.Y));
                i += m_sceneManager.Grid.Cellsize;
            }
            for (float j = 0; j < screen.Y; )
            {
                m_renderer.DrawLine(new Vector2(0, j), new Vector2(screen.X, j));
                j += m_sceneManager.Grid.Cellsize;
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            bool skyDrawed = false;
            for (int i = 1; i < m_level.Length; i++)
            {
                if (m_renderer.WorldToScreen(m_level[i].Position).X < 800 && m_renderer.WorldToScreen(m_level[i].Position).X > 0)
                {
                    // double
                    skyDrawed = true;
                    Vector2 v = m_renderer.WorldToScreen(m_level[i].Position);

                    m_renderer.DrawSky(m_level[i].cTop, m_level[i].cBot, new Vector2(v.X + (800 - v.X)/2, 300), new Vector2(800 - v.X, 600));
                    m_renderer.DrawSky(m_level[i - 1].cTop, m_level[i - 1].cBot, new Vector2(v.X/2, 300), new Vector2(v.X, 600));

                }
            }

            if(!skyDrawed)
                m_renderer.DrawSky(m_level[Level-1].cTop, m_level[Level-1].cBot, new Vector2(400, 300), new Vector2(800, 600));


            if (gameState == GameState.GW_GAME && m_drawDebug)
            {
                m_renderer.DebugColor = Color.Blue;                
                if (m_sceneManager.SelectedEntity != null)
                {
                    m_renderer.DrawLine(m_sceneManager.SelectedEntity.Position, m_sceneManager.SelectedEntity.Position + m_sceneManager.SelectedEntityGoal * 5);
                }
            }


            // Changed !!!
            m_renderer.DebugColor = Color.Orange;
            foreach (Entity e in m_sceneManager)
            {
                if (e is Boid && ((Boid)e).Leader)
                {
                    m_renderer.DrawLeaderBird(e);
                }
                else
                {
                    if (e is Boid && ((Boid)e).m_isPassive)
                    {
                        m_renderer.DrawEntity(e, Color.Gray);
                    }
                    else
                    {
                        m_renderer.DrawEntity(e);
                    }
                }
                m_renderer.DebugColor = Color.DarkBlue;
                if(m_drawDebug && e is BlockEntity)
                {
                    if (e.Collision is BoxCollision)
                        m_renderer.DrawRectangle(e.Collision.Bounds + e.Collision.Position, ((BoxCollision)e.Collision).Size);
                    else if (e.Collision is CircleCollision)
                        m_renderer.DrawCircle(e.Collision.Bounds + e.Collision.Position, ((CircleCollision)e.Collision).Radius);
                }

                m_renderer.DebugColor = Color.Gold;
                if (m_drawDebug && e is EnergyEntity)
                {                    
                    m_renderer.DrawRectangle(e.Collision.Bounds + e.Collision.Position, ((BoxCollision)e.Collision).Size);                    
                }

                if (m_drawDebug && e is Boid)
                {
                    if (((Boid)e).Leader)
                    {
                        m_renderer.DebugColor = Color.Red;
                    }
                    else
                    {
                        m_renderer.DebugColor = Color.Orange;
                    }

                    m_renderer.DrawCircle(e.Collision.Bounds + e.Collision.Position, ((CircleCollision)e.Collision).Radius);
                }
            }


            m_renderer.DrawHUD(new StaticSprite(m_frameTex, new Vector2(800, 600)), new Vector2(400, 300), 0);

            
            if (gameState == GameState.GW_MENU || gameState == GameState.GW_FADE)
            {
                m_renderer.DrawSprite(m_titleTex, new Rectangle(0, 0, 800, 600), new Vector2(400, 300), new Vector2(800, 600), 0, m_GW_alphaLogo);
                m_renderer.DrawHUD(new StaticSprite(m_frameTex, new Vector2(800, 600)), new Vector2(400, 300), 0);

                m_renderer.DrawSprite(m_frameLeftTex, new Rectangle(0, 0, 64, 600), m_GW_frameLeft, new Vector2(64, 600), 0, 1);
                
                if(gameState == GameState.GW_MENU)
                    m_renderer.DrawText("press any key to start", new Vector2(300, 400), Color.Black);                   

            }
            else if (gameState == GameState.GW_INTRO_FAST)
            {
                m_GW_frameLeft.X -= 1;
                m_renderer.DrawSprite(m_frameLeftTex, new Rectangle(0, 0, 64, 600), m_GW_frameLeft, new Vector2(64, 600), 0, 1);
            }

            for (int i = Level - 1; i < m_level.Length; i++)
            {
                m_renderer.DrawSprite(m_level[i].Sprite, m_level[i].Position);
            }

            base.Draw(gameTime);
        }
    }
}
