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
{
    enum BlockType { Block, SpikesFloor, SpikesCeeling, Stalagtite, Fuel, BombPack,
                     Artifact1, Artifact2, Artifact3, Artifact4, Artifact5, Artifact6, Artifact7, Artifact8, Artifact9, Artifact10, Artifact11 }
    class Block : Sprite
    {
        public BlockType Type;
        public Rectangle Rect;
        public int GridX;
        public bool Collides = true;
        public bool Falling = false;
        public int Dead = -1;
        public Row Parent;

        Rectangle l0, l1, l2;
        bool b1, b2;
        Vector2 v2;
        float r0, r1, r2;

        int artifactID = -1;

        Vector2 Velocity = Vector2.Zero;

        public Block(Row parent, BlockType type, int gridX)
        {
            Parent = parent;
            GridX = gridX;
            parent.Blocks[gridX] = this;
            Type = type;
            Position = new Vector2(gridX * Engine.TileSize, parent.PosY);

            if ((int)type >= (int)BlockType.Artifact1)
            {
                artifactID = (int)type - (int)BlockType.Artifact1;
                SourceRec = Engine.artifactRects[artifactID];
                Collides = false;
                Position.X += 8;
                Position.Y += 16;
            }

            switch (type)
            {
                case BlockType.Block:
                    SourceRec = new Rectangle(160, 0, Engine.TileSize, Engine.TileSize);
                    Tex = Engine.Tile1;

                    int tile = Engine.TileSize;

                    l0 = new Rectangle(Level.S.Rnd.Next(3) * tile, 96, tile, tile);
                    r0 = (float)(Level.S.Rnd.Next(3) * Math.PI);
                    if (Level.S.Rnd.NextDouble() > 0.7f)
                    {
                        b1 = true;
                        l1 = new Rectangle(Level.S.Rnd.Next(3) * tile, 64, tile, tile);
                        r1 = (float)(Level.S.Rnd.Next(3) * Math.PI);
                    }
                    else if (Level.S.Rnd.NextDouble() > 0.85f)
                    {
                        b2 = true;
                        l2 = new Rectangle(96 + Level.S.Rnd.Next(2) * 16, 64 + Level.S.Rnd.Next(2) * 16, 16, 16);
                        r2 = (float)(Level.S.Rnd.Next(3) * Math.PI);
                        v2 = new Vector2(8 + 3 + (float)Level.S.Rnd.NextDouble() * 12, 8 +3 + (float)Level.S.Rnd.NextDouble() * 12);
                    }

                    break;

                case BlockType.SpikesFloor:
                    Position.Y += 16;
                    SourceRec = new Rectangle(192, 16, Engine.TileSize, 16);
                    Collides = false;
                    break;

                case BlockType.SpikesCeeling:
                    if (Level.S.Rnd.NextDouble() > 0.5f)
                        SourceRec = new Rectangle(192, 0, 32, 16);
                    else
                        SourceRec = new Rectangle(128, 0, 32, 16);
                    Collides = false;
                    break;

                case BlockType.Stalagtite:
                    SourceRec = new Rectangle(160, Engine.TileSize, Engine.TileSize, Engine.TileSize);
                    Collides = false;
                    break;

                case BlockType.Fuel:
                    SourceRec = new Rectangle(224, 0, Engine.TileSize, Engine.TileSize);
                    Collides = false;
                    break;

                case BlockType.BombPack:
                    SourceRec = new Rectangle(224, 32, Engine.TileSize, Engine.TileSize);
                    Collides = false;
                    break;

                default:
                    break;
            }

            Rect = new Rectangle((int)Position.X, (int)Position.Y, SourceRec.Width, SourceRec.Height);

            switch (type)
            {
                case BlockType.SpikesFloor:
                    Rect.Height = 3;
                    Rect.Y += 13;
                    Rect.X += 4;
                    Rect.Width -= 8;
                    break;

                case BlockType.SpikesCeeling:
                    Rect.Height = 3;
                    Rect.X += 4;
                    Rect.Width -= 8;
                    break;
            }
        }

        public override void Update()
        {
            if (Dead > 0)
            {
                Dead--;
                if (Dead == 0)
                {
                    Parent.Blocks[GridX] = null;
                    return;
                }
            }
            if (Falling)
            {
                if (Velocity.Y == 0) // One time thingy
                {
                    SourceRec = new Rectangle(160, 0, Engine.TileSize, Engine.TileSize);
                    if (Parent.Above != null)
                        Parent.Above.CalcTiles();
                    Parent.CalcTiles();
                    if (Parent.Below != null)
                        Parent.Below.CalcTiles();
                    Parent.ExtraBlocks.Add(this);
                    new Block(Parent, BlockType.SpikesCeeling, GridX) 
                    { 
                        SourceRec = new Rectangle(128, 0, 32, 16)
                    };
                }


                Velocity.Y = Math.Min(Player.maxDownItem, Velocity.Y + Player.gravityItems);
                Position.Y += Velocity.Y;

                if (Position.Y > Engine.CamPos.Y + 200)
                {
                    Die();
                    return;
                }

                int yindex = (int)((Position.Y - Level.S.Rows[0].PosY) / Engine.TileSize) + 1;
                if (yindex >= Level.S.Rows.Count)
                {
                    Die();
                    return;
                }
                var block = Level.S.Rows[yindex].Blocks[GridX];
                if (block != null && block.Type == BlockType.Block)
                {
                    Die();
                    return;
                }

                var dist = Vector2.DistanceSquared(Player.S.Position, Position);
                //var xDist = Math.Abs(Position.X + Rect.Width / 2 - Player.S.Position.X + Player.S.Size.X / 2);

                if (dist < Player.ItemDistSq)
                {
                    Die();
                    if (Velocity.Y > 1.5f && Player.S.Position.Y + 8 > Position.Y)
                        Player.S.Die(DeathReason.Stalagtite);
                    return;
                }

                Ward.SetLine("fall", Player.S.Position, Position, Color.Red);
                Ward.Watch("Fall dist", Vector2.Distance(Player.S.Position, Position));
            }

            if (Type == BlockType.Fuel || artifactID >= 0 || Type == BlockType.BombPack)
            {
                var dist = Vector2.DistanceSquared(Player.S.Position, Position);
                if (dist < Player.ItemDistSq)
                    Die();
            }
        }

        Vector2 varOrigin = new Vector2(16, 16);

        public override void Draw(SpriteBatch sb)
        {
            base.Draw(sb);
            //if (Input.KeyDown(Microsoft.Xna.Framework.Input.Keys.Z))
            //    sb.Draw(Engine.Pixel, Rect, new Color(100, 0, 0, 100));
            sb.Draw(Engine.Spritesheet, Position + varOrigin, l0, Color, r0, varOrigin, 1, SpriteEffects.None, 1);
            if (b1)
                sb.Draw(Engine.Spritesheet, Position + varOrigin, l1, Color, r1, varOrigin, 1, SpriteEffects.None, 1);
            if (b2)
                sb.Draw(Engine.Spritesheet, Position + v2, l2, Color, r2, new Vector2(8, 8), 1, SpriteEffects.None, 1);
        }

        public void Die()
        {
            if (Dead > 0) 
                return;

            Falling = false;
            if (Type == BlockType.Stalagtite)
            {
                SourceRec = new Rectangle(192, Engine.TileSize, Engine.TileSize, Engine.TileSize);
                Dead = 5;
                Engine.PlaySound("stalagtite");
            }
            else if (Type == BlockType.Fuel)
            {
                Dead = 1;
                Engine.PlaySound("fuel");
                Player.S.fuel = Math.Min(Player.fuelMax, Player.S.fuel + Player.fuelRefill);
            }
            else if (artifactID >= 0)
            {
                Dead = 3;
                Engine.UnlockArtifact(artifactID);
            }
            else if (Type == BlockType.BombPack)
            {
                Dead = 3;
                Player.S.Bombs += 3;
                Engine.PlaySound("bombpickup");
            }
        }

        #region Set Tile ID

        const bool borderOutofBounds = false;

        public Rectangle SetTileID(int gridSize)
        {
            return SetTileID(new Point2(gridSize, gridSize));
        }

        public Rectangle SetTileID(Point2 gridSize)
        {
            Point2 id;
            Direction borders = Direction.None;
            Direction connected = Direction.None;
            getBorders(ref borders, ref connected);

            // 4 walls
            if (borders.HasFlag(Direction.North | Direction.South | Direction.West | Direction.East)) id = new Point2(2, 1);
            // 0 walls
            else if (connected.HasFlag(Direction.North | Direction.South | Direction.West | Direction.East))
            {
                if (borders.HasFlag(Direction.NorthWest))
                    id = new Point2(3, 3);
                else if (borders.HasFlag(Direction.NorthEast))
                    id = new Point2(2, 3);
                else if (borders.HasFlag(Direction.SouthWest))
                    id = new Point2(3, 2);
                else if (borders.HasFlag(Direction.SouthEast))
                    id = new Point2(2, 2);
                else
                    id = new Point2(3, 1); // no corners
            }

            // three walls
            else if (borders.HasFlag(Direction.North | Direction.South | Direction.East) && connected.HasFlag(Direction.West)) id = new Point2(4, 0);
            else if (borders.HasFlag(Direction.North | Direction.South | Direction.West) && connected.HasFlag(Direction.East)) id = new Point2(2, 0);
            else if (borders.HasFlag(Direction.North | Direction.East | Direction.West) && connected.HasFlag(Direction.South)) id = new Point2(4, 1);
            else if (borders.HasFlag(Direction.South | Direction.East | Direction.West) && connected.HasFlag(Direction.North)) id = new Point2(4, 3);

            // two coridor walls
            else if (borders.HasFlag(Direction.South | Direction.North) && connected.HasFlag(Direction.East | Direction.West)) id = new Point2(3, 0);
            else if (borders.HasFlag(Direction.East | Direction.West) && connected.HasFlag(Direction.South | Direction.North)) id = new Point2(4, 2);

            // One wall
            else if (connected.HasFlag(Direction.North | Direction.South | Direction.East) && borders.HasFlag(Direction.West))
            {
                if (!connected.HasFlag(Direction.NorthEast) && !connected.HasFlag(Direction.SouthEast)) id = new Point2(8, 3);
                else if (!connected.HasFlag(Direction.SouthEast)) id = new Point2(6, 2);
                else if (!connected.HasFlag(Direction.NorthEast)) id = new Point2(6, 3);
                else id = new Point2(5, 3); // no corner
            }
            else if (connected.HasFlag(Direction.North | Direction.South | Direction.West) && borders.HasFlag(Direction.East))
            {
                if (!connected.HasFlag(Direction.NorthWest) && !connected.HasFlag(Direction.SouthWest)) id = new Point2(8, 2);
                else if (!connected.HasFlag(Direction.SouthWest)) id = new Point2(7, 2);
                else if (!connected.HasFlag(Direction.NorthWest)) id = new Point2(7, 3);
                else id = new Point2(5, 2);
            }
            else if (connected.HasFlag(Direction.North | Direction.East | Direction.West) && borders.HasFlag(Direction.South))
            {
                if (!connected.HasFlag(Direction.NorthEast) && !connected.HasFlag(Direction.NorthWest)) id = new Point2(8, 1);
                else if (!connected.HasFlag(Direction.NorthWest)) id = new Point2(7, 1);
                else if (!connected.HasFlag(Direction.NorthEast)) id = new Point2(6, 1);
                else id = new Point2(5, 1);
            }
            else if (connected.HasFlag(Direction.East | Direction.South | Direction.West) && borders.HasFlag(Direction.North))
            {
                if (!connected.HasFlag(Direction.SouthEast) && !connected.HasFlag(Direction.SouthWest)) id = new Point2(8, 0);
                else if (!connected.HasFlag(Direction.SouthWest)) id = new Point2(7, 0);
                else if (!connected.HasFlag(Direction.SouthEast)) id = new Point2(6, 0);
                else id = new Point2(5, 0);
            }

            // 2 corner walls
            else if (borders.HasFlag(Direction.North | Direction.West))
            {
                if (connected.HasFlag(Direction.SouthEast))
                    id = new Point2(0, 0);
                else
                    id = new Point2(0, 2);
            }
            else if (borders.HasFlag(Direction.North | Direction.East))
            {
                if (connected.HasFlag(Direction.SouthWest))
                    id = new Point2(1, 0);
                else
                    id = new Point2(1, 2);
            }
            else if (borders.HasFlag(Direction.South | Direction.West))
            {
                if (connected.HasFlag(Direction.NorthEast))
                    id = new Point2(0, 1);
                else
                    id = new Point2(0, 3);
            }
            else if (borders.HasFlag(Direction.South | Direction.East))
            {
                if (connected.HasFlag(Direction.NorthWest))
                    id = new Point2(1, 1);
                else
                    id = new Point2(1, 3);
            }
            else
                id = new Point2(8, 0);

            return new Rectangle(id.X * gridSize.X, id.Y * gridSize.Y, gridSize.X, gridSize.Y);
        }

        private void getBorders(ref Direction borders, ref Direction connected)
        {
            var a = Parent.Above != null ? Parent.Above.Blocks : null;
            var me = Parent.Blocks;
            var b = Parent.Below.Blocks;

            setBorder(ref borders, ref connected, Direction.NorthWest, a, GridX - 1);
            setBorder(ref borders, ref connected, Direction.North, a, GridX);
            setBorder(ref borders, ref connected, Direction.NorthEast, a, GridX + 1);

            setBorder(ref borders, ref connected, Direction.West, me, GridX - 1);
            setBorder(ref borders, ref connected, Direction.East, me, GridX + 1);

            setBorder(ref borders, ref connected, Direction.SouthWest, b, GridX - 1);
            setBorder(ref borders, ref connected, Direction.South, b, GridX);
            setBorder(ref borders, ref connected, Direction.SouthEast, b, GridX + 1);
        }

        private void setBorder(ref Direction borders, ref Direction connected, Direction relationship, Block[] col, int id)
        {
            if (col == null || id < 0 || id >= col.Length)
            {
                if (borderOutofBounds)
                    borders |= relationship;
                else
                    connected |= relationship;
                return;
            }

            var b = col[id];
            if (b == null || b.Type != BlockType.Block ) //|| b.Falling || (b.Type != BlockType.Block && (relationship == Direction.East || relationship == Direction.West))) //  
                borders |= relationship;
            else
                connected |= relationship;
        }

        #endregion
    }
}
