using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SFML.Graphics;
using SFML.Window;
using YourBeatingHeart.Main.Entities;
using YourBeatingHeart.Main.Helper;
using YourBeatingHeart.Main.States.Play.Collision;
using YourBeatingHeart.Main.States.Play.Entities;
using YourBeatingHeart.Main.States.Play.Levels;
using YourBeatingHeart.Main.Managers;
using System.Diagnostics;
using YourBeatingHeart.Main.Effects;
using SlimDX.XInput;
using SFML.Audio;
using YourBeatingHeart.Main.Sound;

namespace YourBeatingHeart.Main.States.Play
{
    public class PlayState : GameState
    {
        public class Layers
        {
            public const int Sky = 0;
            public const int Moon = 1;
            public const int LevelForeground = 2;
            public const int TempleBlood = 3;
            public const int HeartEater = 4;
            public const int Players = 5;
            public const int Items = 6;
            public const int Overlay = 9;
            public const int TopmostText = 10;
        }

        protected EntityList<Entity> EntityList { get; set; }
        public EntityList<Item> ItemList { get; set; }

        public List<Player> Players { get; protected set; }

        public LiveTweakingManager TweakingManager { get; protected set; }
        public Stopwatch Timer { get; protected set; }
        public CollisionManager CollisionManager { get; protected set; }
        public Level Level;
        public ScreenShake ScreenShake { get; protected set; }

        PointDisplayEntity pointDisplay;
        public HeartEater HeartEater;

        public int totalScore;
        public Player wonPlayer;
        private bool noWinner;
        private List<SFML.Audio.Music> music = new List<SFML.Audio.Music>();
        private int activeMusicIndex = 1;

        public float killingPhaseCountdown;
        private int spawnPlayerCount;
        public long RestartTime;

        public float[] HeartBeatEffectValues { get; protected set; }

        //public bool GameOver
        //{
        //    get { return (wonPlayer != null) || noWinner; }
        //}

        public Font Font { get; private set; }

        public PlayState(Game game, int spawnPlayerCount) : base(game)
        {
            Font = Game.LoadFont("feast_of_flesh.TTF");

            EntityList = new EntityList<Entity>();
            Players = new List<Player>();
            ItemList = new EntityList<Item>();

            CollisionManager = new CollisionManager();
            Level = new Level(Game);
            Level.Load("Level");

            ScreenShake = new ScreenShake();

            wonPlayer = null;
            noWinner = false;

            this.spawnPlayerCount = spawnPlayerCount;

            killingPhaseCountdown = 0;
        }

        public void AddEntity(Entity entity)
        {
            EntityList.Add(entity);

            if (entity is Item)
                ItemList.Add((Item) entity);

            //entity.Added();
        }

        public void RemoveEntity(Entity entity)
        {
            EntityList.Remove(entity);

            if (entity is Item)
                ItemList.Remove((Item) entity);

            //entity.Removed();
        }

        public override void SetUp()
        {
            TweakingManager = new LiveTweakingManager(this);

            //score = new int[4];
            //for (int i = 0; i < 4; ++i) { score[i] = 0; }
            HeartBeatEffectValues = CurveEditorHelper.ReadCurveFile(Path.Combine(Constants.FOLDER_DATA, "heart_beat.cbv"));

            music.Add(new SFML.Audio.Music(Path.Combine(Constants.FOLDER_SOUNDS, "Song_Fast.wav")));
            music.Add(new SFML.Audio.Music(Path.Combine(Constants.FOLDER_SOUNDS, "Song_Slow.wav")));
            music[activeMusicIndex].Play();
            music[activeMusicIndex].Loop = true;

            

            var playerTextureOrigin = new Vector2f(64, 112);

            const float playerY = Constants.PLAYER_START_HEIGHT;
            while (Players.Count < spawnPlayerCount && new Controller(ControllerVibration.IdToUserIndex((uint)Players.Count)).IsConnected)
            {
                var player1 = new Player(this, Constants.PLAYER_NAMES[Players.Count], new Vector2f((((float)Players.Count+1) / (spawnPlayerCount+1)) * Constants.TARGET_WIDTH, playerY), playerTextureOrigin,
                                            Keyboard.Key.F2, Keyboard.Key.F2, Keyboard.Key.F2, Keyboard.Key.F2, Keyboard.Key.F2, Keyboard.Key.F2, (uint)Players.Count);
                AddEntity(player1);
                Players.Add(player1);
            }

            if (Players.Count < spawnPlayerCount)
            {
                var player2 = new Player(this, Constants.PLAYER_NAMES[Players.Count], new Vector2f((((float)Players.Count + 1) / (spawnPlayerCount + 1)) * Constants.TARGET_WIDTH, playerY), playerTextureOrigin,
                                         Keyboard.Key.Up, Keyboard.Key.Down, Keyboard.Key.Left, Keyboard.Key.Right, Keyboard.Key.O, Keyboard.Key.P, (uint)Players.Count);
                AddEntity(player2);
                Players.Add(player2);
            }

            if (Players.Count < spawnPlayerCount)
            {
                var player3 = new Player(this, Constants.PLAYER_NAMES[Players.Count], new Vector2f((((float)Players.Count + 1) / (spawnPlayerCount + 1)) * Constants.TARGET_WIDTH, playerY), playerTextureOrigin,
                                         Keyboard.Key.W, Keyboard.Key.S, Keyboard.Key.A, Keyboard.Key.D, Keyboard.Key.Tab, Keyboard.Key.Q, (uint)Players.Count);
                AddEntity(player3);
                Players.Add(player3);
            }

            pointDisplay = new PointDisplayEntity(this);
            AddEntity(pointDisplay);
            totalScore = 0;

            
            //var heart = new Heart(this, new Vector2f(400, 1024), player2);
            //heart.Throw(new Vector2f(1, -1.2f));
            //AddEntity(heart);

            Window.JoystickButtonPressed += WindowOnJoystickButtonPressed;
            Window.KeyPressed += WindowOnKeyPressed;

            Timer = new Stopwatch();
            Timer.Restart();
            RestartTime = long.MaxValue;
            AddEntity(new TimerTextEntity(this, new Vector2f(Constants.TARGET_WIDTH / 2, 10), Layers.TopmostText));
            AddEntity(new MoonEntity(this));

            Level.Show(this);
            HeartEater = new HeartEater(this, new Vector2f(830, 110));
            AddEntity(HeartEater);

            for (var i = 0; i < Constants.REMAINS_COUNT; i++)
                SpawnRemains();

            int index = Game.Random(2);
            if (index == 0)
                SoundManager.Instance.PlaySound(SoundEvent.AnnouncerStart1);
            else if (index == 1)
                SoundManager.Instance.PlaySound(SoundEvent.AnnouncerStart2);
        }

        public void SpawnRemains()
        {
            float minX = 50;
            float maxX = Constants.TARGET_WIDTH - minX * 2;
            float minY = 0;
            float maxY = 1020;

            RemainType type = RemainType.Arm;
            if (Game.Random(0f, 1f) > 0.5f)
            {
                type = RemainType.Skull;
            }

            var remains = new Remains(this, new Vector2f(Game.Random(minX, maxX), Game.Random(minY, maxY)), "items.xml", type, 40, 40);
            AddEntity(remains);
        }

        public override void TearDown()
        {
            TweakingManager.Destroy();
            TweakingManager = null;
            Window.KeyPressed -= WindowOnKeyPressed;
            Window.JoystickButtonPressed -= WindowOnJoystickButtonPressed;
            Level.Hide(this);
        }

        public override void Update(float elapsedTime)
        {
            //if (GameOver)
            //    return;

            if (RestartTime < Timer.ElapsedMilliseconds)
            {
                Restart();
            }

            EntityList.Update(elapsedTime);
            ScreenShake.Update(elapsedTime);
            UpdateMusic();

            var v = Window.GetView();
            v.Center = ScreenShake.CurrentOffset - new Vector2f(-Constants.TARGET_WIDTH / 2, -Constants.TARGET_HEIGHT / 2);
            Window.SetView(v);
            if (TimeRemaining <= 0)
            {
                if (Players.Count > 0)
                {
                    var player = GetWinningPlayer();
                    if (player != null)
                    {
                        PlayerWon(player);
                    }
                    else
                    {
                        KillingPhaseUpdate(elapsedTime);
                    }
                }
                else
                {
                    PlayerWon(null);
                }
            }
        }

        private void UpdateMusic()
        {
            if (TimeRemaining < 25 * 1000 && activeMusicIndex == 1)
            {
                activeMusicIndex = 0;
                music[1].Stop();
                music[0].Play();
            }

            if (activeMusicIndex == 0 && music[0].Status == SoundStatus.Stopped)
                music[1].Play();
        }

        private void KillingPhaseUpdate(float elapsedTime)
        {
            killingPhaseCountdown -= elapsedTime;
            if (killingPhaseCountdown <= 0)
            {
                killingPhaseCountdown = Constants.KILLING_PHASE_BETWEEN_DELAY;

                ExecuteKills();
            }
        }

        private void ExecuteKills()
        {
            int lowestPoints = Players.Select(player => player.Score).Min();

            foreach (var player in new List<Player>(Players))
            {
                if ((player.Score == lowestPoints) && !player.GodsFavor)
                {
                    Players.Remove(player);
                    player.KilledByGod();
                }
            }
        }

        public void PlayerScored(Player player, int scoreAdded)
        {
            player.Score += scoreAdded;
            totalScore += scoreAdded;
            HeartEater.Play();
            SoundManager.Instance.PlaySound(SoundEvent.TempleBlood);

            UpdateGodsFavor();
        }

        private void UpdateGodsFavor()
        {
            int maxPlayerPoints = Players.Select(player => player.Score).Max();
            foreach (var player in Players)
            {
                player.GodsFavor = player.Score == maxPlayerPoints;
            }
        }

        private Player GetWinningPlayer()
        {
            if (Players.Count == 1)
            {
                return Players[0];
            }
            else
            {
                return null;
            }
        }

        private void WindowOnKeyPressed(object sender, KeyEventArgs args)
        {
            //if (GameOver)
            //{
            //    if ((args.Code == Keyboard.Key.Return) || (args.Code == Keyboard.Key.Space))
            //    {
            //        Restart();
            //    }
            //}

            if (args.Code == Keyboard.Key.Escape)
            {
                BackToSplashScreen();
            }
        }

        private void WindowOnJoystickButtonPressed(object sender, JoystickButtonEventArgs args)
        {
            //if (GameOver)
            //{
            //    if (args.Button == 7) // Start
            //    {
            //        Restart();
            //    }
            //}
        }

        private void Restart()
        {
            Game.SwitchToGameState(new PlayState(Game, spawnPlayerCount));
        }

        private void BackToSplashScreen()
        {
            Game.SwitchToGameState(new SplashScreenState(Game));
        }

        public void RestartDelayed(long delay)
        {
            RestartTime = Timer.ElapsedMilliseconds + delay;
        }

        public long TimeRemaining
        {
            get { return Constants.ROUND_LENGTH - Timer.ElapsedMilliseconds; }
        }

        private void PlayerWon(Player player)
        {
            if (noWinner || (wonPlayer != null))
                return;

            this.wonPlayer = player;

            if (wonPlayer == null)
            {
                noWinner = true;

                /*
                Text gameOverWonText;
                gameOverWonText = new Text("Enraged by the absence of sacrifices,\nthe gods kill you all.", Font, 48);

                var bounds = gameOverWonText.GetLocalBounds();
                gameOverWonText.Position = new Vector2f(Constants.TARGET_WIDTH / 2 - bounds.Width / 2, Constants.TARGET_HEIGHT / 2 - bounds.Height / 2);
                AddEntity(new DrawableEntity(gameOverWonText, Layers.TopmostText));
                 */

                var sprite = new Sprite(Game.LoadTexture("no_winner.png"));
                sprite.Origin = new Vector2f(sprite.Texture.Size.X / 2f, sprite.Texture.Size.Y / 2f);
                sprite.Position = new Vector2f(Constants.TARGET_WIDTH / 2f, Constants.TARGET_HEIGHT / 2f);
                AddEntity(new DrawableEntity(sprite, Layers.TopmostText));

                RestartDelayed(Constants.RESTART_DELAY_AFTER_NO_WINNER);
            }
            else
            {
                wonPlayer.PromotedByGod();
                wonPlayer.CanSacrificeSelf = true;

                SoundManager.Instance.PlaySound(SoundEvent.Winner);
                
                var sprite = new Sprite(Game.LoadTexture("sacrifice_yourself.png"));
                sprite.Origin = new Vector2f(sprite.Texture.Size.X / 2f, sprite.Texture.Size.Y / 2f);
                sprite.Position = new Vector2f(Constants.TARGET_WIDTH / 2f, Constants.TARGET_HEIGHT / 2f);
                AddEntity(new DrawableEntity(sprite, Layers.TopmostText));
                //gameOverWonText = new Text("Sacrifice Yourself!", Font, 72);
                //gameOverWonText.Color = wonPlayer.BodyColor;

            }
        }

        public override void Draw()
        {
            Window.Clear(new Color(0, 0, 0));

            EntityList.Draw(Window);
           
            if (TweakingManager != null)
                TweakingManager.Draw(Window);
        }

        public Vector2f GetSpawnPosition(Player player)
        {
            float border = Constants.RESPAWN_BORDER + player.CollisionRect.Width / 2;
            float x = Game.Random(border, Constants.TARGET_WIDTH - border);
            float y = Constants.RESPAWN_HEIGHT;

            return new Vector2f(x, y);
        }
    }
}
