using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Drawing;
using Math3;
using System.Runtime.CompilerServices;

namespace Hex_2048
{
    public class classChallenge
    {
        static int _intNumLevels = -1;
        static public int NumLevels
        {
            get { return _intNumLevels; }
            set { _intNumLevels = value; }
        }

        static int _intNumBranches = -1;
        static public int NumBranches
        {
            get { return _intNumBranches; }
            set { _intNumBranches = value; }
        }

        static int intMinutesToSolveChallenge = 1;
        static int MinutesToSolveChallenge
        {
            get { return intMinutesToSolveChallenge; }
            set { intMinutesToSolveChallenge = value;}
        }

        static int _intChallengesSolved = 0;
        public static int intChallengesSolved
        {
            get
            {
                return _intChallengesSolved;
            }
            set
            {
                _intChallengesSolved = value;
            }
        }
 
        
        static int intLivesRemaining_Max = 9;
        static int _intLivesRemaining = 3;
        public static int LivesRemaining
        {
            get
            {
                return _intLivesRemaining;
            }
            set
            {
                if (value > intLivesRemaining_Max)
                    _intLivesRemaining = intLivesRemaining_Max;
                else
                    _intLivesRemaining = value;
            }
        }
 public static int Score { get { return formHex2048.Score; } }

        static public void Challenge_Next()
        {
            lstLevels.Clear();
            lstTiles.Clear();
            int intNumBranches = 1;
            int intDegree = 0; 

            int intOddShift =  classRND.Get_Int(0, int.MaxValue) % 2;     // 0, 1
            int intRnd = 1 + classRND.Get_Int(0, int.MaxValue) % 3;         //  1, 2, 3 

            //intChallengesSolved = 22;
            if (intChallengesSolved < 2)
            {
                intDegree = 0;
                lstLevels.Add(intOddShift + intRnd); 
                intNumBranches = 1;
                MinutesToSolveChallenge = 20;
            }
            else if (intChallengesSolved <15)
            {
                intDegree = 1;
                lstLevels.Add(intOddShift + intRnd); 
                intNumBranches = 1;
                MinutesToSolveChallenge = 10;
            }
            else if (intChallengesSolved <50)
            {
                intDegree = 2;
                lstLevels.Add(1+ intOddShift);  
                lstLevels.Add(3+ intOddShift);  
                intNumBranches = 2;
                MinutesToSolveChallenge = 10;
            }            
            else if (intChallengesSolved <100)
            {
                intDegree = 3;
                lstLevels.Add(1+ intOddShift); 
                lstLevels.Add(3 + intOddShift);
                intNumBranches = 2;
                MinutesToSolveChallenge = 10;
            }
            else if (intChallengesSolved < 200)
            {
                intDegree = 4;
                lstLevels.Add(1);
                lstLevels.Add(3);
                lstLevels.Add(5);
                intNumBranches = 3;
                MinutesToSolveChallenge = 10;
            }            
            else if (intChallengesSolved < 400)
            {
                intDegree = 5;
                lstLevels.Add(2);  // # 4, 8, 16
                lstLevels.Add(4);  // # 2, 4, 8, 16 -> shifted down/up to math
                lstLevels.Add(6);  // #  4, 8, 16
                intNumBranches = 4;
                MinutesToSolveChallenge = 10;
            }    
            else if (intChallengesSolved < 500)
            {
                intDegree = 6;
                lstLevels.Add(intOddShift +3+ intRnd);  // # 4, 8, 16
                lstLevels.Add(intOddShift + 4 + intRnd);  // #  4, 8, 16
                intNumBranches = 4;
                MinutesToSolveChallenge = 10;
            }
            else if (intChallengesSolved < 1000)
            {
                intDegree = 6;
                lstLevels.Add(intOddShift +4+ intRnd);  
                lstLevels.Add(intOddShift +5+ intRnd);  
                intNumBranches = 4;
                MinutesToSolveChallenge = 10;
            }
            else if (intChallengesSolved < 1500)
            {
                intDegree = 6;
                lstLevels.Add(intOddShift + 6 + intRnd);
                lstLevels.Add(intOddShift + 7 + intRnd);
                intNumBranches = 4;

                int intHundredsAboveAThousand = (intChallengesSolved - 1000) / 100;
                int intMaxDeductionTime = 7;// minutes
                if (intHundredsAboveAThousand > intMaxDeductionTime)
                    intHundredsAboveAThousand = intMaxDeductionTime;

                int intRndTimeDelay = 10 - classRND.Get_Int(0, int.MaxValue) % intHundredsAboveAThousand;
                MinutesToSolveChallenge = intRndTimeDelay;
            }
            else 
            {
                intDegree = 6;
                lstLevels.Add(intOddShift +7+ intRnd);  
                lstLevels.Add(intOddShift +8+ intRnd);  
                intNumBranches = 4;

                int intHundredsAboveAThousand = (intChallengesSolved - 1000) / 100;
                int intMaxDeductionTime = 7;// minutes
                if (intHundredsAboveAThousand > intMaxDeductionTime)
                    intHundredsAboveAThousand = intMaxDeductionTime;

                int intRndTimeDelay = 10 - classRND.Get_Int(0, int.MaxValue) % intHundredsAboveAThousand;
                MinutesToSolveChallenge = intRndTimeDelay;
            }

            lstLevelsCopy.Clear();
            lstLevelsCopy.AddRange(lstLevels);

            intNumBranches_Build
                = NumBranches
                = intNumBranches;
            intNumLevels_Build
                = NumLevels
                = lstLevels.Count;
            intNumTries = 0;
            eState = enuChallengeState.building;
            dtChallengeEnd = DateTime.Now.AddSeconds(ChallengeLength);

            ArrTiles = null;
            ptCenter = new Point(-1, -1);

            
            string strMsg = "----created- degree:" + intDegree.ToString();
            for (int intCounter = 0; intCounter < lstLevels.Count; intCounter++)
            {
                int intLevel = lstLevels[intCounter];
                int intNumeral = (int)Math.Pow(2, intLevel-1);
                strMsg += " " + intNumeral.ToString();
            }

        }


        static int intChallengeLength = 10 * 60;
        public static int ChallengeLength
        {
            get { return intChallengeLength; }
            set { intChallengeLength = value; }
        }

        public enum enuChallengeState { idle, building, ready };
        static enuChallengeState _eState = enuChallengeState.idle;
        static Semaphore semState = new Semaphore(1, 1);
        public static enuChallengeState eState
        {
            get { return _eState; }
            set
            {
                if (_eState != value)
                {
                    _eState = value;
                    switch(eState)
                    {
                        case enuChallengeState.idle:
                            {

                            }
                            break;

                        case enuChallengeState.building:
                            {

                            }
                            break;

                        case enuChallengeState.ready:
                            {
                                chrLegCounter = 'A';
                            }
                            break;
                    }
                }
            }
        }
   

        public static Size szGame {  get { return formHex2048.szGame; } }

        public static classTile[,] _ArrTiles = null;
        public static classTile[,] ArrTiles
        {
            get { return _ArrTiles; }
            set { _ArrTiles = value; }
        }

        public static List<classTile> lstTiles = new List<classTile>();
        static Point ptCenter = new Point(-1, -1);


        static int intNumTries = 0;
        static int intNumTries_Max = 64;
        public static void tmr_Tick(object sender, EventArgs e)
        {
            switch(eState)
            {
                case enuChallengeState.idle:
                    {
                        Challenge_Next();
                    }
                    break;

                case enuChallengeState.building:
                    {
                        Challenge_Build();
                    }
                    break;

                //case enuChallengeState.ready:
                //    {
                //        Challenge_TestComplete();
                //    }
                //    break;
            }            
        }

        static Semaphore semArrTile = new Semaphore(1, 1);
        static void Challenge_Build()
        {
            semArrTile.WaitOne();

            try
            {

                intNumTries++; 

                if (ArrTiles == null)
                { // init tiles
                    ArrTiles = new classTile[szGame.Width, szGame.Height];
                    goto quit;
                }
                else if (ptCenter.X < 0)
                { // pick a center 
                    ptCenter.X = classRND.Get_Int(0, szGame.Width);
                    ptCenter.Y = classRND.Get_Int(0, szGame.Height);
                    goto quit;
                }
                else
                {
                    if (lstLevels.Count > 0)
                    {
                        int intLevel = lstLevels[0];
                        lstLevels.RemoveAt(0);
                        Challenge_Build_AddLeg(intLevel);
                    }
                    else if (NumBranches > 0)
                    {
                        int intIndexRnd = classRND.Get_Int(0, int.MaxValue) % lstLevelsCopy.Count;
                        int intLevel = lstLevelsCopy[intIndexRnd];
                        NumBranches -= 1;
                        Challenge_Build_AddLeg(intLevel);
                    }
                    else
                        eState = enuChallengeState.ready;
                }
            }
            catch (Exception)
            {

            }

        quit:
            semArrTile.Release();
        }

        static List<Point> Challenge_Build_getTilesSameValue(Point pt, int intValue)
        {
            List<Point> lstRetVal = new List<Point>();
            
            for (int intX = 0; intX < szGame.Width; intX++)
            {
                for (int intY = 0; intY < szGame.Height; intY++)
                {
                    Point ptNeighbour = new Point(intX, intY);
                    classTile cTileExistant = ArrTiles[ptNeighbour.X, ptNeighbour.Y];
                    if (cTileExistant != null && cTileExistant.Value == intValue)
                    {
                        if (!lstRetVal.Contains(ptNeighbour))
                        lstRetVal.Add(ptNeighbour);
                        // reject it and its neighbours
                        for (int intDirCounter = 0; intDirCounter < 6; intDirCounter++)
                        {
                            Point ptAdj = formHex2048.move(ptNeighbour, intDirCounter);
                            if (!lstRetVal.Contains(ptAdj))
                            if (ptAdj.X >=0 && ptAdj.X < szGame.Width && ptAdj.Y >=0 && ptAdj.Y < szGame.Height)
                            {
                                    lstRetVal.Add(ptAdj);
                            }
                        }
                    }
                }
            }

            return lstRetVal;
        }

        static char chrLegCounter = 'A';
        static void Challenge_Build_AddLeg(int intLevel)
        {
            Point ptStart = ptCenter;
            int intError = 0;
            int intErrorMax = 32;
            int intPointsToFind = 3;
            List<Point> lstRejects = Challenge_Build_getTilesSameValue(ptCenter, intLevel);

            List<classTile> lstNewLimb = new List<classTile>();
            Point ptNew0 = new Point();
            do
            {
                intError++;
                ptNew0 = getEmptyNeighbour(ptStart, lstRejects);
                if (ptNew0.X != ptCenter.X || ptNew0.Y != ptCenter.Y)
                    if (!lstRejects.Contains(ptNew0))
                    {
                        classTile cTileNew = new classTile(ptNew0);
                        bool bolValidNewTile = true;
                        for (int intTileCounter = 0; intTileCounter < lstNewLimb.Count; intTileCounter++)
                        {
                            classTile cTileValid = lstNewLimb[intTileCounter];
                            if (cTileValid.ID.X == cTileNew.ID.X && cTileValid.ID.Y == cTileNew.ID.Y)
                            {
                                bolValidNewTile = false;
                                break;
                            }    
                        }

                        if (bolValidNewTile)
                        {
                            lstNewLimb.Add(cTileNew);

                            cTileNew.intChallenge_GroupID = chrLegCounter;
                            ptStart = ptNew0;
                            intPointsToFind--;
                        }
                    }

            }
            while (intError < intErrorMax && intPointsToFind > 0);

            if (lstNewLimb.Count == 3)
            {
                bool bolValidGroup = true;
                for (int intTileCounter = 1; intTileCounter < lstNewLimb.Count; intTileCounter++)
                {
                    if (lstNewLimb[0].Value != lstNewLimb[intTileCounter].Value)
                    {
                        bolValidGroup = false;
                        break;
                    }
                }
                
                if (bolValidGroup)
                {
                    for (int intTileCounter = 0; intTileCounter < lstNewLimb.Count; intTileCounter++)
                    {
                        classTile cTileNew = lstNewLimb[intTileCounter];
                        Point ptNew = cTileNew.ID;
                        ArrTiles[ptNew.X, ptNew.Y] = cTileNew;
                        ArrTiles[ptNew.X, ptNew.Y].Value = intLevel;
                        ArrTiles[ptNew.X, ptNew.Y].intChallenge_GroupID = chrLegCounter;
                        lstTiles.Add(cTileNew);
                        
                    }
                    chrLegCounter = (char)((int)chrLegCounter + 1);
                    NumBranches -= 1;

                    return;
                }
                else
                {

                }
            }


            //     if (intError >= intErrorMax)
            eState = enuChallengeState.idle;
       
        }

        static Point getEmptyNeighbour(Point ptStart)
        {
            return getEmptyNeighbour(ptStart, new List<Point>());
        }
        static Point getEmptyNeighbour(Point ptStart, List<Point> lstPt_Reject)
        {
            List<int> lstDir = new List<int>();
            for (int i = 0; i < 6; i++)
                lstDir.Add(i);

            do
            {
                int intRnd = classRND.Get_Int(0, lstDir.Count);
                int intDir = lstDir[intRnd];
                lstDir.RemoveAt(intRnd);
                Point ptNew = formHex2048.move(ptStart, intDir);
                if (ptNew.X >= 0 && ptNew.X < szGame.Width)
                {
                    if (ptNew.Y >= 0 && ptNew.Y <= szGame.Height)
                    {
                        if (!(ptNew.X == ptCenter.X && ptNew.Y == ptCenter.Y))
                        {
                            if (ptNew.X >= 0 && ptNew.X < szGame.Width && ptNew.Y >= 0 && ptNew.Y < szGame.Height)
                            {
                                if (ArrTiles[ptNew.X, ptNew.Y] == null)
                                {
                                    for (int intDirCounter = 0; intDirCounter < 6; intDirCounter++)
                                    {
                                        Point ptNeighbour = formHex2048.move(ptNew, intDirCounter);

                                        if (ptNeighbour.X >= 0 && ptNeighbour.X < szGame.Width && ptNeighbour.Y >= 0 && ptNeighbour.Y < szGame.Height)
                                        {
                                            if (!lstPt_Reject.Contains(ptNeighbour))
                                            {
                                                return ptNew;

                                            }

                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } while (lstDir.Count > 0);

            return ptStart;
        }

        static int intAnimationCounter = 0;
        public static bool Highlight
        {
            get { return intAnimationCounter >3; }
        }
        static public void Animate()
        { intAnimationCounter = (intAnimationCounter + 1) % 10; }

        static TimeSpan _tsChallengeTimeRemaining;
        public static TimeSpan tsChallengeTimeRemaining
        {
            get { return _tsChallengeTimeRemaining; }
            set
            {
                _tsChallengeTimeRemaining = value;
            }
        }


        public static void ChallengeTimeRemaining_Set()
        {
            DateTime dtNow = DateTime.Now;
            tsChallengeTimeRemaining = dtChallengeEnd.Subtract(dtNow);

        }
        
        static List<List<classTile>> lstTilesMatching = new List<List<classTile>>();
        public static void Challenge_TestComplete()
        {
            semArrTile.WaitOne();
            try
            {

                lstTilesMatching.Clear();
                ChallengeTimeRemaining_Set();

                if (eState == enuChallengeState.ready && tsChallengeTimeRemaining.TotalMilliseconds < 0)
                {
                    int intASubScore = -2000;
                    Point ptTileSubScore = formHex2048.ptTileClicked;
                    RectangleF recLivesRemaining = formHex2048.recLivesRemaining;
                    Point pt = new Point((int)(recLivesRemaining.Left + recLivesRemaining.Width / 2),
                                         (int)(recLivesRemaining.Top + recLivesRemaining.Height / 2));
                    new classAddScore(intASubScore, pt);

                    eState = enuChallengeState.idle;
                    LivesRemaining -= 1;
                    formHex2048.instance.ExcitementLevel_Temp += 3;
                    if (LivesRemaining <= 0)
                    {
                        LivesRemaining = 0;
                        formHex2048.instance.eFSM = enuHex2048_FSM.Game_Over;
                    }
                    goto quit;
                }


                for (int intRowCounter = 0; intRowCounter < szGame.Height; intRowCounter++)
                {
                    for (int intColumnCounter = 0; intColumnCounter < szGame.Width; intColumnCounter++)
                    {
                        if (ArrTiles != null)
                        {
                            classTile cTile_Challenge = ArrTiles[intColumnCounter, intRowCounter];
                            if (cTile_Challenge != null)
                            {
                                classTile cTile_Board = Board.Tiles[intColumnCounter, intRowCounter];
                                if (cTile_Board.Value == cTile_Challenge.Value)
                                    Challenge_TestComplete_SortMatchFound(ref cTile_Challenge);
                            }
                        }
                    }
                }

                IEnumerable<List<classTile>> qSortTiles = lstTilesMatching.OrderByDescending(tile => tile.Count);
                List<List<classTile>> lstTilesSorted = (List<List<classTile>>)qSortTiles.ToList<List<classTile>>();
                int intTilesRemoved = 0;
                while (lstTilesSorted.Count > 0)
                {
                    List<classTile> lstTilesTemp = lstTilesSorted[0];
                    if (lstTilesTemp.Count == 3)
                    {
                        lstTilesSorted.RemoveAt(0);

                        // this leg is solved 
                        int intAddScore = ScoreValue();
                        Point ptTile = formHex2048.ptTileClicked;
                        new classAddScore(intAddScore, formHex2048.TileCenter(ptCenter));


                        for (int intTileCounter = 0; intTileCounter < lstTilesTemp.Count; intTileCounter++)
                        {
                            classTile cTileRemoveFromChallenge = lstTilesTemp[intTileCounter];
                            if (ArrTiles != null)
                            {
                                ArrTiles[cTileRemoveFromChallenge.ID.X, cTileRemoveFromChallenge.ID.Y] = null;
                                lstTiles.Remove(cTileRemoveFromChallenge);
                                intTilesRemoved++;
                            }
                        }
                    }
                    else
                        goto quit;
                }
                if (lstTiles.Count > 0) goto quit;


                intChallengesSolved++;

                formHex2048.instance.ExcitementLevel_Temp += 1;
                eState = enuChallengeState.idle;
            }
            catch (Exception)
            {

            }

        quit:
            semArrTile.Release();
        }


        static void Challenge_TestComplete_SortMatchFound(ref classTile cTileNew)
        {
            for (int intListCounter = 0; intListCounter < lstTilesMatching.Count; intListCounter++)
            {
                List<classTile> lstTemp = lstTilesMatching[intListCounter];
                if (lstTemp .Count > 0)
                {
                    if (lstTemp[0].intChallenge_GroupID ==cTileNew.intChallenge_GroupID)
                    {
                        lstTemp.Add(cTileNew);
                        return;
                    }
                }
            }
            List<classTile> lstNew = new List<classTile>();
            lstNew.Add(cTileNew);
            lstTilesMatching.Add(lstNew);
        }
        static int ScoreValue()
        {
            int intRetVal = (intNumBranches_Build * intNumLevels_Build ) * 10;
            return intRetVal;
        }
        static List<int> lstLevels = new List<int>();
        static List<int> lstLevelsCopy = new List<int>();
  
        public static int intNumBranches_Build=0, intNumLevels_Build = 0;
        public static DateTime dtChallengeEnd = DateTime.Now;
        static bool bolFirstChallenge = true;
  
    }
}
