using System;
using System.Collections.Generic;
using System.Text;
using HumanLikeBattleRobots.Control;
using HumanLikeBattleRobots.Data;
using HumanLikeBattleRobots.Graphic;
using QuadEngine;

namespace HumanLikeBattleRobots.Logic
{
    class GameManager
    {
        private IQuadRender render;

        public static PlayerRobot myRobot;
        public static Vec2f camera = new Vec2f(0, 0);
        private Vec2f currentScreen = new Vec2f(10, 10);

        public static List<EnemyRobot> enemyRobots = new List<EnemyRobot>();
        public static List<Bullet> bullets = new List<Bullet>();
        public static List<ICollidable> collidable = new List<ICollidable>();
        public static List<IFX> FX = new List<IFX>();
        public static List<IOtherObjects> otherObjects = new List<IOtherObjects>();
        private static List<StarSprite> stars = new List<StarSprite>();
        private static Vec2f starPosition = new Vec2f(10, 10);

        private List<Vec2f> createdScreens = new List<Vec2f>();
        private List<Screen> screens = new List<Screen>();

        public static Random MainRandom;

        private float timer = 0;
        private float restartTimer = 0;
        public static float hitBonusTimer = 0;

        public static bool NeedToRestart { get; set; }
        public static int enemiesKilled = 0;

        private InfoSprite info;
        public static bool Mute = false;

        public GameManager(IQuadDevice quadDevice, IQuadRender quadRender)
        {
            GraphicResources.Initialize(quadDevice, quadRender);
            render = quadRender;

            SoundResources.Initialize();

            MainRandom = new Random();

            NeedToRestart = false;

            info = new InfoSprite(2);

            Restart();
        }
        
        public static Vec2f CheckCollision(ICollidable myObject)
        {
            Vec2f pushOff = new Vec2f(0, 0);

            foreach (ICollidable otherObject in collidable)
            {
                if (myObject != otherObject)
                     pushOff += CollisionBetween(myObject, otherObject);
            }

            return pushOff;
        }

        private static Vec2f CollisionBetween(ICollidable firstObject, ICollidable secondObject)
        {
            Vec2f pushOff = new Vec2f(0, 0);
            Vec2f vectorBetween = firstObject.Position - secondObject.Position;
            if (vectorBetween.Length() < firstObject.Radius + secondObject.Radius)
                pushOff = vectorBetween.Normalize() * (firstObject.Radius + secondObject.Radius) - vectorBetween;
            return pushOff;
        }

        private void Restart()
        {
            restartTimer = 0.75f;
            timer = 0;

            myRobot = new PlayerRobot(new Vec2f(0, 0));
            Keyboard.OnKey += myRobot.KeyReaction;
            Mouse.OnMouse += myRobot.MouseMoveReaction;
            Mouse.OnMouse += myRobot.MouseDownReaction;

            info.Position = myRobot.Position + new Vec2f(200, 200);

            currentScreen = new Vec2f(10, 10);
            starPosition = new Vec2f(10, 10);
            stars.Clear();
            screens.Clear();
            createdScreens.Clear();
            bullets.Clear();
            FX.Clear();
            otherObjects.Clear();
            collidable.Clear();
            enemyRobots.Clear();
            collidable.Add(myRobot);

            NeedToRestart = false;
            enemiesKilled = 0;

            if (!GameManager.Mute)
            {
                Bass.Bass.BASS_Stop();
                Bass.Bass.BASS_Start();
                Bass.Bass.BASS_StreamPlay(SoundResources.Music, false, (UInt16)4);
            }
        }

        public static bool CheckBulletCollision(Bullet bullet, bool enemyBullet)
        {
            bool result = false;

            float damage = 1;

            if (bullet is Bullet)
                damage = 0.5f;
            if (bullet is Cannonball || bullet is MyCannonball)
                damage = 10;

            if (enemyBullet)
            {
                if (CollisionBetweenBullet(bullet, myRobot))
                {
                    (myRobot as ICollidable).GetDamage(damage);
                    if (!myRobot.IsAlive)
                    {
                        new Explosion(myRobot.Position);
                    }
                }
                foreach (ICollidable otherObject in collidable)
                    if (CollisionBetweenBullet(bullet, otherObject))
                        result = true;
            }
            else
            {
                if (hitBonusTimer > 0)
                    damage *= 3;
                foreach (ICollidable otherObject in collidable)
                    if (CollisionBetweenBullet(bullet, otherObject))
                    {
                        otherObject.GetDamage(damage);
                        if (otherObject is Robot && !(otherObject as Robot).IsAlive)
                        {
                            enemiesKilled++;
                            stars.Add(new StarSprite(1));
                            if (enemiesKilled == 60)
                                Bass.Bass.BASS_SamplePlayEx(SoundResources.Laugh,
                                    0, 0, 100, 0, false);
                        }
                        result = true;
                    }
            }
            return result;
        }

        private static bool CollisionBetweenBullet(Bullet bullet, ICollidable someObject)
        {
            Vec2f vectorBetween = bullet.Position - someObject.Position;
            if (vectorBetween.Length() < bullet.HitRadius + someObject.HitRadius)
                return true;
            else
                return false;
        }

        public void Proceed(double delta)
        {
            #region Куча говнокода для красивой перезагрузки
            if (NeedToRestart)
                restartTimer += (float)delta;
            else
                restartTimer -= (float)delta;
            if (restartTimer >= 0.75f)
                Restart();

            if (restartTimer < 0)
                restartTimer = 0;

            if (myRobot.IsAlive)
                myRobot.Proceed(delta);
            else
                timer += (float)delta;
            #endregion

            #region Создание новых экранов по мере продвижения игрока
            if (currentScreen != Screen.GetCurrentScreen(myRobot.Position))
            {
                currentScreen = Screen.GetCurrentScreen(myRobot.Position);

                for (int i = createdScreens.Count - 1; i >= 0; i--)
                    if ((currentScreen - createdScreens[i]).Length() > 4) //MAGIC NUMBER!
                    {
                        createdScreens.Remove(createdScreens[i]);
                        screens.Remove(screens[i]);
                    }

                for (int i = -1; i < 2; i++)
                    for (int j = -1; j < 2; j++)
                        if (!createdScreens.Contains(currentScreen + new Vec2f(i, j)))
                        {
                            //создается экран
                            screens.Add(new Screen(currentScreen + new Vec2f(i, j)));
                            createdScreens.Add(currentScreen + new Vec2f(i, j));
                        }
            }
            #endregion

            camera = myRobot.Position - new Vec2f(1024/2, 768/2);
            info.Position = myRobot.Position + new Vec2f(450, 240);
            info.Health = myRobot.Health;

            if (hitBonusTimer > 0)
                hitBonusTimer += (float)delta;
            if (hitBonusTimer > 10)
                hitBonusTimer = 0;

            for (int i = 0; i < stars.Count; i++)
            {
                stars[i].Position = camera + new Vec2f(15 + 25 * (i % 7), 20 + 25 * (int)(i/7));
            }

            for (int i = enemyRobots.Count - 1; i >= 0; i--)
                if (!enemyRobots[i].IsAlive || enemyRobots[i].Position.Distance(myRobot.Position) > 2500)
                {
                    new Explosion(enemyRobots[i].Position);
                    collidable.Remove(enemyRobots[i]);
                    enemyRobots.Remove(enemyRobots[i]);
                }


            foreach (Robot enemyRobot in enemyRobots)
                enemyRobot.Proceed(delta);
            
            for (int i = bullets.Count - 1; i >= 0; i--)
            {
                if ((Math.Abs(bullets[i].Position.X - myRobot.Position.X) > 512 || Math.Abs(bullets[i].Position.Y - myRobot.Position.Y) > 384) &&
                    bullets[i].Stage != 0)
                    bullets.Remove(bullets[i]);
                else if  (bullets[i].Stage == 0 && bullets[i].Position.Distance(myRobot.Position) > 1200)
                    bullets.Remove(bullets[i]);
                else
                    bullets[i].Proceed(delta);
            }

            for (int i = FX.Count - 1; i >= 0; i--)
                if (FX[i].IsOver)
                    FX.Remove(FX[i]);
                else
                    FX[i].Proceed(delta);

            for (int i = otherObjects.Count - 1; i >= 0; i--)
                otherObjects[i].Proceed();
        }

        public void Draw()
        {
            foreach (Screen screen in screens)
                screen.Draw();

            render.SetBlendMode(TQuadBlendMode.qbmSrcAlphaMul);
            foreach(Bullet bullet in bullets)
                if (bullet is Cannonball)
                    (bullet as Cannonball).DrawShadow();

            render.SetBlendMode(TQuadBlendMode.qbmSrcAlpha);
            foreach (Bullet bullet in bullets)
                if (bullet.Stage == 0)
                    bullet.Draw();

            render.SetBlendMode(TQuadBlendMode.qbmSrcAlphaMul);
            foreach (Robot enemyRobot in enemyRobots)
                enemyRobot.Draw(0);

            if (myRobot.IsAlive)
                myRobot.Draw(0);

            render.SetBlendMode(TQuadBlendMode.qbmSrcAlpha);

            for (int i = otherObjects.Count - 1; i >= 0; i--)
            {
                if (otherObjects[i].Stage() == 0)
                    otherObjects[i].Draw();
            }

            for (int i = otherObjects.Count - 1; i >= 0; i--)
            {
                if (otherObjects[i].Stage() != 0)
                    otherObjects[i].Draw();
            }

            for (int stage = 1; stage <= 3; stage++)
            {
                if (myRobot.IsAlive)
                    myRobot.Draw(stage);

                foreach (Robot enemyRobot in enemyRobots)
                    enemyRobot.Draw(stage);
            }

            foreach (Bullet bullet in bullets)
                if (bullet.Stage != 0)
                    bullet.Draw();

            for (int i = FX.Count - 1; i >= 0; i--)
                FX[i].Draw();

            foreach (StarSprite star in stars)
                star.Draw();

            render.SetBlendMode(TQuadBlendMode.qbmSrcAlphaAdd);
            info.Draw();
            render.SetBlendMode(TQuadBlendMode.qbmSrcAlpha);

            //GraphicResources.TestFont.TextOut(new Vec2f(800, 10), 1, "До базы " + 
            //    ((float)(myRobot.Position.Length()/100)).ToString("0.0") + " м", QuadColor.White);

            QuadColor color = QuadColor.White;
            color.A = Math.Min(timer/5,1);
                
            GraphicResources.TestFont.TextOut(new Vec2f(280, 10), 1, "Вы проиграли! Нажмите R для перезапуска.", color);
            GraphicResources.TestFont.TextOut(new Vec2f(400, 40), 1, "Уничтожено врагов: " + enemiesKilled.ToString(), color);

            //render.SetBlendMode(TQuadBlendMode.qbmNone);
            if (restartTimer != 0)
            {
                QuadColor restartScreenColor = QuadColor.Black;
                restartScreenColor.A = restartTimer/0.75f;
                render.Rectangle(new Vec2f(0, 0), new Vec2f(1024, 768), restartScreenColor);
            }
        }
    }
}
