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.Threading;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using EvoBoat.EvoAlg;
using EvoBoat.EvoAlg.Perm;
using EvoBoat.EvoAlg.PSO;
using EvoBoat.EvoAlg.BBO;
using EvoBoat.EvoAlg.WWO;

namespace TSPTest
{
    using PVS = VecSolution<PermVector, int>;
    using DVS = VecSolution<EvoBoat.EvoAlg.Vector, double>;
    using EA = EAlg<TSP, VecSolution<PermVector, int>, PermVector, int>;

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        TSP[] problems; //候选问题集
        TSP problem;//当前所选问题
        EA[] algs; //候选算法集
        EA alg; //当前所选算法
        Ellipse[] points;
        Path path;
        DispatcherTimer timer;
        int size = 50, iter = 0, iters = 1000;
        double xMin, yMin, rx, ry;

        public MainWindow()
        {
            timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(50) };
            timer.Tick += Timer_Tick;
            InitializeComponent();
            problems = new TSP[] { TSPLib.Burma14, TSPLib.Sahara29, TSPLib.Djibouti38, TSPLib.Ch130, TSPLib.Qatar194, };
            algs = new EA[] { new NMPermAlg<TSP, PVS, Num_PSOAlg<DVS>>(), new NMPermAlg<TSP, PVS, Num_CLPSOAlg<DVS>>(), new NMPermAlg<TSP, PVS, Num_EBOAlg<DVS>>(), new Perm_GA<TSP, PVS>(), new Perm_BBOAlg<TSP, PVS>(), new Perm_LocalBBOAlg<TSP, PVS>(), new TSP_WWOAlg(), new TSP_NWWOAlg() };
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            foreach (TSP p in problems)
                cmbPoblem.Items.Add(p);
            foreach (EA a in algs)
                cmbAlg.Items.Add(a);
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (problem != null)
                this.DrawTSPMap(problem);
        }

        private void cmbPoblem_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            problem = (TSP)cmbPoblem.SelectedItem;
            lbKnown.Content = "已知最优解" + problem.KownBestValue;
            this.DrawTSPMap(problem);
        }

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

        private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            size = (int)slider1.Value;
        }

        private void slider2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            iters = (int)slider2.Value;
        }

        private void slider3_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            timer.Interval = TimeSpan.FromMilliseconds(1000.0 / slider3.Value);
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            if (cmbPoblem.SelectedItem == null || cmbAlg.SelectedItem == null)
            {
                MessageBox.Show("请选择要求解的问题和要执行的算法", "提示");
                return;
            }
            button.IsEnabled = false;
            alg.Reset();
            alg.Problem = problem;
            alg.Size = size;
            alg.Iters = iters;
            alg.SetParameters();
            alg.SetFactory();
            alg.InitPop();
            iter = 0;
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            alg.Calculate();
            alg.Evolve();
            this.DrawTSPPath(problem, alg.Best);
            lbResult.Content = string.Format("best:{0}({1}次)", alg.Best.Fit, ++iter);
            if (iter == iters)
            {
                timer.Stop();
                MessageBox.Show(string.Format("算法求得最优解{0}({1}): ", alg.Best.Fit, alg.Evaluate(alg.Best)));
                button.IsEnabled = true;
            }
        }

        void DrawTSPMap(TSP problem)
        {
            xMin = yMin = double.PositiveInfinity;
            double xMax = 0, yMax = 0;
            for (int i = 0; i < problem.Locations.Length; i++)
            {
                if (xMin > problem.Locations[i].X)
                    xMin = problem.Locations[i].X;
                if (xMax < problem.Locations[i].X)
                    xMax = problem.Locations[i].X;
                if (yMin > problem.Locations[i].Y)
                    yMin = problem.Locations[i].Y;
                if (yMax < problem.Locations[i].Y)
                    yMax = problem.Locations[i].Y;
            }
            rx = (canvas1.ActualWidth - 5) / (xMax - xMin);
            ry = (canvas1.ActualHeight - 5) / (yMax - yMin);
            points = new Ellipse[problem.Locations.Length];
            canvas1.Children.Clear();
            for (int i = 0; i < problem.Locations.Length; i++)
            {
                points[i] = new Ellipse() { Width = 5, Height = 5, Fill = Brushes.Black };
                Point pt = MapLocation(problem.Locations[i]);
                Canvas.SetLeft(points[i], pt.X);
                Canvas.SetTop(points[i], pt.Y);
                canvas1.Children.Add(points[i]);
            }
        }

        void DrawTSPPath(TSP problem, PVS s)
        {
            if (path != null)
                canvas1.Children.Remove(path);
            path = new Path() { Stroke = Brushes.Blue, StrokeThickness = 2 };
            PathFigure fig = new PathFigure() { IsClosed = true };
            fig.StartPoint = MapLocation(problem.Locations[s.Content[0]]);
            for (int i = 1; i < s.Content.Length; i++)
            {
                fig.Segments.Add(new LineSegment(MapLocation(problem.Locations[s.Content[i]]), true));
            }
            PathGeometry geo = new PathGeometry();
            geo.Figures.Add(fig);
            path.Data = geo;
            canvas1.Children.Add(path);
        }

        Point MapLocation(LocationD loc)
        {
            return new Point((loc.X - xMin) * rx + 1, (loc.Y - yMin) * ry + 1);
        }
    }
}
