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;

using Microsoft.Devices.Sensors;

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

        int displayWidth;
        int displayHeight;

        // Ser 
        Texture2D cheeseTexture;
        Rectangle cheeseRectangle;
        int cheeseWidth;
        float cheeseWidthFactor = 8;

        float cheeseX;
        float cheeseY;

        // Chleb
        Texture2D breadTexture;
        Rectangle breadRectangle;
        int breadWidth;
        float breadWidthFactor = 3;

        // Tła i komunikaty
        Texture2D wonTexture;
        Texture2D lostTexture;
        Texture2D backgroundTexture;
        Rectangle fullScreenDrawPos;

        // Wartości przyspieszenia

        Accelerometer accel;
        Vector3 accelReading;
        float acceleratorPower = 8;

        enum gameState
        {
            playing,
            won,
            lost
        }

        private gameState state = gameState.playing;

        SoundEffect wonSound;
        SoundEffect lostSound;

        Random rand = new Random();

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

            graphics.PreferredBackBufferWidth = 240;
            graphics.PreferredBackBufferHeight = 400;

            // 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()
        {
            accel = new Accelerometer();

            accel.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accel_ReadingChanged);

            accel.Start();

            base.Initialize();
        }

        void accel_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
        {
            accelReading.X = (float)e.X;
            accelReading.Y = (float)e.Y;
            accelReading.Z = (float)e.Z;
        }

        /// <summary>
        /// LoadContent bedzie wywołany raz na grę i tu należy ładować
        /// całą zawartość.
        /// </summary>
        protected override void LoadContent()
        {
            displayWidth = GraphicsDevice.Viewport.Width;
            displayHeight = GraphicsDevice.Viewport.Height;

            cheeseWidth = (int)(displayWidth / cheeseWidthFactor);
            breadWidth = (int)(displayWidth / breadWidthFactor);

            // Stwórz prostokąt do rysowania 
            // Rozmiar sera powinien wynosić 1/12 szerokosci ekranu
            // Stwórz nowy SpriteBatch, którego można uzyć do rysowania tekstur.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Wczytaj obraz sera i ustaw rozmiar prostokąta wyświetlania
            cheeseTexture = Content.Load<Texture2D>("cheese");
            cheeseRectangle.Width = cheeseWidth;
            float aspectRatio = (float)cheeseTexture.Width / cheeseTexture.Height;
            cheeseRectangle.Height = (int)((cheeseWidth / aspectRatio) + 0.5f);

            // Wczytaj obraz chleba i ustaw rozmiar prostokąta wyświetlania
            breadTexture = Content.Load<Texture2D>("bread");
            breadRectangle.Width = breadWidth;
            aspectRatio = (float)breadTexture.Width / breadTexture.Height;
            breadRectangle.Height = (int)((breadWidth / aspectRatio) + 0.5f);

            // Wczytaj obraz tła
            backgroundTexture = Content.Load<Texture2D>("background");

            // Wczytaj tekstury dla zwycięstwa i przegranej
            wonTexture = Content.Load<Texture2D>("wonScreen");
            lostTexture = Content.Load<Texture2D>("lostScreen");

            fullScreenDrawPos = new Rectangle(
                0, 0,
                displayWidth, displayHeight);

            // Wczytaj efekty dźwiękowe
            wonSound = Content.Load<SoundEffect>("wonSound");
            lostSound = Content.Load<SoundEffect>("lostSound");

            newGame();

        }

        /// <summary>
        /// UnloadContent będzie 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
        }

        private void updateGame()
        {
            // Aktualizuj składowe X i Y położenia
            cheeseX = cheeseX + (accelReading.X * acceleratorPower);
            cheeseY = cheeseY - (accelReading.Y * acceleratorPower);

            // Ustaw jako współrzędne prostokąta dla wyświetlania sera wartości cheeseX oraz cheeseY
            cheeseRectangle.X = (int)(cheeseX + 0.5f);
            cheeseRectangle.Y = (int)(cheeseY + 0.5f);

            // Ograniczenia na bokach

            if (cheeseX < 0)
            {
                cheeseX = 0; ;
            }

            if (cheeseY < 0)
            {
                cheeseY = 0;
            }

            if (cheeseRectangle.Right > displayWidth)
            {
                cheeseX = displayWidth - cheeseWidth;
            }

            if (cheeseRectangle.Bottom > GraphicsDevice.Viewport.Height)
            {
                cheeseY = displayHeight - cheeseRectangle.Width;
            }

            if (cheeseRectangle.Intersects(breadRectangle))
            {
                // Możliwa wygrana 
                if (cheeseRectangle.Top < breadRectangle.Top &&
                    cheeseRectangle.Left >= breadRectangle.Left &&
                    cheeseRectangle.Right <= breadRectangle.Right)
                {
                    // prawidłowe lądowanie - gracz wygrał
                    gameWon();
                }
                else
                {
                    // nieprawidłowe lądowanie - gracz przegrał
                    gameLost();
                }
            }
        }

        private void gameWon()
        {
            wonSound.Play();
            state = gameState.won;
        }

        private void gameLost()
        {
            lostSound.Play();
            state = gameState.lost;
        }

        private void newGame()
        {
            // Umieść ser w losowym miejscu na górze ekranu
            cheeseX = rand.Next(displayWidth - cheeseWidth);
            cheeseY = 0;

            // Przemieść chleb poziomo w nowe miejsce
            breadRectangle.X = rand.Next(displayWidth - breadWidth);
            breadRectangle.Y = displayHeight - breadRectangle.Height;

            // Rozpocznij rozgrywkę
            state = gameState.playing;
        }

        private void checkNewGame()
        {
            // Odczytaj ekran dotykowy

            TouchCollection touches = TouchPanel.GetState();

            foreach (TouchLocation location in touches)
            {
                if (location.State == TouchLocationState.Pressed)
                {
                    newGame();
                    break;
                }
            }
        }


        /// <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();

            switch (state)
            {
                case gameState.playing:
                    updateGame();
                    break;
                case gameState.lost:
                    checkNewGame();
                    break;
                case gameState.won:
                    checkNewGame();
                    break;
            }

            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();

            switch (state)
            {
                case gameState.playing:
                    spriteBatch.Draw(backgroundTexture, fullScreenDrawPos, Color.White);
                    spriteBatch.Draw(breadTexture, breadRectangle, Color.White);
                    spriteBatch.Draw(cheeseTexture, cheeseRectangle, Color.White);
                    break;

                case gameState.won:
                    spriteBatch.Draw(wonTexture, fullScreenDrawPos, Color.White);
                    break;

                case gameState.lost:
                    spriteBatch.Draw(lostTexture, fullScreenDrawPos, Color.White);
                    break;
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
