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

namespace PlantDemo
{
    public class PlantZombProblem : IntProgProblem<PlantZombSolution>
    {
        public Type[] PlantTypes { get; set; }
        public Type[] ZombieTypes { get; set; }

        int tsun, rows, pCols, pStartCol, pEndCol, zStartCol, zEndCol;
        public int TotalSun { get { return tsun; } }
        public int Rows { get { return rows; } }
        public int PlantStartColumn { get { return pStartCol; } }
        public int PlantEndColumn { get { return pEndCol; } }
        public int ZombStartColumn { get { return zStartCol; } }
        public int ZombEndColumn { get { return zEndCol; } }

        double[,] zOProbs;
        public double[,] ZombOccureProbs { get { return zOProbs; } }

        double[,] pzDams;
        public double[,] PlantZombDamages { get { return pzDams; } }

        double[] pFF;
        public double[] PlantFireFreqs { get { return pFF; } }

        int[] prices;
        public int[] Prices { get { return prices; } }

        public int PenalWeight = 99999;
        protected double upper;

        public PlantZombProblem(int tsun = 2500, int rows = 6, int plantStartCol = 1, int plantEndCol = 4, int zombStartCol = 5, int zombEndCol = 9)
            : base(rows * (plantEndCol - plantStartCol + 1), 0, 7, "Plant VS Zombie") 
        {
            Func = Evaluate;
            this.tsun = tsun;
            this.pStartCol = plantStartCol;
            this.pEndCol = plantEndCol;
            this.zStartCol = zombStartCol;
            this.zEndCol = zombEndCol;
            this.rows = rows;
            this.pCols = pEndCol - pStartCol + 1;
            PlantTypes = new Type[] { null, typeof(PlWDSS), typeof(PlWDSS2), typeof(PlWDSS3), typeof(PlHBSS), typeof(PlXMG), typeof(PlPMG) };
            ZombieTypes = new Type[] { typeof(Zombie), typeof(WireZombie) };
            zOProbs = new double[6, 2] { { 0.67, 0.33 }, { 0.33, 0.67 }, { 0.5, 0.5 }, { 0.5, 0.5 }, { 0.33, 0.67 }, { 0.67, 0.33 } };
            pzDams = new double[7, 2] { { 0, 0 }, { 0.2, 0.1 }, { 0.2, 0.1 }, { 0.2, 0.1 }, { 0.2, 0.1 }, { 0.1, 0.125 }, { 0.125, 0.15 } };
            pFF = new double[] { 0, 1, 2, 1, 1, 0.8, 1 };
            prices = new int[] { 0, 100, 200, 325, 175, 45, 225 };
            upper = 0.2 * pCols * (zombEndCol - zombStartCol + 1);
        }

        public bool bCover(int pRow, int pCol, int plantIndex, int tRow, int tCol)
        {
            if (plantIndex == 0) return false;
            if (pRow == tRow) return true;
            if (plantIndex == 3) //(PlantTypes[plantIndex] == typeof(PlWDSS3))
            {
                int c = tCol - pCol, r = tRow - pRow;
                return (c > 0) && (c == r || c == -r);
            }
            if (plantIndex == 6) //(PlantTypes[plantIndex] == typeof(PlPMG))
            {
                int c = tCol - pCol, r = tRow - pRow;
                return (c == r || c == -r);
            }
            return false;
        }

        public bool[] HBCover(PlantZombSolution sol)
        {
            bool[] hbrs = new bool[rows];
            for (int d = 0; d < dimension; d++)
                if (sol.Content[d] == 4) //(PlantTypes[sol[d]] == typeof(PlHBSS))
                    hbrs[d / pCols] = true;
            return hbrs;
        }

        public double GetZombOccurProb(int row, int col, int zombIndex)
        {
            return zOProbs[row, zombIndex];
        }

        public int ComputeCost(PlantZombSolution sol)
        {
            sol.TCost = sol.Content.Sum(i => prices[i]);
            return sol.TCost;
        }

        public override double Evaluate(PlantZombSolution sol)
        {
            int row, col;
            bool[] hbrs = this.HBCover(sol);
            sol.TDamage = 0;
            for (int i = 0; i < rows; i++)
                for (int j = zStartCol; j <= zEndCol; j++)
                    for (int d = 0; d < dimension; d++)
                    {
                        if (sol.Content[d] == 0) continue;
                        row = d / pCols;
                        col = pStartCol + d % pCols;
                        if (!bCover(row, col, sol.Content[d], i, j)) continue;
                        for (int z = 0; z < ZombieTypes.Length; z++)
                        {
                            sol.TDamage += zOProbs[i, z] * pzDams[sol.Content[d], z] * pFF[sol.Content[d]] * (hbrs[i] ? 1.25 : 1);
                        }
                    }
            double tprice = sol.Content.Sum(i => prices[i]);
            double violation = this.ComputeCost(sol) - tsun;
            sol.Fit = upper - sol.TDamage + ((violation <= 0) ? 0 : PenalWeight * violation);
            return sol.Fit;
        }

        public PlantZombSolution TryImproveSolution(PlantZombSolution sol)
        {
            PlantZombSolution sol1 = sol.Clone();
            int minPi = (new IntVector(prices.Skip(1).ToArray())).IndexofMin() + 1;
            int mp = sol1.Content.IndexofMin();
            while (sol1.Content[mp] == 0 && sol1.TCost + prices[minPi] < tsun)
            {
                sol1.Content[mp] = minPi;
                sol1.TCost += prices[minPi];
                mp = sol1.Content.IndexofMin();
            }
            this.Evaluate(sol1);
            return sol1;
        }
    }
}
