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.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace ShuffleBoard
{
    public class Region
    {
        public Texture2D Texture;
        public Rectangle DrawPosition;
        public Color TextureColor;

        public SpriteFont Font;
        public string Message;

        public int Score;

        public Region(Texture2D inTexture,
            Rectangle inDRawPosition,
            Color inTextureColor,
            string inMessage,
            SpriteFont inFont,
            int inScore)
        {
            Texture = inTexture;
            TextureColor = inTextureColor;
            DrawPosition = inDRawPosition;
            Message = inMessage;
            Font = inFont;
            Score = inScore;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Texture, DrawPosition, TextureColor);

            Vector2 textSize = Font.MeasureString(Message);
            Vector2 textPos = new Vector2(
                DrawPosition.X + ((DrawPosition.Width - textSize.X) / 2),
                DrawPosition.Y + ((DrawPosition.Height - textSize.Y) / 2)
                );

            spriteBatch.DrawString(Font, Message, textPos, Color.White);
        }

    }

    public enum PuckState
    {
        playerDragging,
        moving,
        stopped
    }

    public class PuckSprite
    {
        public Texture2D PuckTexture;
        public Rectangle PuckRectangle;
        public Vector2 PuckPosition;
        public Vector2 PuckVelocity;
        public static float Friction = 0.9f;
        public float PuckRadius;

        private PuckState state = PuckState.stopped;

        public PuckState State
        {
            get
            {
                return state;
            }
        }

        TouchLocation lastTouch;

        ShuffleBoardGame game;

        public PuckSprite(Texture2D inPadTexture, Rectangle inPadRectangle, Vector2 inPosition, ShuffleBoardGame inGame)
        {
            PuckTexture = inPadTexture;
            PuckRectangle = inPadRectangle;
            PuckPosition = inPosition;
            PuckRadius = PuckRectangle.Width / 2.0f;
            game = inGame;
        }

        private void updateWhenStopped()
        {
            foreach (TouchLocation touch in game.Touches)
            {
                if (touch.State == TouchLocationState.Pressed)
                {
                    Vector2 vectorToPuck = PuckPosition - touch.Position;
                    if (vectorToPuck.Length() < PuckRadius)
                    {
                        lastTouch = touch;
                        PuckPosition = touch.Position;
                        state = PuckState.playerDragging;
                    }
                }
            }
        }

        private void updateWhenDragging()
        {
            foreach (TouchLocation touch in game.Touches)
            {
                if (touch.Id == lastTouch.Id)
                {
                    if (touch.State == TouchLocationState.Moved)
                    {
                        PuckVelocity = touch.Position - lastTouch.Position;
                    }

                    lastTouch = touch;
                    PuckPosition = touch.Position;

                    if (touch.State == TouchLocationState.Released)
                    {
                        state = PuckState.moving;
                    }
                }
            }
        }

        private void updateWhenMoving()
        {
            // Jeśli nie nastąpiło dotknięcie, odśwież zgodnie z prawami fizyki
            PuckPosition += PuckVelocity;

            if (PuckRectangle.Right > game.DisplayWidth)
            {
                PuckVelocity.X = -(float)Math.Abs(PuckVelocity.X);
            }

            if (PuckRectangle.Left < 0)
            {
                PuckVelocity.X = (float)Math.Abs(PuckVelocity.X);
            }

            if (PuckRectangle.Bottom > game.DisplayHeight)
            {
                PuckVelocity.Y = -(float)Math.Abs(PuckVelocity.Y);
            }

            if (PuckRectangle.Top < 0)
            {
                PuckVelocity.Y = (float)Math.Abs(PuckVelocity.Y);
            }

            PuckVelocity = PuckVelocity * Friction;
            if (PuckVelocity.Length() < 0.05f)
            {
                PuckVelocity.X = 0;
                PuckVelocity.Y = 0;
                game.PuckStopped();
                state = PuckState.stopped;
            }
        }

        public void Update()
        {
            switch (state)
            {
                case PuckState.stopped:
                    updateWhenStopped();
                    break;
                case PuckState.playerDragging:
                    updateWhenDragging();
                    break;
                case PuckState.moving:
                    updateWhenMoving();
                    break;
            }

            // Umieść prostokąt rysowania wokół położenia środka

            PuckRectangle.X = (int)(PuckPosition.X - PuckRadius + 0.5f);
            PuckRectangle.Y = (int)(PuckPosition.Y - PuckRadius + 0.5f);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(PuckTexture, PuckRectangle, Color.White);
        }
    }

    /// <summary>
    /// To jest główny typ dla naszej gry
    /// </summary>
    public class ShuffleBoardGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // Świat gry

        SpriteFont font;
        Vector2 scorePos;

        // Parametry ekranu
        public int DisplayWidth;
        public int DisplayHeight;

        // Informacje o dotkniętym miejscu, do uzycia przez krążek
        public TouchCollection Touches;

        // Obszary pola gry
        List<Region> regions = new List<Region>();

        Region startRegion;

        PuckSprite playerSprite;

        // Początkowe położenie krążka
        Vector2 puckStart;

        int GameScore;

        public ShuffleBoardGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferWidth = 480;
            graphics.PreferredBackBufferHeight = 800;

            // Domyślna częstotliwość odświeżania dla Windows Phone wynosi 30 fps.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
        }

        /// <summary>
        /// Pozwala grze wykonać dowolną inicjalizację jakiej potrzebuje przed rozpoczeciem działania.
        /// To tutaj może ona zażądać potrzebnych usług i załadować związaną zawartość nie graficzną.
        /// Wywołanie base.Initialize wyliczy wszystkie komponenty i także je zainicjuje.
        /// </summary>
        protected override void Initialize()
        {
            DisplayWidth = GraphicsDevice.Viewport.Width;
            DisplayHeight = GraphicsDevice.Viewport.Height;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent bedzie wywołany raz na grę i tu należy ładować
        /// całą zawartość.
        /// </summary>
        protected override void LoadContent()
        {
            // Stwórz nowy SpriteBatch, którego można uzyć do rysowania tekstur.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Texture2D regionTexture = Content.Load<Texture2D>("Images/GreyRegion");

            font = Content.Load<SpriteFont>("RegionFont");
            scorePos = new Vector2(0, DisplayHeight - font.LineSpacing);

            int regionHeight = DisplayHeight / 8;

            regions.Add(new Region(
                regionTexture,
                new Rectangle(0, 0, DisplayWidth, regionHeight),
                Color.Red,
                "Przegrana",
                font,
                -1)
                );

            regions.Add(new Region(
                regionTexture,
                new Rectangle(0, regionHeight, DisplayWidth, regionHeight),
                Color.Green,
                "100",
                font,
                100)
                );

            regions.Add(new Region(
                regionTexture,
                new Rectangle(0, 2 * regionHeight, DisplayWidth, regionHeight),
                Color.Yellow,
                "50",
                font,
                50)
                );

            regions.Add(new Region(
                regionTexture,
                new Rectangle(0, 3 * regionHeight, DisplayWidth, regionHeight),
                Color.Orange,
                "20",
                font,
                20)
                );

            regions.Add(new Region(
                regionTexture,
                new Rectangle(0, 4 * regionHeight, DisplayWidth, regionHeight),
                Color.Purple,
                "10",
                font,
                10)
                );

            startRegion = new Region(
                regionTexture,
                new Rectangle(0, 5 * regionHeight, DisplayWidth, 3 * regionHeight),
                Color.White,
                "START",
                font,
                0);

            regions.Add(startRegion);

            Texture2D puckTexture = Content.Load<Texture2D>("Images/YellowPuck");

            Rectangle puckRectangle = new Rectangle(
                0, 0,
                DisplayWidth / 8, DisplayWidth / 8);

            puckStart = new Vector2(DisplayWidth / 2, 7 * regionHeight + regionHeight / 2);

            playerSprite = new PuckSprite(puckTexture, puckRectangle, puckStart, this);

        }

        public void PuckStopped()
        {
            foreach (Region r in regions)
            {
                if (r.DrawPosition.Contains(
                    (int)playerSprite.PuckPosition.X,
                    (int)playerSprite.PuckPosition.Y))
                {
                    // To jest obszar zawierający krążek
                    if (r.Score < 0)
                    {
                        GameScore = 0;
                    }
                    else
                    {
                        GameScore += r.Score;
                    }
                }
            }
        }

        /// <summary>
        /// UnloadContent bedzie wywołany raz na grę i tu należy usuwać 
        /// całą zawartość.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Usunąć w tym miejscu każdą zawartość nie obsługiwaną przez ContentManager
        }

        /// <summary>
        /// Pozwala uruchomić w grze logikę, taką jak aktualizacja świata,
        /// sprawdzenie kolizji, pobranie wejścia i odtwarzanie dźwięku.
        /// </summary>
        /// <param name="gameTime">Dostarcza chwilową wartość zmiennych czasowych.</param>
        protected override void Update(GameTime gameTime)
        {
            // Pozwala opuscić grę
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Odświerz wartość zmiennej Touches do użytku sprajtów
            Touches = TouchPanel.GetState();

            if (playerSprite.State == PuckState.stopped)
            {
                // Jeśli krążek jest nieruchomy, sprawdź czy trzeba uruchomić ponownie grę
                if (playerSprite.PuckRectangle.Top < startRegion.DrawPosition.Top)
                {
                    // Krążek nie jest w obszarze startowym
                    // Sprawdź czy uruczamiamy grę ponownie
                    if (Touches.Count > 0)
                    {
                        if (Touches[0].State == TouchLocationState.Pressed)
                        {
                            if (startRegion.DrawPosition.Contains(
                                (int)Touches[0].Position.X,
                                (int)Touches[0].Position.Y))
                            {
                                playerSprite.PuckPosition = puckStart;
                            }
                        }
                    }
                }
            }

            playerSprite.Update();

            base.Update(gameTime);
        }

        /// <summary>
        /// Metoda wywoływana kiedy gra powinna się wyswietlić.
        /// </summary>
        /// <param name="gameTime">Dostarcza chwilową wartość zmiennych czasowych.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            foreach (Region r in regions)
            {
                r.Draw(spriteBatch);
            }

            playerSprite.Draw(spriteBatch);

            spriteBatch.DrawString(font, GameScore.ToString(), scorePos, Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
