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

namespace EvoBoat.EvoAlg.Perm
{
    public class Perm_GA<P, S> : GA<P, S, PermVector, int>
        where P : PermProblem<S>
        where S : VecSolution<PermVector, int>, new()
    {
        public static Func<EAlg<P, S, PermVector, int>, S, int, double, S> MutationFunc = (alg, sol, index, mr) =>
        {
            bool bChanged = false;
            S sol1 = new S() { Content = sol.Content.Clone(), Fit = sol.Fit };
            int D = alg.Problem.Dimension;
            for (int d = 0; d < D; d++)
            {
                if (alg.Rand.NextDouble() < mr)
                {
                    int i = alg.Rand.Next(D);
                    int j = alg.Rand.Next(D);
                    while (j == i || (j == 0 && i == D - 1) || (i == 0 && j == D - 1))
                        j = alg.Rand.Next(D);
                    sol1.Content.Reverse(i, j);
                    if (!bChanged) bChanged = true;
                }
            }
            if (bChanged) alg.Evaluate(sol1);
            return sol1;
        };

        public Perm_GA() : base()
        { }

        public Perm_GA(P problem, int seed = 0) : base(problem, seed)
        {
            this.Factory = new PermSolutionFactory<P, S>(problem);
        }

        public override void SetFactory()
        {
            if (Problem == null)
                throw new ArgumentNullException("Please assign the problem instance before set the factory");
            Factory = new PermSolutionFactory<P, S>(Problem);
        }

        public override Tuple<S, S> Crossover(S sol1, S sol2)
        {
            int n = Problem.Dimension;
            int[] arr1 = new int[n], arr2 = new int[n];
            int i, i1 = Rand.Next(1, n - 2), i2 = Rand.Next(i1 + 1, n - 1), k, v;
            for (i = i1; i < i2; i++)
            {
                arr1[i] = sol2.Content[i];
                arr2[i] = sol1.Content[i];
            }
            for (i = 0; i < i1; i++)
            {
                v = sol1.Content[i];
                k = Array.IndexOf(arr1, v, i1, i2 - i1);
                while (k >= 0)
                {
                    v = sol1.Content[k];
                    k = Array.IndexOf(arr1, v, i1, i2 - i1);
                }
                arr1[i] = v;
                v = sol2.Content[i];
                k = Array.IndexOf(arr2, v, i1, i2 - i1);
                while (k >= 0)
                {
                    v = sol2.Content[k];
                    k = Array.IndexOf(arr2, v, i1, i2 - i1);
                }
                arr2[i] = v;
            }
            for (i = i2; i < n; i++)
            {
                v = sol1.Content[i];
                k = Array.IndexOf(arr1, v, i1, i2 - i1);
                while (k >= 0)
                {
                    v = sol1.Content[k];
                    k = Array.IndexOf(arr1, v, i1, i2 - i1);
                }
                arr1[i] = v;
                v = sol2.Content[i];
                k = Array.IndexOf(arr2, v, i1, i2 - i1);
                while (k >= 0)
                {
                    v = sol2.Content[k];
                    k = Array.IndexOf(arr2, v, i1, i2 - i1);
                }
                arr2[i] = v;
            }
            PermVector vec1 = new PermVector(n, false), vec2 = new PermVector(n, false);
            vec1.values = arr1;
            vec2.values = arr2;
            S ch1 = new S() { Content = vec1 };
            S ch2 = new S() { Content = vec2 };
            this.Evaluate(ch1);
            this.Evaluate(ch2);
            return new Tuple<S, S>(ch1, ch2);
        }

        public override S Mutate(S sol, int index)
        {
            return MutationFunc(this, sol, index, this.MutationRate);
        }
    }
}
