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

namespace PrequentialAucVerifier
{
    public struct ScoredExample
    {
        public byte Class;
        public byte Score;
        public byte Id;

        public override string ToString()
        {
            return "Id:" + this.Id + " Score:" + this.Score + " Class:" + this.Class;
        }

        public static bool PermuteRanking(ScoredExample[] ranking, bool resetOrder)
        {
            int i, j, k, l = 0;
            byte tmp;
            bool lastPermutation = false;

            j = -1;
            for (i = 0; i < ranking.Length - 1; i++)
            {
                if (ranking[i + 1].Class > ranking[i].Class)
                {
                    j = i;
                }
            }
            if (j == -1)
            {
                lastPermutation = true;
            }

            if (!lastPermutation)
            {
                for (i = j + 1; i < ranking.Length; i++)
                {
                    if (ranking[i].Class > ranking[j].Class)
                    {
                        l = i;
                    }
                }

                tmp = ranking[j].Class;
                ranking[j].Class = ranking[l].Class;
                ranking[l].Class = tmp;
                //reverse j+1 to end
                k = (ranking.Length - 1 - j) / 2; // number of pairs to swap
                for (i = 0; i < k; i++)
                {
                    tmp = ranking[j + 1 + i].Class;
                    ranking[j + 1 + i].Class = ranking[ranking.Length - 1 - i].Class;
                    ranking[ranking.Length - 1 - i].Class = tmp;
                }
            }

            if (resetOrder)
            {
                for (int rIdx = 0; rIdx < ranking.Length; rIdx++)
                {

                    ranking[rIdx].Id = (byte)rIdx;
                }
            }

            return !lastPermutation;
        }

        public static bool PermuteOrder(ScoredExample[] ranking)
        {
            int i, j, k, l = 0;
            byte tmp;
            bool lastPermutation = false;

            j = -1;
            for (i = 0; i < ranking.Length - 1; i++)
            {
                if (ranking[i + 1].Id > ranking[i].Id)
                {
                    j = i;
                }
            }
            if (j == -1)
            {
                lastPermutation = true;
            }

            if (!lastPermutation)
            {
                for (i = j + 1; i < ranking.Length; i++)
                {
                    if (ranking[i].Id > ranking[j].Id)
                    {
                        l = i;
                    }
                }

                tmp = ranking[j].Id;
                ranking[j].Id = ranking[l].Id;
                ranking[l].Id = tmp;
                //reverse j+1 to end
                k = (ranking.Length - 1 - j) / 2; // number of pairs to swap
                for (i = 0; i < k; i++)
                {
                    tmp = ranking[j + 1 + i].Id;
                    ranking[j + 1 + i].Id = ranking[ranking.Length - 1 - i].Id;
                    ranking[ranking.Length - 1 - i].Id = tmp;
                }
            }

            return !lastPermutation;
        }

        public static ScoredExample[] CreatePermutationArray(int n, double majorityClassPercentage)
        {
            ScoredExample[] ranking = new ScoredExample[n];

            for (int i = (int)Math.Round(n * majorityClassPercentage); i < n; i++)
            {
                ranking[i].Class = 1;
            }

            for (byte i = 0; i < n; i++)
            {
                ranking[i].Score = (byte)(n - i);
                ranking[i].Id = (byte)i;
            }

            return ranking;
        }

        public static double CalculateAUC(ScoredExample[] sortedScores)
        {
            double AUC = 0;
            double c = 0;

            int numPos = 0;
            int numNeg = sortedScores.Length;

            for (int i = 0; i < sortedScores.Length; i++)
            {
                if (sortedScores[i].Class == byte.MaxValue)
                {
                    numNeg--;
                }
                else if (sortedScores[i].Class == 1)
                {
                    c++;
                    numPos++;
                }
                else
                {
                    AUC += c;
                }
            }

            numNeg -= numPos;

            if (numPos == 0 || numNeg == 0)
            {
                return 1;
            }
            else
            {
                return AUC / (numPos * numNeg);
            }
        }

        public static double CalculatePrequentialAUC(ScoredExample[] array, ScoredExample[] window)
        {
            double avg = 0;
            double slides = array.Length - window.Length + 1;
            int windowIdx = 0;

            for (int pos = 0; pos < slides; pos++)
            {
                windowIdx = 0;
                for (int i = 0; i < array.Length; i++)
                {
                    if (array[i].Id >= pos && array[i].Id < pos + window.Length)
                    {
                        window[windowIdx].Class = array[i].Class;
                        windowIdx++;
                    }
                }

                avg += CalculateAUC(window);
            }

            return avg / slides;
        }

        public static double CalculateBlockAUC(ScoredExample[] array, ScoredExample[] block)
        {
            double avg = 0;
            double blocks = 0;
            int blockIdx = 0;

            for (int pos = 0; pos < array.Length; pos += block.Length)
            {
                blockIdx = 0;

                for (int i = 0; i < array.Length; i++)
                {
                    if (array[i].Id >= pos && array[i].Id < pos + block.Length)
                    {
                        block[blockIdx].Class = array[i].Class;
                        blockIdx++;
                    }
                }

                for (int i = blockIdx; i < block.Length; i++)
                {
                    block[i].Class = byte.MaxValue;
                }

                avg += CalculateAUC(block);
                blocks += 1;
            }

            return avg / blocks;
        }
    }
}
