using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EvoBoat.EvoAlg;
using EvoBoat.EvoAlg.Perm;

namespace TSPTest
{
    public struct Location
    {
        public int X { get; set; }
        public int Y { get; set; }

        public Location(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public static implicit operator LocationD(Location l)
        {
            return new Location(l.X, l.Y);
        }
    }

    public struct LocationD
    {
        public double X { get; set; }
        public double Y { get; set; }

        public LocationD(double x, double y)
        {
            this.X = x;
            this.Y = y;
        }

        public static explicit operator Location(LocationD l)
        {
            return new Location((int)Math.Round(l.X), (int)Math.Round(l.Y));
        }
    }

    public class TSP : PermProblem<VecSolution<PermVector, int>>
    {
        double[,] matrix;
        /// <summary>
        /// Matrix of distances
        /// </summary>
        public double[,] CostMatrix
        {
            get { return matrix; }
        }

        LocationD[] locations;
        public LocationD[] Locations
        {
            get { return locations; }
        }

        double kbv;
        public double KownBestValue
        {
            get { return kbv; }
        }

        public string Name { get; set; }

        public TSP(double[,] matrix) : base(matrix.GetLength(0))
        {
            this.matrix = matrix;
        }

        public TSP(double[,] matrix, int kbv) : base(matrix.GetLength(0))
        {
            this.matrix = matrix;
            this.kbv = kbv;
        }

        public TSP(LocationD[] locations) : base(locations.Length)
        {
            this.locations = locations;
            matrix = new double[dimension, dimension];
            double dx, dy;
            for (int i = 0; i < dimension; i++)
            {
                matrix[i, i] = 0;
                for (int j = i + 1; j < dimension; j++)
                {
                    dx = locations[i].X - locations[j].X;
                    dy = locations[i].Y - locations[j].Y;
                    matrix[i, j] = (int)Math.Round(Math.Sqrt(dx * dx + dy * dy));
                    matrix[j, i] = matrix[i, j];
                }
            }
        }

        public TSP(LocationD[] locations, int kbv) : base(locations.Length)
        {
            this.locations = locations;
            matrix = new double[dimension, dimension];
            double dx, dy;
            for (int i = 0; i < dimension; i++)
            {
                matrix[i, i] = 0;
                for (int j = i + 1; j < dimension; j++)
                {
                    dx = locations[i].X - locations[j].X;
                    dy = locations[i].Y - locations[j].Y;
                    matrix[i, j] = (int)Math.Round(Math.Sqrt(dx * dx + dy * dy));
                    matrix[j, i] = matrix[i, j];
                }
            }
            this.kbv = kbv;
        }

        public override string ToString()
        {
            return Name;
        }

        public override double Evaluate(PermVector s)
        {
            if (s.Length != dimension)
                throw new ArgumentException("dimension error");
            double dis = 0;
            for (int i = 0; i < dimension - 1; i++)
                dis += matrix[s[i], s[i + 1]];
            dis += matrix[s[dimension - 1], s[0]];
            return dis;
        }
    }
}
