using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Bouncer
{
    public partial class Form1 : Form
    {
        #region Старая версия
        // <summary>
        /// Изображение, содержащее сыр
        /// </summary>
        //private Image cheeseImage = null;

        /// <summary>
        /// Изображение, содержащее батон хлеба
        /// </summary>
        //private Image breadImage = null;

        /// <summary>
        /// Изображение, содержащее помидор
        /// </summary>
        //private Image tomatoImage = null;

        /// <summary>
        /// Изображение, содержащее ветчину
        /// </summary>
        //private Image bonusHamImage = null;

        /// <summary>
        /// Изображение, содержащее фон игры.
        /// </summary>
        //private Image backgroundImage = null;


        /// <summary>
        ///  Координата X для рисования сыра
        /// </summary>
        // private int cx = 0;   // старая версия

        /// <summary>
        /// Координата Y для рисования сыра
        /// </summary>
        // private int cy = 0;   // старая версия

        /// <summary>
        /// Позиции всех помидоров на экране
        /// </summary>
        //private tomato[] tomatoes;

        /// <summary>
        /// True, если ветчина на экране
        /// </summary>
        //private bool hamPresent = false;
#endregion


        // Графические объекты для игры
        /// <summary>
        /// Кусок сыра. Используется на переденем плане. Используется как мяч
        /// </summary>
        ForeSprite Cheese;

        /// <summary>
        /// Батон хлеба. Используется на переднем плане. Используется как ракетка
        /// </summary>
        ForeSprite Bread;

        /// <summary>
        /// Бонус - кусочек ветчины. Рисуется в случайной позиции при столкновении с помидором.
        /// </summary>
        ForeSprite Ham;

        /// <summary>
        /// Помидор. Используется как мишень. Данный спрайт используется для создания
        /// ряда помидорво
        /// </summary>
        BackSprite Tomato;

        /// <summary>
        /// Фоновый рисунок. Обрезается в верхней части, чтобы оставить место
        /// для сообщений и счета игры.
        /// </summary>
        Background TableCloth;

        /// <summary>
        /// Игровое поле. На нем рисуются спрайты.
        /// </summary>
        PlayField Field;

        /// <summary>
        /// Расстояние межды помидорами. 
        /// Устанавливаем один раз для игры
        /// </summary>
        private int tomatoSpacing = 4;

        /// <summary>
        /// Начальная высота, на которой располагаются помидоры
        /// </summary>
        private int tomatoLevelStartHeight = 20;

        /// <summary>
        /// Высота, на которой рисуется помидор
        /// Высота может меняться в процессе игры
        /// Начинаем с верхней части экрана
        /// </summary>
        private int tomatoDrawHeight = 20;

        /// <summary>
        /// Число помидоров на экране.
        /// Устанавливается при старте игры 
        /// методом initialiseTomatoes.
        /// </summary>
        private int noOfTomatoes;

        /// <summary>
        /// Позиции всех помидоров на экране
        /// </summary>
        private BackSprite[] tomatoes;

        /// <summary>
        /// Счет в игре
        /// </summary>
        private int scoreValue = 0;

        /// <summary>
        /// Звук, воспроизводимый при столкновении с батоном хлеба
        /// </summary>
        private Sound batHitSound;

        /// <summary>
        /// Звук, воспроизводимый при столкновении с помидором
        /// </summary>
        private Sound tomatoHitSound;

        /// <summary>
        /// Звук, воспроизводимый при столкновении с ветчиной
        /// </summary>
        private Sound bonusHamSound;

        /// <summary>
        /// True, если игра запущена на экране.
        /// </summary>
        private bool gameLive = false;

        /// <summary>
        /// Число оставшихся жизней.
        /// </summary>
        private int livesLeft;

        /// <summary>
        /// Число жизней, доступных для игрока.
        /// </summary>
        private int startLives = 3;

        /// <summary>
        /// Лучший результат, достигнутый в игре
        /// </summary>
        private int highScoreValue = 0;

        /// <summary>
        /// Имя игрока, достигшего лучшего результата.
        /// </summary>
        private string highScorePlayer = "Rob";

        /// <summary>
        /// Максимально допустимая скорость в игре
        /// </summary>
        private int maxSpeed = 5;

        /// <summary>
        /// Имя файла для хранения лучших результатов.
        /// </summary>
        private string highScoreFile = "highscore.bin";

        /// <summary>
        /// Папка, в которой находится программа.
        /// Используется как место для хранения настроек игры.
        /// </summary>
        private string applicationDirectory;

        /// <summary>
        /// Форма для ввода имени игрока с лучшим результатом.
        /// </summary>
        private HighScore highScore;

        /// <summary>
        /// Маска для белого цвета, который будет считаться прозрачным
        /// </summary>
        private System.Drawing.Imaging.ImageAttributes transparentWhite;

        /// <summary>
        /// Направление движения по оси X
        /// </summary>
        private bool goingRight = true;

        /// <summary>
        /// Направление движения по оси Y
        /// </summary>
        private bool goingDown = true;

        /// <summary>
        /// Скорость движения сыра по горизонтали
        /// </summary>
        private int xSpeed = 1;

        /// <summary>
        /// Скорость движения сыра по вертикали
        /// </summary>
        private int ySpeed = 1;

        /// <summary>
        /// Тестирование программы. Батон автоматически отслеживает движение сыра 
        /// </summary>
        private bool testingGame = false;

        public Form1()
        {
            InitializeComponent();

            // Получим ссылку на сборку
            System.Reflection.Assembly execAssem =
                System.Reflection.Assembly.GetExecutingAssembly();

            // Получим доступ к рисункам
            Cheese = new ForeSprite( execAssem, "cheese.gif" ) ;
			Bread = new ForeSprite( execAssem, "bread.gif" ) ;
			Tomato = new BackSprite( execAssem, "tomato.gif" ) ;
			Ham = new ForeSprite( execAssem, "ham.gif" ) ;
            TableCloth = new Background(execAssem, "tablecloth.gif");

            Field = new PlayField(TableCloth, ClientSize.Width, ClientSize.Height);

            Field.AddForegroundSprite(Bread);
            Field.AddForegroundSprite(Cheese);

            Ham.Visible = false;
            Bread.Visible = false;
            Field.AddForegroundSprite(Ham);

            // Инициализируем массив помидоров и прямоугольников
            initialiseTomatoes();

            // Получим звук при столкновении с батоном хлеба
            batHitSound =
                    new Sound(execAssem.GetManifestResourceStream(@"Bouncer.click.wav"));

            // Получим звук при столкновении с помидором
            tomatoHitSound =
                new Sound(execAssem.GetManifestResourceStream(@"Bouncer.burp.wav"));

            // Получим звук при столкновении с ветчиной
            bonusHamSound =
                new Sound(execAssem.GetManifestResourceStream(@"Bouncer.pig.wav"));

            // Генерируем новое случайное число
            randomNumbers = new Random();

            // Получим имя файла программы из текущей сборки
            string appFilePath =
                execAssem.GetModules()[0].FullyQualifiedName;
            // Выделяем из полного пути имени файла только путь к файлу
            applicationDirectory =
                System.IO.Path.GetDirectoryName(appFilePath);
            // Обязательно должен быть разделитель в конце пути
            if (!applicationDirectory.EndsWith(@"\"))
            {
                applicationDirectory += @"\";
            }

            LoadHighScore();

            // Выводим на экран лучший результат
            Field.Message = highScorePlayer + " : " + highScoreValue;

            // Создаем форму для лучших результатов
            highScore = new HighScore(this);

            //  Задаем белую маску.
            transparentWhite = new System.Drawing.Imaging.ImageAttributes();
            transparentWhite.SetColorKey(Color.White, Color.White);

            // Включаем таймер
            timer1.Enabled = true;
          }


        private void updatePositions()
        {
            // Движение кусочка сыра

            if (goingRight)
            {
                Cheese.X += xSpeed;
            }
            else
            {
                Cheese.X -= xSpeed;
            }

            if ((Cheese.X + Cheese.Width) >= this.Width)
            {
                goingRight = false;
            }

            if (Cheese.X <= 0)
            {
                goingRight = true;
            }
          
            if (goingDown)
            {
                Cheese.Y += ySpeed;
            }
            else
            {
                Cheese.Y -= ySpeed;
            }


            if ((Cheese.Y + Cheese.Height) >= this.Height)
            {
                // сыр достиг нижней части экрана
                loseLife();
                goingDown = false;
            }

            if (Cheese.Y <= Field.Messageheight)
            {
                goingDown = true;
            }

            if (!gameLive)
            {
                return;
            }

            // Управление батоном хлеба

            if (keyArgs != null)
            {
                switch (keyArgs.KeyCode)
                {
                                           
                        case Keys.Up:
                            Bread.Y -= ySpeed;
                            break;
                        case Keys.Down:
                            Bread.Y += ySpeed;
                            break;
                        case Keys.Left:
                            Bread.X -= xSpeed;
                            break;
                        case Keys.Right:
                            Bread.X += xSpeed;
                            break;
                }

            }

            if (testingGame)
            {
                Bread.X = Cheese.X;
                Bread.Y = ClientSize.Height - Bread.Height;
            }

            // Проверка на столкновение
            bool noTomatoes = true;

            if (goingDown)
            {
                // одно отражение при движении сыра вниз
                if (Cheese.IntersectsWith(Bread))
                {
                    // столкновение
                    // воспроизводим удар
                    batHitSound.Play();

                    bool rightIn = Bread.Contains(Cheese.Right, Cheese.Bottom);
                    bool leftIn = Bread.Contains(Cheese.Left, Cheese.Bottom);

                    // способ отражения
                    if (rightIn & leftIn)
                    {
                        // отражаемся вверх
                        goingDown = false;
                    }
                    else
                    {
                        // отражаемся вверх
                        goingDown = false;
                        // в зависимости от вида столкновений
                        if (rightIn)
                        {
                            goingRight = false;
                        }
                        if (leftIn)
                        {
                            goingRight = true;
                        }
                    }
                }
            }

                // Уничтожаем помидоры при столкновении с сыром
                foreach (BackSprite tomato in tomatoes)
                {
                    if (!(tomato.DrawState == BackSprite.BackSpriteState.visible))
                    {
                        continue;
                    }

                    noTomatoes = false;

                    if (Cheese.SpriteRect.IntersectsWith(tomato.SpriteRect)) 
                    {
                        // воспроизводим звук столкновения сыра с помидором
                        tomatoHitSound.Play();

                        // прячем томат
                        tomato.DrawState = BackSprite.BackSpriteState.disappearing;
                        
                        // обновляем счет
                        scoreValue = scoreValue + 10;
                        Field.Message = "Счет: " + scoreValue + "  Жизнь: " + livesLeft;

                        // меняем направление движения сыра
                        goingDown = !goingDown;

                        // Активизируем ветчину
                        startHam();

                        // удаляем только один помидор за раз
                        break;
                    }
                }
                if (noTomatoes)
                {
                    newLevel();
                }
                hamTick();
  
        }

        /// <summary>
        /// Изменение скорости движения сыра по осям x и y
        /// </summary>
        /// <param name="change">Изменяемое значение. Может быть отрицательным.</param>
        private void changeSpeed(int change)
        {
            xSpeed += change;
            ySpeed += change;
        }

        /// <summary>
        /// Вызывается один раз для установки всех помидоров
        /// </summary>
        private void initialiseTomatoes()
        {
            noOfTomatoes =
                (Field.WindowWidth - tomatoSpacing) /
                (Tomato.Width + tomatoSpacing);

            // создаем массив, содержащий позицию помидоров
            tomatoes = new BackSprite[noOfTomatoes];

            // Координата x каждого помидора
            int tomatoX = tomatoSpacing / 2;

            for (int i = 0; i < tomatoes.Length; i++)
            {
                tomatoes[i] = new BackSprite(Tomato, tomatoX, tomatoDrawHeight);
                Field.AddBackgroundSprite(tomatoes[i]);
                tomatoX = tomatoX + Tomato.Width + tomatoSpacing;
            }            
        }

        /// <summary>
        /// Вызывается для создания ряда помидоров.
        /// </summary>
        private void placeTomatoes()
        {
            foreach (BackSprite s in tomatoes)
            {
                s.SpriteRect.Y = tomatoDrawHeight;
                s.DrawState = BackSprite.BackSpriteState.appearing;
            }
        }

        private void startGame()
        {
            // Очистим поле
            Field.ClearBackground();

            // Поместим кусок сыра в центре поля
            Cheese.SpriteRect.X = ClientSize.Width / 2;
            Cheese.SpriteRect.Y = ClientSize.Height / 2;

            // Прячем ветчину
            Ham.Visible = false;

            // Поместим батон хлеба нижней части экрана (посередине)
            Bread.SpriteRect.X = (ClientSize.Width - Bread.Width) / 2;
            Bread.SpriteRect.Y = ClientSize.Height - Bread.Height - 20;
            Bread.Visible = true;

            // Устанавливаем число жизней и счет 
            livesLeft = startLives;
            scoreValue = 0;

            // Располагаем помидоры в верхней части экрана
            tomatoDrawHeight = tomatoLevelStartHeight;
            placeTomatoes();

            // Установим начальную скорость
            xSpeed = 1;
            ySpeed = 1;

            // update the message
            Field.Message = "Score : " + scoreValue + "  Lives: " + livesLeft;

            // Установим флаг, позволяющий начать игру
            gameLive = true;
        }

        private void loseLife()
        {
            if (!gameLive)
            {
                return;
            }
            // Потеряли еще одну жизнь 
            livesLeft--;
            if (livesLeft > 0)
            {
                // обновим сообщение на экране
                Field.Message = "Счет: " + scoreValue + "  Жизнь: " + livesLeft;
            }
            else
            {
                // Останавливаем игру
                gameLive = false;
                // сравниваем с лучшим результатом
                if (scoreValue > highScoreValue)
                {
                    highScoreValue = scoreValue;
                    timer1.Enabled = false;
                    // Показываем форму для лучших результатов
                    highScore.ShowDialog();
                    timer1.Enabled = true;
                    highScorePlayer = highScore.PlayerName;
                }
                // меняем сообщение на экране
                Field.Message = highScorePlayer + " : " + highScoreValue;
            }
        }

        private void newLevel()
        {
            if (!gameLive)
            {
                return;
            }
            // Рисуем помидоры чуть ниже
            tomatoDrawHeight += tomatoSpacing;

            if (tomatoDrawHeight > (ClientSize.Height - (Bread.Height + Tomato.Height))) 
            {
                //  Рисуем помидоры снова в верхней части экрана
                tomatoDrawHeight = tomatoLevelStartHeight;
            }
            placeTomatoes();

            // Увеличиваем скорость
            if (xSpeed < maxSpeed)
            {
                xSpeed++;
                ySpeed++;
            }
        }

        /// <summary>
        /// Интервал от 0 до 10. Чем выше значение,
        /// тем чаще ветчина появляется на экране
        /// </summary>
        private int hamLikelihood = 5;

        /// <summary>
        /// Отчет времени перед исчезновением ветчины.
        /// Устанавливаем случайное число при появлении ветчины.
        /// </summary>
        private int hamTimerCount;

        /// <summary>
        /// Случайное число.
        /// </summary>
        private Random randomNumbers;

        /// <summary>
        /// Вызывается для активизации ветчины
        /// </summary>
        private void startHam()
        {
            // не продолжать, если ветчина уже есть на экране
            if (Ham.Visible)
            {
                return;
            }
            // решаем, как часто выводить ветчину на экран
            if (randomNumbers.Next(10) > hamLikelihood)
            {
                // не выводить ветчину на экран
                return;
            }
            // позиция ветчины в случайной позиции на экране
            Ham.X = randomNumbers.Next(ClientSize.Width - Ham.Width);
            Ham.Y = randomNumbers.Next(ClientSize.Height - Ham.Height);
            // делаем ветчину видимым
            Ham.Visible = true;

            // как долго держится изображение ветчины на экране
            // (по крайне мере 50 тиков)
            hamTimerCount = 50 + randomNumbers.Next(100);

        }

        /// <summary>
        /// Обновляем состояние ветчины
        /// </summary>
        private void hamTick()
        {
            // ничего не делаем, если ветчина невидима
            if (!Ham.Visible)
            {
                return;
            }
            if (Bread.IntersectsWith(Ham))
            {
                // при касании игроком куска ветчины
                // прибавляем 100 очков
                scoreValue = scoreValue + 100;
                Field.Message = "Счет: " + scoreValue + "  Жизнь: " + livesLeft;
                // звук касания ветчины
                bonusHamSound.Play();
                // прячем ветчину с экрана
                Ham.Visible = false;
            }
            else
            {
                // Отчитываем время назад
                hamTimerCount--;
                if (hamTimerCount == 0)
                {
                    // время вышло – удаляем ветчину
                    Ham.Visible = false;
                }
            }
        }

        /// <summary>
        /// Сохраняем лучший результат в файле.
        /// </summary>
        public void SaveHighScore()
        {
            System.IO.TextWriter writer = null;
            try
            {
                writer = new System.IO.StreamWriter(
                    applicationDirectory + highScoreFile);
                writer.WriteLine(highScorePlayer);
                writer.WriteLine(highScoreValue);
            }
            catch { }
            finally
            {
                if (writer != null)
                {
                    writer.Close();
                }
            }
        }

        /// <summary>
        /// Загружаем лучший результат из файла.
        /// </summary>
        public void LoadHighScore()
        {
            System.IO.TextReader reader = null;
            try
            {
                reader = new System.IO.StreamReader(
                    applicationDirectory + highScoreFile);
                highScorePlayer = reader.ReadLine();
                string highScoreString = reader.ReadLine();
                highScoreValue = int.Parse(highScoreString);
            }
            catch { }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }
        }


        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            // Не разрешаем перерисовывать фон  
        } 

        /// <summary>
        /// картинка-буфер
        /// </summary>
        //private Bitmap backBuffer = null;

        private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Field.doPaint(sender, e);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Field.ClearForeground();
            updatePositions();
            Field.DrawForeground();
            Invalidate();
        }

        /// <summary>
        /// Используем keyArgs в качестве флага
        /// </summary>
        private System.Windows.Forms.KeyEventArgs keyArgs = null;

        private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            keyArgs = e;
        }

        private void Form1_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            keyArgs = null;
        }

        private void menuItem1_Click(object sender, EventArgs e)
        {
            startGame();
        }

        private void menuItem2_Click(object sender, EventArgs e)
        {
            SaveHighScore();
            Application.Exit();
        }

    }
}