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 Row
    {
        const int RowWidth = 16;
        const int size = Engine.TileSize;

        Rectangle block = new Rectangle(160, 0, size, size);
        Rectangle spikes = new Rectangle(128, Engine.TileSize, size, size);

        public Block[] Blocks = new Block[RowWidth];
        public bool[] Greens = new bool[RowWidth];
        public List<Block> ExtraBlocks = new List<Block>();

        //int[,] grid;

        public int PosY;
        public int RowNum;
        public Level Level;
        public Row Above;
        public Row Below;

        public static int CurrentRowNum = 0;

        public Row(Level level)
        {
            RowNum = ++CurrentRowNum;
            if (level.Rows.Count > 0)
            {
                Above = level.Rows[level.Rows.Count-1];
                Above.Below = this;
                if (Above.Above != null && Above.Above.Above != null)
                    Above.Above.Above.CalcTiles();
            }
            Level = level;
            level.Rows.Add(this);
            PosY = RowNum * size;
        }

        List<int> open = new List<int>();
        public void Initialize()
        {
            if (Above != null)
            {
                #region Make sure we have a connected opening to a green above

                open.Clear();
                bool found = false;
                for (int i = 0; i < Above.Blocks.Length; i++)
                {
                    if (Above.Greens[i])
                    {
                        open.Add(i);
                        if (Blocks[i] == null)
                        {
                            Greens[i] = true;
                            found = true;
                        }
                    }
                }
                if (!found) // No matching green openings, create one at random
                {
                    int rndIndex = open[Level.Rnd.Next(open.Count)];
                    Blocks[rndIndex] = null;
                    Greens[rndIndex] = true;
                }

                #endregion

                #region Spread green

                for (int i = 1; i < Blocks.Length; i++)
                    if (Greens[i - 1] && Blocks[i] == null)
                        Greens[i] = true;
                for (int i = Blocks.Length-1 - 1; i >= 0; i--)
                {
                    if (Greens[i + 1] && Blocks[i] == null)
                        Greens[i] = true;
                }

                #endregion

                Above.InitializePost();
            }


            #region Color Variations on blocks

            for (int i = 0; i < Blocks.Length; i++)
            {
                var block = Blocks[i];
                if (block != null && block.Type == BlockType.Block)
                {
                    const float scale = 0.12f;
                    const float scaleBlack = 0.2f;
                    const float blackCutoff = 0.3f;
                    const float blackDecrease = 0.0f;

                    var r = Level.S.Perlin.Noise(1394 + i * 0.9129394292f * scale,
                                                 3848 + RowNum * 0.9738394939f * scale);
                    var g = Level.S.Perlin.Noise(-1394 + i * 0.9129394292f * scale,
                                                 3848 + RowNum * 0.9738394939f * scale);
                    var b = Level.S.Perlin.Noise(9394 + i * 0.9129394292f * scale,
                                                 -3848 + RowNum * 0.9738394939f * scale);
                    var s = Level.S.Perlin.Noise(-9394 + i * 0.9129394292f * scaleBlack,
                                                 -3848 + RowNum * 0.9738394939f * scaleBlack);

                    // Normalize values between 0-0.5f;
                    r = (r + 1) / 4;
                    g = (g + 1) / 4;
                    b = (b + 1) / 4;
                    s = (s + 1) / 4;
                    var black = s > blackCutoff ? blackDecrease : 0;
                    block.Color = new Color(0.5f + r - black,
                                            0.5f + g - black,
                                            0.5f + b - black);
                }
            }

            #endregion
        }

        public void InitializePost()
        {
            #region Avoid single blocks

            // Doesnt work stops green
            //if (Above != null && Below != null)
            //    for (int i = 0; i < Blocks.Length; i++)
            //    {
            //        var me = Blocks[i];
            //        if (me == null &&)
            //        {
            //            var a = Above.Blocks[i];
            //            var b = Below.Blocks[i];
            //            if (a == null && b == null)
            //                Below.Blocks[i] = new Block(Below, BlockType.Block, i);
            //        }
            //    }

            #endregion

            #region Change some blocks to traps & artifacts

            for (int i = 0; i < Blocks.Length; i++)
            {
                if (RowNum >= Level.startingRowTraps)
                {
                    var me = Blocks[i];
                    if (me == null && Below != null)
                    {
                        var b = Below.Blocks[i];
                        if (b != null)
                        {
                            if (Level.S.artifactsSpawned < Engine.artifactDepths.Length && RowNum > Engine.artifactDepths[Level.artifactsSpawned])
                            {
                                Blocks[i] = new Block(this, (BlockType)((int)BlockType.Artifact1 + Level.artifactsSpawned), i);
                                Level.artifactsSpawned++;
                            }
                            if (Level.Rnd.NextDouble() > Level.SpawnFuel(RowNum))
                                Blocks[i] = new Block(this, BlockType.Fuel, i);
                        }
                    }
                    if (me != null && Above != null)
                    {
                        var a = Above.Blocks[i];
                        var b = Below.Blocks[i];

                        if (a == null && b != null)
                        {
                            if (Level.S.Rnd.NextDouble() > Level.BombChance(RowNum))
                                Blocks[i] = new Block(this, BlockType.BombPack, i);
                            else if (Level.S.Rnd.NextDouble() > Level.SpikeFloorChance(RowNum))
                                Blocks[i] = new Block(this, BlockType.SpikesFloor, i);
                        }
                        if (a != null && a.Type == BlockType.Block && b == null)
                        {
                            if (Level.S.Rnd.NextDouble() > Level.SpikeCeelingChance(RowNum))
                                Blocks[i] = new Block(this, BlockType.Stalagtite, i);
                            else if (Level.S.Rnd.NextDouble() > Level.StalagniteChance(RowNum))
                                Blocks[i] = new Block(this, BlockType.SpikesCeeling, i);
                        }
                    }
                }
            }

            #endregion
        }

        public void CalcTiles()
        {
            if (Below == null) return;
            // Calc Tiles
            for (int i = 0; i < Blocks.Length; i++)
            {
                var me = Blocks[i];
                if (me != null && me.Type == BlockType.Block)
                    me.SourceRec = me.SetTileID(Engine.TileSize);
            }
        }

        public void Update()
        {
            if (Player.S.Position.Y > PosY && Player.S.Position.Y - 250 < PosY)
            {
                int pindex = (int)MathHelper.Clamp((int)((Player.S.Position.X + 16) / Engine.TileSize), 0, Blocks.Length - 1);
                var pb = Blocks[pindex];
                if (pb != null && pb.Type == BlockType.Stalagtite && !pb.Falling)
                {
                    pb.Falling = true;
                    Engine.PlaySound("stalagtitedrop");
                }
            }
            foreach (var item in Blocks)
                if (item != null)
                    item.Update();
            for (int i = 0; i < ExtraBlocks.Count; i++)
            {
                ExtraBlocks[i].Update();
                if (ExtraBlocks[i].Dead == 0)
                    ExtraBlocks.RemoveAt(i);
            }
        }

        public void Draw(SpriteBatch sb)
        {
            foreach (var item in ExtraBlocks)
                item.Draw(sb);
            for (int i = 0; i < Blocks.Length; i++)
			{
                if (Blocks[i] != null)
                    Blocks[i].Draw(sb);
                //else if (Greens[i])
                //    sb.Draw(Engine.Pixel, new Rectangle(i * size, PosY, size, size), new Color(0, 100, 0, 100));
			}
        }

        public void RandomBlocks()
        {
            for (int i = 0; i < RowWidth; i++)
            {
                float scale = 0.5f;
                float x = i * 0.4394934f * scale;
                float y = RowNum * 0.634897334f * scale;

                var f = Level.S.Perlin.Noise(x, y);

                if (f > Level.S.PerlinCutoff(RowNum))
                    new Block(this, BlockType.Block, i);

                //if (Level.S.Rnd.NextDouble() > 0.5f)
                //{
                //    if (Level.S.Rnd.NextDouble() > 0.9f)
                //        new Block(this, BlockType.Spikes, i);
                //    else
                //        new Block(this, BlockType.Block, i);
                //}
            }
        }

        public bool Collide(Player player, ref Vector2 velocity)
        {
            Vector2 localPos = player.Position - new Vector2(0, PosY);
            AABB source = new AABB(player.Position, player.Size);
            AABB dest = new AABB(source.X + velocity.X, source.Y + velocity.Y, source.Width, source.Height);
            bool collision = false;

            foreach (var item in Blocks)
            {
                if (item == null) continue;

                var target = item.Rect;
                if (dest.Intersects(target))
                {
                    collision = true;

                    float top = dest.Bottom - target.Top;
                    float bot = target.Bottom - dest.Top;
                    float right = target.Right - dest.Left;
                    float left = dest.Right - target.Left;

                    if (top < bot && top < left && top < right) // Move up
                    {
                        if (item.Type == BlockType.Block && Player.S.Position.Y - Player.S.fallStart  >= Player.fallDeath)
                            player.Die(DeathReason.Fall);

                        if (item.Collides)
                            velocity.Y = target.Top - source.Bottom;
                        
                        player.GroundSafe = 0;

                        if (item.Type == BlockType.SpikesFloor && velocity.Y > 0.3f)
                            player.Die(DeathReason.Spikes);
                    }
                    else if (right < bot && right < left) // Move right
                    {
                        if (item.Collides)
                            velocity.X = target.Right - source.Left;
                    }
                    else if (left < bot) // move left
                    {
                        if (item.Collides)
                            velocity.X = target.Left - source.Right;
                    }
                    else // move down
                    {
                        if (item.Collides)
                            velocity.Y = target.Bottom - source.Y;

                        if (item.Type == BlockType.SpikesCeeling && velocity.Y < -0.3f)
                            player.Die(DeathReason.Spikes);
                    }
                }
            }
            return collision;
        }

        public override string ToString()
        {
            return string.Concat("Row: " + RowNum);
        }
    }
}
