using System;
using System.Collections.Generic;
using System.Linq;
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.Media;

namespace Hover
{
    class Player : Sprite
    {
        public static Player S;
        static Vector2 StartPos = new Vector2(233, 163);

        public bool Alive { get; private set; }
        public DeathReason DeathReason { get; private set; }

        public Vector2 Size;
        public int GroundSafe = 99;
        public int DeadTime = -10;
        public int Bombs;

        // MOVEMENT
        const float gravity = 0.28f;
        public const float gravityItems = 0.2f;
        const float boost = 2f;
        //const float maxUp = -3f;
        public const float maxDown = 4f;
        public const float maxDownItem = 3f;
        const float moveSpeed = 1.9f;
        const float runMultiplier = 1.8f;
        const float moveFalloff = 0.8f;
        const float jumpVel = -4.6f;
        public Vector2 Velocity;
        bool right;
        bool jumpReleased = false;
        public float fallStart = 0;
        public int fallWarning = 5 * Engine.TileSize;
        public const int fallDeath = 7 * Engine.TileSize;
        public const float ItemDistSq = 17 * 17;

        public int fuel = fuelMax;
        public int fuelCounter;
        public const int fuelMax = 180;
        public const int fuelMin = fuelTick * 2;
        public const int fuelRefill = 60;
        public const int fuelTick = 3; // Amount of fuel taken each fuelcheck, so that "one frame clicks" aren't over

        List<Bomb> bombs = new List<Bomb>();

        public Color fuelColor;

        Rectangle recAlive = new Rectangle(0, 194, 16, 31);
        public Rectangle recDead = new Rectangle(42, 193, 29, 29);
        Rectangle recFall = new Rectangle(17, 194, 20, 31);

        Rectangle recWalk = new Rectangle(0, 225, 17, 31);
        int walkFrame = 0;
        const int walkFrames = 12;
        const int walkFirst = 4;
        int walkCount = 0;
        int walkFrameSpeed = 4;

        // FIRE
        Sprite fire1, fire2;
        int fire1i, fire2i;
        Rectangle[] exhaust;
        int[] exhaustOffset;
        SoundEffectInstance fireSfx;

        public Player()
        {
            S = this;
            Alive = false;

            SourceRec = recAlive;
            Size = new Vector2(SourceRec.Width - 6, SourceRec.Height -3);
            DrawOffset = new Vector2(-3, -3);

            exhaust = new Rectangle[]
            {
                new Rectangle(0, 40, 2, 2),
                new Rectangle(0, Engine.TileSize, 7, 8),
                new Rectangle(8, Engine.TileSize, 9, 12),
                new Rectangle(18, Engine.TileSize, 11, 16),
                new Rectangle(30, Engine.TileSize, 13, 18),
                new Rectangle(44, Engine.TileSize, 13, 19)
            };
            exhaustOffset = new int[] { 3, 4, 5, 6, 6 };

            fire1 = new Sprite();
            fire2 = new Sprite();
            fire1.SourceRec = exhaust[0];
            fire2.SourceRec = exhaust[0];
            fire1i = fire2i = 0;
        }

        public override void Update()
        {
            throwBomb();

            if (DeadTime < 0)
                DeadTime++;

            if (GroundSafe == 0 && fuel < fuelMin)
                fuel = fuelMin;
            
            if (Input.Jump && GroundSafe < 3 && Alive)
            {
                jumpReleased = false;
                Velocity.Y += jumpVel;
                //Engine.sJump.Play();
                if (Engine.Rnd.NextDouble() > 0.5f)
                    Engine.PlaySound("jump3");
                else if (Engine.Rnd.NextDouble() > 0.5f)
                    Engine.PlaySound("jump4");
                else
                    Engine.PlaySound("jump5");
            }
            if (!Input.JumpHold) jumpReleased = true;

            if (Input.JumpHold && jumpReleased && fuel > 0 && Alive)
            {
                fuelCounter++;
                if (fuelCounter >= fuelTick)
                {
                    fuel -= fuelTick;
                    fuel = Math.Max(fuel, 0);
                    fuelCounter = 0;
                }

                //accel.Y -= boost;
                if (Velocity.Y > -boost)
                    Velocity.Y = -boost;

                if (fire1i < 4)
                    fire1i++;
                if (fire2i < 4)
                    fire2i++;

                if (fireSfx == null)
                {
                    fireSfx = Data.Sfx["jet1"].CreateInstance();
                    fireSfx.Volume = Engine.S.sfxVolume;
                    fireSfx.IsLooped = true;
                }
                fireSfx.Play();
            }
            else
            {
                fuelCounter = fuelTick;
                Velocity.Y += gravity;

                if (fire1i > 0)
                    fire1i--;
                if (fire2i > 0)
                    fire2i--;

                if (fireSfx != null && fireSfx.State == SoundState.Playing)
                    fireSfx.Pause();
            }

            fire1.SourceRec = exhaust[fire1i];
            fire2.SourceRec = exhaust[fire2i];



            Velocity.Y = MathHelper.Min(Velocity.Y, maxDown);

            if (Alive)
            {
                if (GroundSafe == 0)
                {
                    SourceRec = recWalk;
                    SourceRec.X = (recWalk.Width + 1) * walkFrame;
                }
                else
                    SourceRec = recAlive;
                if (Velocity.Y >= maxDown && Position.Y - fallStart  >= fallWarning)
                    SourceRec = recFall;
            }

            float move = moveSpeed * (Input.Run ? runMultiplier : 1);

            if (Input.Left && Alive)
            {
                Velocity.X = -move;
                if (right)
                {
                    walkFrame = 0;
                    walkCount = 0;
                }
                right = false;
                walkCount++;
            }
            else if (Input.Right && Alive)
            {
                Velocity.X = move;
                if (!right)
                {
                    walkFrame = 0;
                    walkCount = 0;
                }
                right = true;
                walkCount++;
            }
            else
            {
                Velocity.X *= moveFalloff;
                walkFrame = walkFirst;
            }
            if (walkCount >= walkFrameSpeed)
            {
                walkCount = 0;
                walkFrame++;
                if (walkFrame >= walkFrames)
                    walkFrame = walkFirst;
            }

            GroundSafe++;
            Level.S.Collide(this, ref Velocity);

            if (Velocity.Y <= 0.1f)
                fallStart = Position.Y;

            Position += Velocity;
            Effects = right ? SpriteEffects.None : SpriteEffects.FlipHorizontally;

            // Collide level bounds
            if (Position.X < 0)
            {
                Position.X = 0;
                Velocity.X = 0;
            }
            if (Position.X > Engine.GameRes.X - SourceRec.Width)
            {
                Position.X = Engine.GameRes.X - SourceRec.Width;
                Velocity.X = 0;
            }
            //if (Position.Y < 0)
            //{
            //    Position.Y = 0;
            //    Velocity.Y = 0;
            //}

            int flipX = right ? -12 : 0;
            int flipX2 = right ? -6 : 0;


            const int fireOffsetX = 11;
            const int fireOffsetY = 23;
            const int fire2OffsetX = -3;
            const int fire2OffsetY = -2;

            fire1.Position = Position + new Vector2(fireOffsetX + flipX - exhaustOffset[fire1i], fireOffsetY);
            fire2.Position = Position + new Vector2(fireOffsetX + fire2OffsetX + flipX2 - exhaustOffset[fire2i], fireOffsetY + fire2OffsetY);

            // FuelColor
            if (fuel <= 0) fuelColor = Color.Red;
            else if (fuel < fuelMax / 10) fuelColor = Color.Orange;
            else if (fuel < fuelMax / 2) fuelColor = Color.Yellow;
            else fuelColor = Color.LightGreen;

            base.Update();
        }

        public override void Draw(SpriteBatch sb)
        {
            fire2.Draw(sb);
            fire1.Draw(sb);
            base.Draw(sb);
            foreach (var item in bombs)
                item.Draw(sb);
            //sb.Draw(Engine.Pixel, Position + new Vector2(16 + (right ? -12 : 0), 14), null, fuelColor, 0, Vector2.Zero, new Vector2(2, 4), SpriteEffects.None, 1);
        }

        public void Die(DeathReason reason)
        {
            if (!Alive) return;

            Alive = false;
            SourceRec = recDead;
            DeathReason = reason;
            Velocity = Vector2.Zero;
            if (Engine.Rnd.NextDouble() > 0.5f)
                Engine.PlaySound("die1");
            else if (Engine.Rnd.NextDouble() > 0.5f)
                Engine.PlaySound("die2");
            else
                Engine.PlaySound("die3");
            DeadTime = -40;

            Engine.lastGameLength = DateTime.Now - Engine.gameStartTime;
            Engine.subName = System.Environment.MachineName;
            Engine.subScore = Engine.Score.ToString();
            Engine.subTime = Engine.lastGameLength.Hours.ToString("00") + Engine.lastGameLength.Minutes.ToString("00") + Engine.lastGameLength.Seconds.ToString("00");

            artiHelper art = artiHelper.none;
            for (int i = 0; i < Engine.artifactsFound.Length; i++)
                if (Engine.artifactsFound[i])
                    art |= (artiHelper)(Math.Pow(2, i));

            Engine.subCustom = ((int)art).ToString();
            Engine.submitScore = true;
        }

        public void Start()
        {
            Alive = true;
            SourceRec = recAlive;
            Velocity = Vector2.Zero;
            Position = StartPos;
            fuel = fuelMax;
            Bombs = 3;
        }

        public void throwBomb()
        {
            for (int i = 0; i < bombs.Count; i++)
            {
                bombs[i].Update();
                if (bombs[i].Exploded)
                    bombs.RemoveAt(i);
            }

            if (Input.Bomb && Bombs > 0)
            {
                Bombs--;
                var bomb = new Bomb();
                bombs.Add(bomb);

                bomb.Position = Position + new Vector2(5, 16);
                if (Input.Right)
                    bomb.Velocity = new Vector2(1, -0.3f);
                else if (Input.Left)
                    bomb.Velocity = new Vector2(-1, -0.3f);
                else
                    bomb.Velocity = new Vector2(0, -0.6f);
            }
        }
    }

    public enum DeathReason { Fall, Spikes, Stalagtite }
}
