using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Windows.Shapes;
using EvoBoat.EvoAlg;
using EvoBoat.EvoAlg.IntProg;

namespace PlantDemo
{
    using EA = EAlg<PlantZombProblem, PlantZombSolution, IntVector, int>;
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        Game game;
        Random rand;
        int iSel = -1, kAni = 0, kTime = 0, iter = 0, iters = 100;
        double gridW, gridH;
        Dictionary<Position, PlantShower> plantShowers;
        List<BulletShower> bulletShowers;
        List<ZombieShower> zombieShowers;
        DispatcherTimer timerPlant, timerBullet, timerZomb, timerAlg;
        PlantZombProblem problem;
        EA[] algs;
        EA alg;
        PlantZombSolutionFactory factory;

        public MainWindow()
        {
            InitializeComponent();
            PresetWindow pWin = new PresetWindow();
            if (pWin.ShowDialog() == true)
                game = new Game(pWin.SunScore);
            else
                game = new Game(2500);
            rand = new Random();
            tbSunScore.Text = game.SunScoreMax.ToString();
            plantShowers = new Dictionary<Position, PlantShower>();
            bulletShowers = new List<BulletShower>();
            zombieShowers = new List<ZombieShower>();
            problem = new PlantZombProblem(game.SunScoreMax);
            algs = new EA[] { new IP_GA<PlantZombProblem, PlantZombSolution>(problem), new IP_PSOAlg<PlantZombProblem, PlantZombSolution>(problem), new IP_DEAlg<PlantZombProblem, PlantZombSolution>(problem), new IP_WWOAlg<PlantZombProblem, PlantZombSolution>(problem) };
            factory = new PlantZombSolutionFactory(problem);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            foreach (EA a in algs)
            {
                cmbAlgs.Items.Add(a);
                a.Factory = factory;
            }
            cmbAlgs.SelectedIndex = 0;
            gridW = grid.ColumnDefinitions[0].ActualWidth;
            gridH = grid.RowDefinitions[0].ActualHeight;
            timerPlant = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100 / game.Speed) };
            timerBullet = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(30 / game.Speed) };
            timerZomb = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100 / game.Speed) };
            timerPlant.Tick += timerPlant_Tick;
            timerBullet.Tick += TimerBullet_Tick;
            timerZomb.Tick += TimerZomb_Tick;
            timerAlg = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(50) };
            timerAlg.Tick += timerAlg_Tick;
            this.ShowStartAvi();
        }

        void ShowStartAvi()
        {
            DoubleAnimation ani = new DoubleAnimation(20, 400, TimeSpan.FromSeconds(2));
            ani.Completed += Ani_Completed;
            DoubleAnimation ani1 = new DoubleAnimation(18, 400, TimeSpan.FromSeconds(2));
            Image[] grasses = { grass1, grass2, grass3, grass4, grass5, grass6 };
            Image[] sodRolls = { sodRoll1, sodRoll2, sodRoll3, sodRoll4, sodRoll5, sodRoll6 };
            Image[] sodRollCaps = { sodRollCap1, sodRollCap2, sodRollCap3, sodRollCap4, sodRollCap5, sodRollCap6 };
            for (int i = 0; i < grasses.Length; i++)
                grasses[i].BeginAnimation(Image.WidthProperty, ani);
            for (int i = 0; i < sodRolls.Length; i++)
            {
                sodRolls[i].BeginAnimation(Canvas.LeftProperty, ani1);
                sodRollCaps[i].BeginAnimation(Canvas.LeftProperty, ani1);
            }
        }

        private void Ani_Completed(object sender, EventArgs e)
        {
            if (++kAni < 6)
                return;
            Image[] imgs = { sodRoll1, sodRollCap1, sodRoll2, sodRollCap2, sodRoll3, sodRollCap3, sodRoll4, sodRollCap4, sodRoll5, sodRollCap5, sodRoll6, sodRollCap6 };
            for (int i = 0; i < imgs.Length; i++)
                canvas.Children.Remove(imgs[i]);
            DoubleAnimation oani = new DoubleAnimation(1, 0.3, TimeSpan.FromSeconds(1));
            Image[] grasses = { grass1, grass2, grass3, grass4, grass5, grass6 };
            for (int i = 0; i < grasses.Length; i++)
                grasses[i].BeginAnimation(Image.OpacityProperty, oani);
            Image img;
            for (int i = 0; i < 6; i++)
            {
                img = new Image() { Width = 56 };
                img.Source = new BitmapImage(new Uri("images/sunflower.gif", UriKind.Relative));
                Grid.SetColumn(img, 0);
                Grid.SetRow(img, i);
                grid.Children.Add(img);
            }
            imgWDSS.IsEnabled = imgWDSS2.IsEnabled = imgWDSS3.IsEnabled = imgHBSS.IsEnabled = imgXMG.IsEnabled = imgPMG.IsEnabled = true;
            timerPlant.Start();
            timerBullet.Start();
            timerZomb.Start();
        }

        private void imgWDSS_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 0;
            this.Cursor = Cursors.Hand;
        }

        private void imgWDSS2_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 1;
            this.Cursor = Cursors.Hand;
        }

        private void imgWDSS3_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 2;
            this.Cursor = Cursors.Hand;
        }

        private void imgHBSS_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 3;
            this.Cursor = Cursors.Hand;
        }

        private void imgXMG_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 4;
            this.Cursor = Cursors.Hand;
        }

        private void imgPMG_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (game.SunScore < 30) return;
            iSel = 5;
            this.Cursor = Cursors.Hand;
        }

        private void grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point pt = e.GetPosition(grid);
            Position pos = new Position((int)(pt.Y / gridH), (int)(pt.X / gridW));
            if (iSel < 0 || pos.Col <= 0 || pos.Col >= 5 || pos.Row < 0 || pos.Row >= grid.RowDefinitions.Count || game.Plants.ContainsKey(pos))
                return;
            Plant p = null;
            PlantShower ps = null;
            if (iSel == 0)
            {
                p = new PlWDSS();
                ps = new PlantShower(p, "images/wdss.png");
            }
            else if (iSel == 1)
            {
                p = new PlWDSS2();
                ps = new PlantShower(p, "images/wdss2.png");
            }
            else if (iSel == 2)
            {
                p = new PlWDSS3();
                ps = new PlantShower(p, "images/wdss3.png");
            }
            else if (iSel == 3)
            {
                p = new PlHBSS();
                ps = new PlantShower(p, "images/hbss.png");
            }
            else if (iSel == 4)
            {
                p = new PlXMG();
                ps = new PlantShower(p, "images/xmg.png");
            }
            else if (iSel == 5)
            {
                p = new PlPMG();
                ps = new PlantShower(p, "images/pmg.png");
            }
            if (game.SunScore < p.Price)
                return;
            Grid.SetColumn(ps.Shower, pos.Col);
            Grid.SetRow(ps.Shower, pos.Row);
            ps.Shower.HorizontalAlignment = HorizontalAlignment.Center;
            ps.Shower.VerticalAlignment = VerticalAlignment.Center;
            grid.Children.Add(ps.Shower);
            plantShowers.Add(pos, ps);
            game.AddPlant(pos, p);
            tbSunScore.Text = game.SunScore.ToString();
            this.Cursor = Cursors.Arrow;
            iSel = -1;
        }

        private void grid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.Cursor = Cursors.Arrow;
            iSel = -1;
        }

        private void timerPlant_Tick(object sender, EventArgs e)
        {
            Plant p;
            PlantShower pSh;
            Bullet bul;
            BulletShower bSh;
            foreach (Position pos in plantShowers.Keys)
            {
                pSh = plantShowers[pos];
                p = pSh.Plant;
                if (kTime * p.FireFrequency % 12 == 0)
                {
                    bul = p.Fire();
                    bSh = bul.CreateShower();
                    bulletShowers.Add(bSh);
                    canvas.Children.Add(bSh.Shower);
                    bul.OnDestroyed += bul_OnDestroyed;
                }
            }
            kTime++;
        }

        private void TimerBullet_Tick(object sender, EventArgs e)
        {
            BulletShower bSh;
            for (int i = bulletShowers.Count - 1; i >= 0; i--)
            {
                bSh = bulletShowers[i];
                bSh.Bullet.Move();
                if (bSh.Bullet.X < 0 || bSh.Bullet.X > canvas.ActualWidth || bSh.Bullet.Y < 0 || bSh.Bullet.Y >= canvas.ActualHeight)
                {
                    bulletShowers.RemoveAt(i);
                    canvas.Children.Remove(bSh.Shower);
                    bSh.Bullet.Destroy();
                }
                else
                    bSh.UpdateShow();
            }
        }

        private void TimerZomb_Tick(object sender, EventArgs e)
        {
            ZombieShower zSh;
            if (rand.Next(5) == 0) //平均每5个时间单位产生一个僵尸
            {
                int row = rand.Next(6);
                Zombie z = rand.NextDouble() < problem.ZombOccureProbs[row, 0] ? new Zombie() : new WireZombie();
                z.X = canvas.ActualWidth - 10;
                z.Y = 80 * row;
                z.OnDestroyed += zomb_OnDestroyed;
                game.AddZombie(z);
                zSh = z.CreateShower();
                canvas.Children.Add(zSh.Shower);
                zombieShowers.Add(zSh);
            }
            for (int i = zombieShowers.Count - 1; i >= 0; i--)
            {
                zSh = zombieShowers[i];
                if (zSh.Zombie.X > 400)
                    zSh.Zombie.Move();
                zSh.UpdateShow();
            }
        }

        private void bul_OnDestroyed(object sender, EventArgs e)
        {
            Bullet bul = sender as Bullet;
            int k = bulletShowers.FindIndex(zSh => zSh.Entity == bul);
            if (k >= 0)
            {
                canvas.Children.Remove(bulletShowers[k].Shower);
                bulletShowers.RemoveAt(k);
            }
        }

        private void zomb_OnDestroyed(object sender, EventArgs e)
        {
            Zombie zomb = sender as Zombie;
            int k = zombieShowers.FindIndex(zSh => zSh.Entity == zomb);
            if (k < 0)
                return;
            RotateTransform rotate = new RotateTransform();
            zombieShowers[k].Shower.RenderTransform = rotate;
            rotate.CenterX = 10 + zomb.Width / 2;
            rotate.CenterY = 10 + zomb.Height / 2;
            DoubleAnimation desAni = new DoubleAnimation(0, 90, new Duration(TimeSpan.FromMilliseconds(200)));
            Storyboard story = new Storyboard();
            story.Children.Add(desAni);
            Storyboard.SetTarget(desAni, zombieShowers[k].Shower);
            Storyboard.SetTargetProperty(desAni, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
            story.Completed += DesAni_Completed;
            zombieShowers.RemoveAt(k);
            story.Begin();
        }

        private void DesAni_Completed(object sender, EventArgs e)
        {
            ClockGroup cg = sender as ClockGroup;
            if (cg == null)
                return;
            UIElement shower = Storyboard.GetTarget(cg.Children[0].Timeline) as UIElement;
            if (shower != null)
                canvas.Children.Remove(shower);
        }

        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            grid.Children.RemoveRange(6, grid.Children.Count - 6);
            plantShowers.Clear();
            game.Reset();
            tbSunScore.Text = game.SunScore.ToString();
            iSel = -1;
            kAni = kTime = 0;
        }

        private void btnCalc_Click(object sender, RoutedEventArgs e)
        {
            int[] vec = new int[24];
            foreach (Position pos in game.Plants.Keys)
                vec[pos.Row * 4 + pos.Col - 1] = Array.IndexOf(problem.PlantTypes, game.Plants[pos].GetType());
            PlantZombSolution sol = new PlantZombSolution(vec);
            problem.Evaluate(sol);
            MessageBox.Show(string.Format("总攻击力{0:F3}", alg.Best.TDamage));
        }

        private void cmbAlgs_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            alg = (EA)cmbAlgs.SelectedItem;
        }

        private void btnSolve_Click(object sender, RoutedEventArgs e)
        {
            btnClear_Click(sender, e);
            timerPlant.IsEnabled = btnClear.IsEnabled = btnCalc.IsEnabled = false;
            int pSize;
            if (!int.TryParse(tbSize.Text, out pSize))
            {
                MessageBox.Show("请输入正确的种群大小");
                tbSize.Focus();
                return;
            }
            if (!int.TryParse(tbIters.Text, out iters))
            {
                MessageBox.Show("请输入正确的迭代次数");
                tbIters.Focus();
                return;
            }
            alg.SetParameters();
            alg.Size = pSize;
            alg.InitPop();
            iter = 0;
            timerAlg.Start();
        }

        private void timerAlg_Tick(object sender, EventArgs e)
        {
            if (iter++ >= iters)
            {
                timerAlg.Stop();
                MessageBox.Show(string.Format("求解结束,攻击力{0:F3}", alg.Best.TDamage));
                timerPlant.IsEnabled = btnClear.IsEnabled = btnCalc.IsEnabled = true;
                alg.Reset();
                return;
            }
            alg.Calculate();
            alg.Evolve();
            alg.Pop[alg.IndWorst] = problem.TryImproveSolution(alg.Best);
            alg.UpdateWorst();
            PlantZombSolution sol = alg.Best;
            if (iter % 5 == 0)
            {
                //timerGA.Stop();
                this.PresentSolution(sol);
                //this.Dispatcher.Invoke(new Action(()=> this.PresentSolution(sol)));
                //timerGA.Start();
            }
        }

        void PresentSolution(PlantZombSolution sol)
        {
            grid.Children.RemoveRange(6, grid.Children.Count - 6);
            Image img;
            for (int d = 0; d < problem.Dimension; d++)
            {
                if (sol.Content[d] == 0)
                    continue;
                img = new Image() { Source = PlantShower.Bitmaps[sol.Content[d] - 1], Width = 72, Height = 72 };
                Grid.SetRow(img, d / 4);
                Grid.SetColumn(img, d % 4 + 1);
                grid.Children.Add(img);
                this.Dispatcher.Invoke(new Action(() => tbDamage.Text = string.Format("攻击力{0:F3}", alg.Best.TDamage)));
                this.Dispatcher.Invoke(new Action(() => tbSunScore.Text = (game.SunScore - sol.TCost).ToString()));
            }
        }
    }
}
