using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EvoBoat.EvoAlg.Perm
{
    public class NMPermAlg<P, S, A> : EAlg<P, S, PermVector, int>
        where P : PermProblem<S>
        where S : VecSolution<PermVector, int>, new()
        where A : EAlg<NumOptProblem<VecSolution<Vector, double>>, VecSolution<Vector, double>, Vector, double>, new()
    {
        A alg;
        public A NumAlg { get { return alg; } }

        double EvaluateVector(Vector v)
        {
            PermVector p = VectorMapPerm(v);
            return Problem.Evaluate(p);
        }
        
        public NMPermAlg(int seed = 0) : base(seed)
        {
            alg = new A();
            Name = "NM" + alg.Name;
        }

        public NMPermAlg(P problem, int seed = 0) : base(problem, seed)
        {
            alg = new A();
            Name = "NM" + alg.Name;
            alg.Problem = new NumOptProblem<VecSolution<Vector, double>>(EvaluateVector, problem.Dimension);
            alg.Factory = new VectorSolutionFactory<NumOptProblem<VecSolution<Vector, double>>, VecSolution<Vector, double>>(alg.Problem);
        }

        public override void SetFactory()
        {
            if (Problem == null)
                throw new ArgumentNullException("Please assign the problem instance before set the factory");
            alg.Problem = new NumOptProblem<VecSolution<Vector, double>>(EvaluateVector, Problem.Dimension);
            alg.Factory = new VectorSolutionFactory<NumOptProblem<VecSolution<Vector, double>>, VecSolution<Vector, double>>(alg.Problem);
        }

        public override void SetParameters()
        {
            alg.SetParameters();
        }

        public override void InitPop()
        {
            alg.InitPop();
            best = new S() { Content = VectorMapPerm(alg.Best.Content) };
            best.Fit = alg.Best.Fit;
        }

        public override void Calculate()
        {
            alg.Calculate();
        }

        public override void Evolve()
        {
            alg.Evolve();
            nfe = alg.Nfe;
            iter++;
            if (alg.Best.Fit < best.Fit)
            {
                best = new S() { Content = VectorMapPerm(alg.Best.Content) };
                best.Fit = alg.Best.Fit;
            }
        }

        public override void Reset()
        {
            alg.Reset();
        }

        public override S Solve(int iters)
        {
            alg.Iters = iters;
            return base.Solve(iters);
        }

        public static PermVector VectorMapPerm(Vector v)
        {
            int n = v.Length, i, j, k;
            PermVector p = new PermVector(n, false);
            double[] ov = v.ToArray();
            Array.Sort(ov);
            for (i = 0; i < n; i++)
                p[i] = Array.IndexOf(ov, v[i]);
            i = 0;
            while (i < n) {
                k = 0;
                for (j = 0; j < n; j++)
                    if (p[j] == i)
                        p[j] += (k++);
                i += k;
            }
            return p;
        }
    }
}
