using Math3;
using Microsoft.Win32.SafeHandles;
using NAudio.Wave;
using PerPixelForm;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;


using static Hex_2048.classChallenge;
using static System.Windows.Forms.AxHost;

namespace Hex_2048
{
    public partial class formHex2048 : PerPixelForm.PerPixelAlphaForm
    {
        #region FSM
        enuHex2048_FSM _eFSM = enuHex2048_FSM.idle;
        public enuHex2048_FSM eFSM
        {
            get { return _eFSM; }
            set
            {
                switch (_eFSM)
                { 
                    case enuHex2048_FSM.Tiles_RemoveFinal:
                        {
                            Board.Tiles[ptFinalTileRemoved.X, ptFinalTileRemoved.Y].Value = 0;
                        }
                        break;
                }

                if (classChallenge.LivesRemaining <= 0)
                {
                    switch (value)
                    {
                        case enuHex2048_FSM.Tile_Moving:

                            return;
                    }
                }
                _eFSM = value;

                switch (_eFSM)
                {
                    case enuHex2048_FSM.idle:
                        {
                            intTilesGathered_ThisScore = 0;
                            if (bolQuitWhenIdle)
                                Quit();
                        }
                        break;

                    case enuHex2048_FSM.Tiles_Adding_Init:
                        {
                            bolNewTiles = true;
                            AddRndTiles();
                        }
                        break;

                    case enuHex2048_FSM.Tiles_Adding_Animate:
                        {

                        }
                        break;

                    case enuHex2048_FSM.Tiles_GatherLike:
                        {
                            Tiles_GatherLike();
                        }
                        break;

                    case enuHex2048_FSM.Tiles_GatherLike_Flash:
                        {
                            intCounter_FlashTiles = 12;
                        }
                        break;

                    case enuHex2048_FSM.Tiles_RemoveLike:
                        {
                            Tiles_RemoveLike();
                        }
                        break;

                    case enuHex2048_FSM.Game_New:
                        {
                            Game_New();
                        }
                        break;

                    case enuHex2048_FSM.Game_Over:
                        {
                            //wo_Next = woGameOver;
                            DrawGame_Over();
                        }
                        break;

                    case enuHex2048_FSM.Tiles_RemoveFinal:
                        {

                        }
                        break;

                    case enuHex2048_FSM.Tile_Moving:
                        {
                            MoveSelectedTile();
                        }
                        break;
                         
                    //case enuHex2048_FSM.Form_Resizing:
                    //    {
                    //        //bolIgnoreFormChanges = true;
                    //    }
                    //    break;
                }
            }
        }

        #endregion


        #region Variables
        public static formHex2048 instance = null;


        static classEventController _cEC = null;
        public static classEventController cEC
        {
            get
            {
                if (_cEC == null)
                    _cEC = new classEventController();
                return _cEC;
            }
        }

        Semaphore semFormChanges = new Semaphore(1, 1);
        static Random rnd = new Random();
        static float fltSize_Default = 14;
        ContextMenu cMnu = new ContextMenu();
        static Font fnt = new Font("ms sans-serif", 14, FontStyle.Bold);

        public static string WorkingDirectory = "";
        classEventController.classEventTicket cEventTicket_MainTimer = null;
        classEventController.classEventTicket cEventTicket_ExcitementLevelTemp_Reduce = null;
        public classEventController.classEventTicket cEventTicket_StartMusic = null;

        public static Size szGame = new Size(7, 9);
        public static Size szTile = new Size();
        List<classTile_New> lstTilesNew = new List<classTile_New>();

        List<Point> lstPtMove = new List<Point>();
        public static List<List<Point>> lstPtMoveDir = new List<List<Point>>();

        List<Point> lstTilesGathered = new List<Point>();
        Bitmap bmpFrameSource = Properties.Resources.Frame;
        Bitmap bmpFrameSource_Highlight = Properties.Resources.Frame_Highlight;

        Bitmap[] bmpGame_Over = new Bitmap[2];

        bool bolHighlight = false;
        bool bolMouseOverGrab = false;
        //bool bolMouseOverResize = false;
        bool bolTilesGathered = false;
        bool bolActivated = false;
        bool bolForm_Moving = false;
        bool bolForm_Resizing = false;
        bool bolQuitWhenIdle = false;
        public bool bolIgnoreFormChanges = true;
        public static List<bool> lstFlareTrigger = new List<bool>();

        public static int intHighScore = 1000;

        int intCounter_AddTile = 0;
        int intCounter_FlashTiles = 0;
        int intCounter_GameOver = 0;
        int intCounter_NoPath = 0;
        int intTimerCounter = 0;
        const int intTimerCounterMax = 64;

        static public long lngTimerDelay_Minutes = -1;
        DateTime dt_Start = DateTime.Now;
        TimeSpan elapsedSpan;
        #endregion

        #region dimensions
        static Point ptDrawGameGap = new Point();
        static Point ptFinalTileRemoved = new Point();
        static Point ptGrab = new Point();

        public static PointF ptNewTilesStart = new PointF();
        static RectangleF recResize = new RectangleF();
        static RectangleF recGrab = new RectangleF();
        static RectangleF recTotalTimePlayed = new RectangleF();
        static RectangleF recChallengeTimer = new RectangleF();
        public static RectangleF recLivesRemaining= new RectangleF();
        public static RectangleF recChallengesSolved= new RectangleF();
        static RectangleF recTitle = new RectangleF();
        public static RectangleF recGame = new RectangleF();
        public static RectangleF recScore = new RectangleF();
        public  static PointF ptScoreBoardCenter = new PointF();
        public static RectangleF recFrame = new RectangleF();
        public static SizeF szForm = new SizeF();

        static PointF ptNewTilesStart_default = new PointF(258, 113);
        static RectangleF recResize_default = new RectangleF(3,768,517,6);
        static RectangleF recGrab_default = new RectangleF(207, 12, 105, 19);
        static RectangleF recTotalTimePlayed_default = new RectangleF(526, 153, 140, 30);
        static RectangleF recChallengeTimer_default = new RectangleF(226, 99, 283-236, 119-99);
        public static RectangleF recLivesRemaining_default = new RectangleF(249, 114, 270-249, 135-114);
        public static RectangleF recChallengesSolved_default = new RectangleF(249, 134, 40, 20);
        static RectangleF recTitle_default = new RectangleF(160, 40, 359 - 160, 78 - 40);
        public static RectangleF recGame_default = new RectangleF(69, 164, 453 - 69, 708 - 164);
        static RectangleF recScore_default = new RectangleF(27, 21, 225 - 27, 58 - 21);
        static PointF ptScoreBoardCenter_default = new PointF (259, 747);
        public static RectangleF recFrame_default = new RectangleF(0,0,524, 779);
        public static SizeF szForm_default = new SizeF();


        #region NAudio
 

        static string[] arrSongTitles =
            {
            "Gospel Plow sample 15.wav",
            "Gospel Plow sample 1.wav",
            "Gospel Plow sample 3.wav",
            "Gospel Plow sample 11.wav",
            "Gospel Plow sample 4.wav",
            "Gospel Plow sample 6.wav"
         };

 
        static List<string> _lstSongTitles = null;
        public static List<string> lstSongTitles
        {
            get
            {
                if (_lstSongTitles == null)
                {
                    _lstSongTitles = arrSongTitles.ToList<string>();
                }
                return _lstSongTitles;
            }
        }
        // */

        public static void Song_MaxExcitement()
        {

            Song_Next = SongFileName(lstSongTitles.Count);
        }

        public static void Song_NextExcitement()
        {
            Song_Next = SongFileName(instance.ExcitementLevel + 1);
        }

        public static void Song_MinExcitement()
        {
            Song_Next = SongFileName(0);
        }
        public static string SongFileName(int intIndex)
        {
            string strRetVal = strMusicDirectory;
            if (intIndex < 0)
                intIndex = 0;
            if (intIndex >= lstSongTitles.Count)
                intIndex = lstSongTitles.Count - 1;

            if (intIndex >= 0 && intIndex < arrSongTitles.Length)
            {
                strRetVal += arrSongTitles[intIndex];
            }

            return strRetVal;
        }


        public static string strMusicDirectory = System.IO.Directory.GetCurrentDirectory() + "\\Music\\";

        WaveOut_Mine woChime = null;
        static public WaveOut_Mine woMusicPlayer = null;
        void nAudio_LoopTracks_Create()
        {
            if (woMusicPlayer == null)
            {
                if (woMusicPlayer == null)
                {
                    woMusicPlayer = new WaveOut_Mine("Music Player");
                    string strFilename = SongFileName(0);
                    var initialFile = new AudioFileReader(strFilename);
                    woMusicPlayer.loopStream = new LoopStream(initialFile);

                    // Subscribe to the loop point event
                    woMusicPlayer.TrackLooped += woLoopTrack_PlayState_Stopped;

                    // Start playing
                    woMusicPlayer.Init(woMusicPlayer.loopStream);
                    woMusicPlayer.Play();
                }
            }

            woChime = new WaveOut_Mine("chime");
            string strChimeFilename = System.IO.Directory.GetCurrentDirectory() + "\\Music\\chime.wav";
            WaveFileReader readerChime = new WaveFileReader(strChimeFilename);
            LoopStream loopChime = new LoopStream(readerChime);
            loopChime.EnableLooping = false;
            woChime.Init(loopChime);
            woChime.loopStream = loopChime;

        }

        static string strSongNext = "";
        [DefaultValue(false)]
        static public string Song_Next
        {
            get { return strSongNext; }
            set
            {
                strSongNext = value;
            }
        }

        //void WO_NextSet()
        //{
        //    if (Toggle_Music)
        //    {
        //        Song_Next = SongFileName(ExcitementLevel);
        //    }

        //    if (woMusicPlayer == null)
        //        LoopTracks_PlayNext();
        //}




        void woLoopTrack_PlayState_Stopped(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.Print("woLoopTrack_PlayState_Stopped A - " + DateTime.Now.ToLongTimeString());
            // Check if we need to marshal the call to the UI thread
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(() => woLoopTrack_PlayState_Stopped(sender, e)));
                return;
            }
            System.Diagnostics.Debug.Print("woLoopTrack_PlayState_Stopped B - " + DateTime.Now.ToLongTimeString());


            int intRndChangeTempo = classRND.Get_Int(0, 10);
            if (intRndChangeTempo < LoopTrack_Momentum)
            {
                int intExcitementLevel_Temp_New = ExcitementLevel_Temp;
                while (intExcitementLevel_Temp_New == ExcitementLevel_Temp)
                    intExcitementLevel_Temp_New = classRND.Get_Int(-3, 3);
                ExcitementLevel_Temp = intExcitementLevel_Temp_New;
                LoopTrack_Momentum = 1;
            }
            LoopTracks_PlayNext();
        }
        static int _intLoopTrack_Momentum = 1;
        public static int LoopTrack_Momentum
        {
            get { return _intLoopTrack_Momentum; }
            set
            {
                if (value < 1)
                    value = 1;
                _intLoopTrack_Momentum = value;
            }
        }


        //WaveOut_Mine woChime = null;
        //WaveOut_Mine woGameOver = null;

        //public static List<WaveOut_Mine> lstWOLoopTracks = null;
        //void nAudio_LoopTracks_Create()
        //{
        //        string  strMusicDirectory = System.IO.Directory.GetCurrentDirectory() + "\\music\\";  
        //    if (lstWOLoopTracks == null)
        //    {
        //        lstWOLoopTracks = new List<WaveOut_Mine>();
        //        //woLoopTracks = new WaveOut_Mine[arrSongTitles.Length];

        //        for (int intNumCounter = 0; intNumCounter < arrSongTitles.Length; intNumCounter++)
        //        {
        //            string strFilename = arrSongTitles[intNumCounter];
        //            WaveOut_Mine woNew = new WaveOut_Mine(strFilename);
        //            string strFilePath = strMusicDirectory + strFilename;
        //            if (System.IO.File.Exists(strFilePath))
        //            {
        //                WaveFileReader reader = new WaveFileReader(strFilePath);
        //                LoopStream loop = new LoopStream(reader);
        //                woNew.Init(loop);
        //                woNew.loopStream = loop;
        //                woNew.PlaybackStopped += woLoopTrack_PlayState_Stopped;
        //            }
        //            lstWOLoopTracks.Add(woNew);
        //        }
        //    }

        //    woChime = new WaveOut_Mine("Chime");
        //    string strChimeFilename = strMusicDirectory + "chime.wav";
        //    if (System.IO.File.Exists(strChimeFilename))
        //    {
        //        WaveFileReader readerChime = new WaveFileReader(strChimeFilename);
        //        LoopStream loopChime = new LoopStream(readerChime);
        //        loopChime.EnableLooping = false;
        //        woChime.Init(loopChime);
        //        woChime.loopStream = loopChime;
        //    }

        //    woGameOver = new WaveOut_Mine("GameOver");
        //    string strGameOverFilename = strMusicDirectory + "Gospel Plow End Of Game.wav";
        //    if (System.IO.File.Exists(strGameOverFilename))
        //    {
        //        WaveFileReader readerGameOver = new WaveFileReader(strGameOverFilename);
        //        LoopStream loopGameOver = new LoopStream(readerGameOver);
        //        loopGameOver.EnableLooping = false;
        //        woGameOver.Init(loopGameOver);
        //        woGameOver.loopStream = loopGameOver;
        //    }
        //    wo_Next = lstWOLoopTracks[0];

        //}

        //WaveOut_Mine _wo_Next = null;
        //WaveOut_Mine wo_Next
        //{
        //    get { return _wo_Next; }
        //    set
        //    {
        //        if (eFSM == enuHex2048_FSM.Game_Over )
        //        {
        //            value = woGameOver;
        //        }
        //        _wo_Next = value;
        //        if (wo_Next != null)
        //        {
        //            if (wo_Current == null || woMusicPlayer.PlaybackState == PlaybackState.Stopped)
        //            {
        //                wo_Current = wo_Next;
        //                _wo_Next = null;
        //                woMusicPlayer.loopStream.EnableLooping = wo_Current != woGameOver;
        //                woMusicPlayer.loopStream.Position = 0;
        //                woMusicPlayer.Play();
        //            }
        //            else if (wo_Current != null)
        //            {
        //                woMusicPlayer.loopStream.EnableLooping = false;
        //            }
        //        }
        //    }
        //}


        static bool _bolDebug_Music = false;
        public static bool bolDebug_Music
        {
            get { return _bolDebug_Music; }
            set 
            {
                _bolDebug_Music = value; 
                formDebug_ExcitementLevel.instance.Visible = value; 
            }
        }


        void WO_NextSet()
        {
            if (Toggle_Music)
            {
                Song_Next = SongFileName(ExcitementLevel);
            }

            if (woMusicPlayer == null)
                LoopTracks_PlayNext();
        }


        //void WO_NextSet()
        //{
        //    if (bolDebug_Music)
        //    formDebug_ExcitementLevel.instance.update();

        //    if (Toggle_Music && eFSM != enuHex2048_FSM.Game_Over)
        //    {
        //        wo_Next = lstWOLoopTracks[ExcitementLevel];
        //    }

        //    if (wo_Current == null)
        //        LoopTracks_PlayNext();
        //}

        //WaveOut_Mine _wo_Current = null;
        //public WaveOut_Mine wo_Current
        //{
        //    get { return _wo_Current; }
        //    set { _wo_Current = value; }
        //}
        //void woLoopTrack_PlayState_Stopped(object sender, EventArgs e)
        //{
        //    LoopTracks_PlayNext();
        //}


        float fltVolume = 1f;
        float Volume
        {
            get { return fltVolume; }
            set
            {
                if (fltVolume != value)
                {
                    if (value < 0.1)
                    {
                        Toggle_Music = false;
                        Toggle_SoundEffects = false;
                        value = 0;
                    }
                    else if (value > 1)
                    {
                        value = 1f;
                    }
                    fltVolume = value;
                    if (fltVolume > 0)
                    {
                        Toggle_Music
                            = Toggle_SoundEffects
                            = true;
                    }
                    
                    woMusicPlayer.Volume = Volume;
                }
            }
        }


        bool _bolToggle_Music = true;
        public bool Toggle_Music
        {
            get { return _bolToggle_Music; }
            set
            {
                _bolToggle_Music = value;
                if (Toggle_Music)
                {
                    if (woMusicPlayer != null)
                    {
                        woMusicPlayer.loopStream.Position = 0;
                        woMusicPlayer.Play();
                    }
                    else
                        LoopTracks_PlayNext();
                }
                else
                {
                    if (woMusicPlayer != null)
                    {
                        woMusicPlayer.Stop();
                    }
                }
            }
        }

        bool _bolToggle_SoundEffects = false;
        public bool Toggle_SoundEffects
        {
            get { return _bolToggle_SoundEffects; }
            set { _bolToggle_SoundEffects = value; }
        }


        void LoopTracks_PlayNext()
        {
            if (!bolLaunchMusic) return;
            if (!Toggle_Music) return;

            if (Song_Next.Length > 0)
            {
                woMusicPlayer.wo_Next = Song_Next;
                Song_Next = "";
            }
        }


        //void LoopTracks_PlayNext()
        //{
        //    if (!bolLaunchMusic) return;
        //    if (!Toggle_Music) return;
        //    if (eFSM == enuHex2048_FSM.Game_Over)
        //    {
        //        if (wo_Next == null)
        //        {
        //            woMusicPlayer.Stop();
        //            return;
        //        }
        //    }

        //    if (wo_Next != null)
        //    {
        //        woMusicPlayer.Stop();
        //        wo_Current = wo_Next;
        //        wo_Next = null;
        //        woMusicPlayer.loopStream.EnableLooping = true;// wo_Current != woGameOver;
        //        woMusicPlayer.Play();
        //        //if (bolDebug_Music  && !formDebug_ExcitementLevel.instance.bolLoop)
        //        //    ExcitementLevel_Temp--;
        //    }
        //}

        bool bolLaunchMusic = false;

        void tmrStartMusic_Tick(object sender, EventArgs e)
        {
            bolLaunchMusic = true;
        }
    

        void ExcitementLevel_Set()
        {
            try
            {
                int intMaxLevel = arrSongTitles.Length;

                float fltNumCells = szGame.Width * szGame.Height;
                float fltPercentage = fltNumberOfTokens / fltNumCells;
                /*
                float fltGradient = 1f / (float)(intMaxLevel+1);
                float fltMaxPercentageRequiredForMaxTune = 0.7f;
                int intExcitementLevel_New = (int)(fltPercentage / fltGradient / fltMaxPercentageRequiredForMaxTune);
                /*/
                float fltGradient = 1f / (float)(intMaxLevel*.6);
                float fltMaxPercentageRequiredForMaxTune = 0.7f;
                int intExcitementLevel_New = (int)(fltPercentage / fltGradient / fltMaxPercentageRequiredForMaxTune);
                // */

                if (intExcitementLevel_New >= intExcitementLevel_Base_Max)
                    intExcitementLevel_New = intExcitementLevel_Base_Max - 1;
                if (intExcitementLevel_New < 0)
                    intExcitementLevel_New = 0;

                string strExcitementLevelDebug = "excitement base : "
                                            + fltNumberOfTokens.ToString() + "/" + fltNumCells.ToString()
                                            + "=" + fltPercentage.ToString("0.00")
                                            + " -> " + intExcitementLevel_New.ToString()
                                            + " + Temp:" + _intExcitementLevel_Temp.ToString();

                int intExcitementMusic = intExcitementLevel_New + ExcitementLevel_Temp;
                if (intExcitementMusic >= arrSongTitles.Length)
                    intExcitementMusic = arrSongTitles.Length - 1;
                strExcitementLevelDebug += " \"" + arrSongTitles[intExcitementMusic].Replace(".wav", "") + "\"";

                System.Diagnostics.Debug.Print(strExcitementLevelDebug);

                ExcitementLevel_Base = intExcitementLevel_New;
            }
            catch (Exception)
            {
                ;
            }
        }
    

        int _intExcitementLevel_Temp_Max = -1;
        int intExcitementLevel_Temp_Max
        {
            get
            {
                if (_intExcitementLevel_Temp_Max < 0)
                {
                    _intExcitementLevel_Temp_Max = arrSongTitles.Length;
                }
                return _intExcitementLevel_Temp_Max;
            }
        }

        int _intExcitementLevel_Temp = 0;
        public int ExcitementLevel_Temp
        {
            get { return _intExcitementLevel_Temp; }
            set
            {
                if (value < 0)
                {
                    cEventTicket_ExcitementLevelTemp_Reduce.AutoRepeat = false;
                    return;
                }

                //value = 0; 
                if (value + ExcitementLevel_Base > intExcitementLevel_Temp_Max)
                {
                    value = intExcitementLevel_Base_Max - ExcitementLevel_Base;
                }

                _intExcitementLevel_Temp = value;
                WO_NextSet();

                //if (bolDebug_Music)
                //    formDebug_ExcitementLevel.instance.update();


                cEventTicket_ExcitementLevelTemp_Reduce.AutoRepeat = ExcitementLevel_Temp >0;
            }
        }

        int intExcitementLevel_Base = 0;
        int intExcitementLevel_Base_Max =-1;
        public int ExcitementLevel_Base
        {
            get { return intExcitementLevel_Base; }
            set
            {
                if (intExcitementLevel_Base_Max < 0)
                {
                    intExcitementLevel_Base_Max = arrSongTitles.Length;
                }

                if (value > intExcitementLevel_Base_Max)
                    value = intExcitementLevel_Base_Max;

                if (value < 0)
                    value = 0;
                //value = 0;
                if (intExcitementLevel_Base != value)
                {
                    intExcitementLevel_Base = value;
                    WO_NextSet();
                    //System.Diagnostics.Debug.Print("Excitement Level:" + ExcitementLevel_Base.ToString());
                }
                //if (bolDebug_Music)
                //    formDebug_ExcitementLevel.instance.update();
            }
        }




        public int ExcitementLevel
        {
            get
            {
                int intMax = arrSongTitles.Length - 1;
                int intRetVal = intExcitementLevel_Base + _intExcitementLevel_Temp;
                if (intRetVal > intMax)
                    intRetVal = intMax;
                return intRetVal;
            }
        }


        float fltMaxScore = 1500000;
        #endregion 

        static void DeltaDrawSizeAdjust_RecResize(ref RectangleF rec, RectangleF recDefault, float fltFactor)
        {
            rec.X = recDefault.X * fltFactor ;
            rec.Y = recDefault.Y * fltFactor ;
            rec.Width = recDefault.Width * fltFactor;
            rec.Height = recDefault.Height * fltFactor;
        }

        static void RecSizes_Set()
        {
            szForm.Width = szForm_default.Width * fltDeltaDrawSize;
            szForm.Height = szForm_default.Height * fltDeltaDrawSize;

            DeltaDrawSizeAdjust_RecResize(ref recGrab, recGrab_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recChallengeTimer, recChallengeTimer_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recLivesRemaining, recLivesRemaining_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recChallengesSolved, recChallengesSolved_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recTitle, recTitle_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recGame, recGame_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recScore, recScore_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recResize, recResize_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recFrame, recFrame_default, fltDeltaDrawSize);
            DeltaDrawSizeAdjust_RecResize(ref recTotalTimePlayed, recTotalTimePlayed_default, fltDeltaDrawSize);
            
            ptScoreBoardCenter.X = ptScoreBoardCenter_default.X * fltDeltaDrawSize ;
            ptScoreBoardCenter.Y = ptScoreBoardCenter_default.Y * fltDeltaDrawSize ;

            ptNewTilesStart.X = ptNewTilesStart_default.X * fltDeltaDrawSize;
            ptNewTilesStart.Y = ptNewTilesStart_default.Y * fltDeltaDrawSize;
        }


        static float fltDeltaDrawSize = 1f;
        public static float DeltaDrawSize
        {
            get { return fltDeltaDrawSize; }
            set
            {
                fltDeltaDrawSize = value;
                float fltFontSize = fltSize_Default * fltDeltaDrawSize;
                fnt = new Font(fnt.FontFamily, fltFontSize, fnt.Style);

                RecSizes_Set();

                TileSize_Set();
                Board.Update();
            }
        }


        void szForm_default_Initialize()
        {
            int intWidthSides_Extra = (int)recFrame_default.Width / 2;

            szForm_default.Width = 2 * intWidthSides_Extra + recFrame_default.Width;
            szForm_default.Height = recFrame_default.Height + 1000;

            recFrame_default = new RectangleF(intWidthSides_Extra, 0, recFrame_default.Width, recFrame_default.Height);
            recGame_default = new RectangleF(intWidthSides_Extra + 69, recGame_default.Top, recGame_default.Width, recGame_default.Height);
            recGrab_default = new RectangleF(207 + intWidthSides_Extra, recGrab_default.Top, recGrab_default.Width, recGrab_default.Height);
            recChallengeTimer_default = new RectangleF(235 + intWidthSides_Extra, recChallengeTimer_default.Top, recChallengeTimer_default.Width, recChallengeTimer_default.Height);
            recLivesRemaining_default = new RectangleF(249 + intWidthSides_Extra, recLivesRemaining_default.Top, recLivesRemaining_default.Width, recLivesRemaining_default.Height);
            recChallengesSolved_default = new RectangleF(246 + intWidthSides_Extra, recChallengesSolved_default.Top, recChallengesSolved_default.Width, recChallengesSolved_default.Height);
            ptNewTilesStart_default.X = intWidthSides_Extra + 418;
            recResize_default = new RectangleF(3 + intWidthSides_Extra, recResize_default.Top, recResize_default.Width, recResize_default.Height);
            recTitle_default = new RectangleF(160 + intWidthSides_Extra, recTitle_default.Top, recTitle_default.Width, recTitle_default.Height);
            ptScoreBoardCenter_default.X 
                = ptNewTilesStart_default.X 
                = 259 + intWidthSides_Extra;
        }

        #endregion

        #region Properties
        Bitmap _bmpNoPath = null;
        public Bitmap bmpNoPath
        {
            get { return _bmpNoPath; }
            set
            {
                _bmpNoPath = value;
                intCounter_NoPath = 12;
            }
        }

        Bitmap _bmpForm = null;
        public Bitmap bmpForm
        {
            get 
            {
                return _bmpForm;
            }
            set 
            {
                _bmpForm = value;
                DrawPerPixelForm(_bmpForm);
            }
        }


        void DrawPerPixelForm(Bitmap bmp)
        {
            if (IsDisposed) return;
            SetBitmap(bmp);
        }


        bool _bolNewTiles = false;
        bool bolNewTiles
        {
            get { return _bolNewTiles; }
            set
            {
                if (_bolNewTiles != value)
                {
                    _bolNewTiles = value;
                }
            }
        }

        static int intScore = 0;
        static public int Score
        {
            get { return intScore; }
            set
            {
                intScore = value;
                if (intScore < 0)
                    intScore = 0;
                else if (intScore > intHighScore)
                    intHighScore = intScore;
            }
        }

        Point _ptTileHighlight = new Point(-1, -1);
        Point ptTileHighLight
        {
            get { return _ptTileHighlight; }
            set
            {
                if (_ptTileHighlight.X != value.X || _ptTileHighlight.Y != value.Y)
                {
                    _ptTileHighlight = value;
                }
            }
        }

        classTile cTileMoving = null;
        public classTile TileMoving
        {
            get { return cTileMoving; }
            set
            {
                cTileMoving = value;
            }
        }

        classTile _cTileSelected = null;
        public classTile TileSelected
        {
            get { return _cTileSelected; }
            set
            {
                _cTileSelected = value;
            }
        }

        Point _ptTileSelected = new Point(-1, -1);
        Point ptTileSelected
        {
            get { return _ptTileSelected; }
            set
            {
                _ptTileSelected = value;
                if (TileInBounds(_ptTileSelected))
                    TileSelected = Board.Tiles[ptTileSelected.X, ptTileSelected.Y];
            }
        }

        #endregion
        public classEventController.classEventTicket cEventTicketFlashDelay = null;
        public formHex2048()
        {
            instance
                = Preferences.frmHex
                = this;
            InitializeComponent();
            new formDebug_ExcitementLevel();
            formDebug_ExcitementLevel.instance.Visible = bolDebug_Music;


            Top = Screen.PrimaryScreen.Bounds.Height + 100;

            new formFlash();
            cEventTicketFlashDelay = cEC.Ticket("flash delay", 2000, TmrFlashDelay_Tick);

            nAudio_LoopTracks_Create();

            MouseWheel += FormHex2048_MouseWheel;
            LocationChanged += FormHex2048_LocationChanged;
            SizeChanged += FormHex2048_SizeChanged;
            Activated += FormHex2048_Activated;
            KeyDown += FormHex2048_KeyDown; 
        }

  

        private void FormHex2048_KeyDown(object sender, KeyEventArgs e)
        {
            switch(e.KeyCode)
            {
                case Keys.Escape:
                    bolDrawAbout = false;
                    break;
            }
        }

        private void FormHex2048_MouseWheel(object sender, MouseEventArgs e)
        {

            if (MouseIsOverVolumeControl())
            {
                if (e.Delta > 0)
                    Volume += 0.1f;
                else
                    Volume -= 0.1f;
                return;
            }


            if (e.Delta < 0)
                Size_Shrink();
            else
                Size_Grow();

            Preferences.Save();
        }



        bool _bolFlash_Frame = false;
        public bool Flash_Frame
        {
            get { return _bolFlash_Frame; }
            set { _bolFlash_Frame = value; }
        }



        #region Menus
        private void CMnu_Popup(object sender, EventArgs e)
        {
            cMnu.MenuItems.Clear();

            cMnu.MenuItems.Add(new MenuItem("New Game", mnuGame_New_Click));
            cMnu.MenuItems.Add(new MenuItem("Timer", mnuTimer_click));
            MenuItem mnuMusic = new MenuItem("Music");
            mnuMusic.Checked = Toggle_Music;
            mnuMusic.Click += MnuMusic_Click;
            cMnu.MenuItems.Add(mnuMusic);

            cMnu.MenuItems.Add(new MenuItem("about", mnuAbout_Click));

            cMnu.MenuItems.Add(new MenuItem("Quit", mnuQuit_Click));

            string strDeveloperDirectory = @"C:\Users\chris\OneDrive\Hex 2048\bin\Debug";
            string strLocalDir =  System.IO.Directory.GetCurrentDirectory();
            if (strLocalDir == strDeveloperDirectory)
            {
                cMnu.MenuItems.Add(new MenuItem("reset highscore", mnuResetHighScore_Click));
                cMnu.MenuItems.Add(new MenuItem("Code Updates", mnuCodeUpdates_Click));
                cMnu.MenuItems.Add(new MenuItem("debug music", mnuDebugMusic_Click));
            }
        }

        void mnuDebugMusic_Click(object sender, EventArgs e)
        {
            bolDebug_Music = !bolDebug_Music;
        }   

        void mnuAbout_Click(object sender, EventArgs e)
        {
            bolDrawAbout = !bolDrawAbout;
        }

        private void MnuMusic_Click(object sender, EventArgs e)
        {
            Toggle_Music = !Toggle_Music;
        }

        void mnuCodeUpdates_Click(object sender, EventArgs e)
        {
            CodeUpdates.formCodeUpdateJournal.instance.Show();
        }
        void mnuResetHighScore_Click(object sender, EventArgs e)
        {
            intHighScore = 0;
        }

        void Size_Shrink()
        {
            DeltaDrawSize *= 0.9f;
            DrawGame();
        }

        
        void Size_Grow()
        {
            DeltaDrawSize *= 1.1f;
            DrawGame();
        }
 
        void mnuTimer_click(object sender, EventArgs e)
        {
            formGetTimer frmGetTimer = new formGetTimer();
            if (frmGetTimer.ShowDialog() == DialogResult.OK)
            {
                dt_Start = DateTime.Now;
                lngTimerDelay_Minutes = frmGetTimer.lngDelayMinutes;
            }
        }

        void mnuGame_New_Click(object sender, EventArgs e)
        {   
            
            if (eFSM != enuHex2048_FSM.Game_Over )
            {
                if (MessageBox.Show("are you sure you want to quit this game?", "new game?", MessageBoxButtons.YesNo) != DialogResult.Yes)
                    return;
            }


            Game_New();
        }

        void mnuQuit_Click(object sender, EventArgs e)
        {
            if (eFSM != enuHex2048_FSM.idle)
            {
                bolQuitWhenIdle = true;
                return;
            }

            Quit();
        }
        public void Quit()
        {
            //tmr.Enabled = false;
            cEventTicket_MainTimer.AutoRepeat = false;
            if (bolDebug_Music)
            {
                //formDebug_ExcitementLevel.SongArray_WriteToClipboard();
                //formDebug_ExcitementLevel.instance.StartUpInfo_Save();
                //formDebug_ExcitementLevel.instance.Close();
            }
            Preferences.Save();
            Dispose();
        }
        #endregion

        #region Helper_Functions
        static int DirReverse(int dir) { return (dir + 3) % 6; }

        public static int RND_GetInt(int intMax) { return RND_GetInt(0, intMax); }
        public static int RND_GetInt(int intMin, int intMax)
        {
            int intRange = intMax - intMin;

            int intRND = (int)((double)intRange * 1024 * rnd.NextDouble()) % intRange;
            return intMin + intRND;
        }

        public static Point move(Point pt, int dir)
        {
            if (lstPtMoveDir.Count == 0)
            {
                List<Point> lstEven = new List<Point>();
                {
                    lstEven.Add(new Point(0, -1));
                    lstEven.Add(new Point(1, -1));
                    lstEven.Add(new Point(1, 0));
                    lstEven.Add(new Point(0, 1));
                    lstEven.Add(new Point(-1, 0));
                    lstEven.Add(new Point(-1, -1));
                }
                lstPtMoveDir.Add(lstEven);

                List<Point> lstOdd = new List<Point>();
                {
                    lstOdd.Add(new Point(0, -1));
                    lstOdd.Add(new Point(1, 0));
                    lstOdd.Add(new Point(1, 1));
                    lstOdd.Add(new Point(0, 1));
                    lstOdd.Add(new Point(-1, 1));
                    lstOdd.Add(new Point(-1, 0));
                }
                lstPtMoveDir.Add(lstOdd);
            }

            return new Point(pt.X + lstPtMoveDir[pt.X % 2][dir].X, pt.Y + lstPtMoveDir[pt.X % 2][dir].Y);
        }

        public static int DirFromMove(Point ptStart, Point ptEnd)
        {
            for (int intDir = 0; intDir < 6; intDir ++)
            {
                Point ptMove = move(ptStart, intDir);

                if (ptMove.X == ptEnd.X && ptMove.Y == ptEnd.Y)
                    return intDir;
            }
            MessageBox.Show("fuck!!!");
            return -1;
        }

        #endregion

        #region Events

        byte bytTickCounter = 0;
        
        private void Tmr_Tick(object sender, EventArgs e)
        {
            if (IsDisposed)
            {
                cEventTicket_MainTimer.AutoRepeat = false; 
                return;
            }

            if (CodeUpdates.formCodeUpdateJournal.instance.Visible) return;
            Flash_Frame = (eState == enuChallengeState.ready && tsChallengeTimeRemaining.TotalMilliseconds < 60 * 1000);

            bytTickCounter = (byte)((bytTickCounter + 1) % 20);

            intTimerCounter = (intTimerCounter + 1) % intTimerCounterMax;

            if (bolForm_Moving)
            {
                        Left = (int)(MousePosition.X - ptGrab.X);
                        Top = (int)(MousePosition.Y - ptGrab.Y);

            }
            if (bolForm_Resizing)
            {
                        DeltaDrawSize = (float)(MousePosition.Y - Top + 31 * DeltaDrawSize) / recFrame_default.Height;

            }

            switch (eFSM)
            {
                case enuHex2048_FSM.Tiles_GatherLike:
                    {

                    }
                    break;
                     

                case enuHex2048_FSM.Game_Over:
                    {
                        if (bolQuitWhenIdle)
                            Quit();
                        if (intTimerCounter % 3 == 0)
                            intCounter_GameOver = (intCounter_GameOver + 1) % 2;
                        DrawPerPixelForm(bmpGame_Over[intCounter_GameOver]);
                        if (!IsDisposed)
                            Show();
                        return;
                    }

                case enuHex2048_FSM.Tiles_GatherLike_Flash:
                    {
                        classFlare.Animate();
                    }
                    break;

                case enuHex2048_FSM.Tile_Moving:
                    {    // set next step to value of moving piece
                        Board.Tiles[lstPtMove[0].X, lstPtMove[0].Y].Value = Board.Tiles[ptTileSelected.X, ptTileSelected.Y].Value;

                        // set old value to zero
                        Board.Tiles[ptTileSelected.X, ptTileSelected.Y].Value = 0;

                        int intDirMove = DirFromMove(ptTileSelected, lstPtMove[0]);

                        // point to new tile
                        ptTileSelected = lstPtMove[0];

                        // remove sstep from list
                        lstPtMove.RemoveAt(0);

                        Board.Tiles[ptTileSelected.X, ptTileSelected.Y].Impulse(ptTileSelected, intDirMove);

                        if (lstPtMove.Count == 0)
                        {
                            eFSM = enuHex2048_FSM.Tiles_GatherLike;
                            classChallenge.Challenge_TestComplete();
                        }
                    }
                    break;

                case enuHex2048_FSM.Tiles_Adding_Animate:
                    {
                        if (intTimerCounter % 1 == 0)
                        {
                            if (intCounter_AddTile > 0)
                                AddTiles_Animate();
                            else
                            {
                                classChallenge.Challenge_TestComplete();
                                eFSM = enuHex2048_FSM.Tiles_GatherLike;
                            }
                        }
                    }
                    break;

                case enuHex2048_FSM.Tiles_RemoveFinal:
                    {
                        if (!classStreamer.Animate())
                            eFSM = enuHex2048_FSM.Tiles_GatherLike;
                    }
                    break;

                default:
                    { 
                       if (intTimerCounter % 3 == 0)
                            bolHighlight = !bolHighlight;
                    }
                    break;
            }

            DateTime currentDate = DateTime.Now;

            long elapsedTicks = currentDate.Ticks - dt_Start.Ticks;

            elapsedSpan = new TimeSpan(elapsedTicks);

            if (lngTimerDelay_Minutes >= 0 && elapsedSpan.TotalMinutes > lngTimerDelay_Minutes)
            {
                if (eFSM == enuHex2048_FSM.idle)
                    Quit();
                else
                    bolQuitWhenIdle = true;
            }

            DrawGame();

            classChallenge.tmr_Tick(sender,e);

            ExcitementLevel_Set();
            if (!IsDisposed)
                Show();

        }

        
        private void TmrFlashDelay_Tick(object sender, EventArgs e)
        {
            //tmrFlashDelay.Enabled = false;
            cEventTicketFlashDelay.AutoRepeat = false;
            formFlash.instance.Hide();
            formFlash.instance.Refresh();
            formFlash.instance.Dispose();
            bolIgnoreFormChanges = true;

            if (Size.Width > Screen.PrimaryScreen.Bounds.Width)
            {
                Width = Screen.PrimaryScreen.Bounds.Width;
            }
            if (Size.Height > Screen.PrimaryScreen.Bounds.Height)
            {
                Height = Screen.PrimaryScreen.Bounds.Height;
            }
            Rectangle recLocation = new Rectangle(Preferences.ptLocation_Load, Size);
            if (recLocation.Right > Screen.PrimaryScreen.Bounds.Width)
                recLocation.X = Screen.PrimaryScreen.Bounds.Width - recLocation.Right;
            if (recLocation.Left <0)
                recLocation.X = 0;
            if (recLocation.Top > Screen.PrimaryScreen.Bounds.Height)
                recLocation.Y = Screen.PrimaryScreen.Bounds.Height - recLocation.Height;
            if (recLocation.Top < 0)
                recLocation.Y = 0;

            Location = recLocation.Location;
            Refresh();
            Show();
            Refresh();

            bolIgnoreFormChanges = false;
            //tmr.Enabled = true;
            cEventTicket_MainTimer.AutoRepeat = true;
        }
        private void FormHex2048_Activated(object sender, EventArgs e)
        {
            if (bolActivated) return;
            bolActivated = true;

            recFrame_default = new RectangleF(new Point(0, 0), Properties.Resources.Frame.Size);
            WorkingDirectory = System.IO.Directory.GetCurrentDirectory() + "\\";

            classSparkle.InitFile();
            szForm_default_Initialize();

            Board.Populate();

            DeltaDrawSize = (float)Screen.PrimaryScreen.WorkingArea.Height / recFrame_default.Height;

            bmpFrameSource.MakeTransparent(bmpFrameSource.GetPixel(0, 0));
            bmpFrameSource_Highlight.MakeTransparent(bmpFrameSource_Highlight.GetPixel(0, 0));

            ContextMenu = cMnu;
            cMnu.Popup += CMnu_Popup;
            //cMnu_Build();

            cEventTicket_MainTimer = cEC.Ticket("main timer", 100, Tmr_Tick);
            //tmr.Interval = 100;
            //tmr.Tick += Tmr_Tick;
            cEventTicket_ExcitementLevelTemp_Reduce = cEC.Ticket("excitement level temp reduce", 4000, tmrExcitementLevelTemp_Reduce_Tick);

            cEventTicket_StartMusic = cEC.Ticket("Start Music", 2000, tmrStartMusic_Tick);
            cEventTicket_StartMusic.quit();


            MouseMove += formHex2048_MouseMove;
            MouseDown += FormHex2048_MouseDown;
            MouseUp += FormHex2048_MouseUp;
            MouseClick += formHex2048_MouseClick;
            bolIgnoreFormChanges = true;
            {
                TileSize_Set();
                if (!Preferences.Load())
                    eFSM = enuHex2048_FSM.Game_New;
                else
                {
                    DrawGame();
                    eFSM = enuHex2048_FSM.idle;
                }
                if (!IsDisposed)
                Show();
            }


            cEventTicket_StartMusic.Reschedule();
            bolIgnoreFormChanges = false;
        }

  
        static public DateTime dtStartToday = DateTime.Now;
        static public TimeSpan tsTimeSpan_TotalTimePlayed = TimeSpan.Zero;

        static public TimeSpan tsTimeSpan_Today()
        {
            DateTime dtNow = DateTime.Now;
            TimeSpan tsRetVal = dtNow.Subtract(dtStartToday);
            return tsRetVal;
        }
        static public TimeSpan tsTimeSpan_Total()
        {
            TimeSpan tsRetVal = tsTimeSpan_TotalTimePlayed.Add(tsTimeSpan_Today());
            return tsRetVal;

        }

        private void FormHex2048_LocationChanged(object sender, EventArgs e)

        {
            if (bolIgnoreFormChanges) return;
            semFormChanges.WaitOne();
            Preferences.Save();
            semFormChanges.Release();
        }

        void tmrExcitementLevelTemp_Reduce_Tick(object sender, EventArgs e)
        {
            //    if (formDebug_ExcitementLevel.instance != null && formDebug_ExcitementLevel.instance.bolLoop)
            //        return;

            ExcitementLevel_Temp--;
        }


        private void FormHex2048_SizeChanged(object sender, EventArgs e)
        {
            if (bolIgnoreFormChanges) return;
            semFormChanges.WaitOne();
            TileSize_Set();
            DrawGame();
            if (!bolIgnoreFormChanges)
                Preferences.Save();
            semFormChanges.Release();
        }
        public static Point ptTileClicked = new Point();
        public void formHex2048_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (eFSM == enuHex2048_FSM.idle)
                {
                    Point ptTile
                        = ptTileClicked
                        = Tile_FromPoint(new Point(e.X, e.Y));
                    if (TileInBounds(ptTile))
                    {
                        if (Board.Tiles[ptTile.X, ptTile.Y].Value != 0)
                        {
                            ptTileSelected = ptTile;
                            classTile cTile = Board.Tiles[ptTile.X, ptTile.Y];

                            TileSelected.Radiate((double)cTile.Value / (double)classTile.numColors);
                        }
                        else
                        {
                            if (TileInBounds(ptTileSelected))
                                if (Board.Tiles[ptTileSelected.X, ptTileSelected.Y].Value != 0)
                                {
                                    TileMoving = Board.Tiles[ptTileSelected.X, ptTile.Y];
                                    eFSM = enuHex2048_FSM.Tile_Moving;
                                }
                        }
                    }
                }
            }
        }

     public  void formHex2048_MouseMove(object sender, MouseEventArgs e)
        {
            ptMouseOnForm = new Point(e.X, e.Y);

            bolMouseOverGrab = PointOverGrab(ptMouseOnForm);
            //bolMouseOverResize = PointOverResize(ptMouse);
            ptTileHighLight = Tile_FromPoint(ptMouseOnForm);
        }

        Point _ptMouseOnForme = new Point();
        Point ptMouseOnForm
        {
            get { return _ptMouseOnForme; }
            set { _ptMouseOnForme =value; }
        }


        public void FormHex2048_MouseUp(object sender, MouseEventArgs e)
        {
            if (bolForm_Moving)
            {
                bolForm_Moving = false;
                return;
            }
            if (bolForm_Resizing)
            {
                bolForm_Resizing = false;
                return;
            }

            //if (eFSM == enuHex2048_FSM.Form_Resizing)
            //    eFSM = enuHex2048_FSM.idle;
            //else
                formHex2048_MouseClick(sender, e);


            //eFSM = enuHex2048_FSM.Tiles_GatherLike;
        }


        bool PointOverResize(PointF pt) 
        {
            return Math3.classMath3.PointIsInsideARectangle(pt, recResize); 
        }
        bool PointOverGrab(PointF pt) { return Math3.classMath3.PointIsInsideARectangle(pt, recGrab); }

        public void FormHex2048_MouseDown(object sender, MouseEventArgs e)
        {
            PointF ptMouse = new Point(e.X, e.Y);
            if (MouseButtons == MouseButtons.Left && bolMouseOverGrab)
            {
                bolForm_Moving = true;
                ptGrab = new Point((int)ptMouse.X, (int)ptMouse.Y);
                return;
            }

            //if (MouseButtons == MouseButtons.Left && bolMouseOverResize)
            //{
            //    bolForm_Resizing = true;
            //    //eFSM = enuHex2048_FSM.Form_Resizing;
            //    return;
            //}

        }

        #endregion

        #region MoveSelectedTiles
        List<Point> MoveSelectedTile_BFS()
        {
            Point ptDestination = ptTileHighLight;
            Point ptStart = ptTileSelected;

            int[,] intSeen = new int[szGame.Width, szGame.Height];
            // init intSeen
            for (int intX = 0; intX < szGame.Width; intX++)
                for (int intY = 0; intY < szGame.Height; intY++)
                    intSeen[intX, intY] = -1;

            List<Point> lstQ = new List<Point>();
            lstQ.Add(ptStart);
            intSeen[ptStart.X, ptStart.Y] = 0;
            int intStepsTaken = 0;
            while (lstQ.Count > 0)
            {
                Point ptTile = lstQ[0];
                lstQ.RemoveAt(0);
                intStepsTaken = intSeen[ptTile.X, ptTile.Y];

                for (int intDirCounter = 0; intDirCounter < 6; intDirCounter++)
                {
                    Point ptNeaghbour = move(ptTile, intDirCounter);

                    if (TileInBounds(ptNeaghbour))
                    {
                        if (intSeen[ptNeaghbour.X, ptNeaghbour.Y] < 0)
                        {  // BFS has not seen neaghbour
                            if (Board.Tiles[ptNeaghbour.X, ptNeaghbour.Y].Value == 0)
                            { // neaghbour is not occupied by a colored tile
                                intSeen[ptNeaghbour.X, ptNeaghbour.Y] = intStepsTaken + 1;

                                if (ptNeaghbour.X == ptDestination.X && ptNeaghbour.Y == ptDestination.Y)
                                {  // we have found a path to the destination
                                    lstQ.Clear();
                                    goto validPath;
                                }
                                else
                                {
                                    lstQ.Add(ptNeaghbour);
                                }
                            }

                        }
                    }
                }
            }

            return new List<Point>();

        validPath:
            intStepsTaken = intSeen[ptDestination.X, ptDestination.Y];

            List<Point> lstSteps = new List<Point>();
            lstSteps.Add(ptDestination);
            Point ptCurrent = ptDestination;
            while (!(ptCurrent.X == ptStart.X && ptCurrent.Y == ptStart.Y))
            {
                for (int intDirCounter = 0; intDirCounter < 6; intDirCounter++)
                {
                    Point ptNeaghbour = move(ptCurrent, intDirCounter);
                    if (TileInBounds(ptNeaghbour))
                    {
                        if (intSeen[ptNeaghbour.X, ptNeaghbour.Y] == intStepsTaken - 1)
                        {
                            lstSteps.Add(ptNeaghbour);
                            ptCurrent = ptNeaghbour;
                            intStepsTaken--;
                            break;
                        }
                    }
                }
            }

            lstSteps.Reverse();
            lstSteps.RemoveAt(0);

            return lstSteps;
        }
        void MoveSelectedTile()
        {
            lstPtMove = MoveSelectedTile_BFS();
            if (lstPtMove.Count <= 0)
            {
                bmpNoPath_Draw();
                eFSM = enuHex2048_FSM.idle;
            }
        }

        void bmpNoPath_Draw()
        {
            /*
            Point ptDestination =TileCenter( ptTileHighLight);
            Point ptStart = TileCenter( ptTileSelected);
            /*/
            Point ptRecGameTL= new Point((int)recGame.Left, (int)recGame.Top);
            Point ptStart = classMath3.SubTwoPoints(TileCenter( ptTileSelected), ptRecGameTL);
            Point ptDestination = classMath3.SubTwoPoints(TileCenter(ptTileHighLight), ptRecGameTL);
            //*/
            Bitmap bmpTemp = new Bitmap((int)recGame.Width, (int)recGame.Height);
            Color clrTransparent = Color.DarkRed;
            using (Graphics g = Graphics.FromImage(bmpTemp))
            {
                g.FillRectangle(new SolidBrush(clrTransparent), new RectangleF(0, 0, bmpTemp.Width, bmpTemp.Height));
                Pen pLine = new Pen(Color.LightGray, 10);
                g.DrawLine(pLine, ptStart, ptDestination);

                classRadialCoordinate cRad = new classRadialCoordinate(ptStart, ptDestination);
                cRad.Magnitude = szTile.Width * 1.2;
                cRad.Radians += (2.0 * Math.PI)/3.0;
                Point ptArrow1 = (classMath3.AddTwoPoints(cRad.toPoint(), ptDestination));
                g.DrawLine(pLine, ptDestination, ptArrow1);

                cRad.Radians += (2.0 * Math.PI)/3.0;
                Point ptArrow2 = (classMath3.AddTwoPoints(cRad.toPoint(), ptDestination));
                g.DrawLine(pLine, ptDestination, ptArrow2);

                string strNoPath = "No Path";
                Font fntNoPath = new Font(fnt.FontFamily.Name, fnt.Size * 3);
                Size szText = TextRenderer.MeasureText(strNoPath, fntNoPath);
                Point ptTextCenter = new Point (bmpTemp.Width/2, bmpTemp.Height/2);
                Point ptTextTL = new Point(ptTextCenter.X - szText.Width / 2,
                                           ptTextCenter.Y - szText.Height / 2);
                if (ptTextTL.X < 0)
                    ptTextTL.X = 0;
                if (ptTextTL.X + szText.Width > bmpTemp.Width)
                    ptTextTL.X = bmpTemp.Width - szText.Width;

                if (ptTextTL.Y < 0)
                    ptTextTL.Y = 0;
                if (ptTextTL.Y + szText.Height > bmpTemp.Height)
                    ptTextTL.Y = bmpTemp.Height - szText.Height;

                int intShift = 2;
                g.DrawString(strNoPath, fntNoPath, Brushes.Black, new Point(ptTextTL.X - intShift, ptTextTL.Y - intShift));
                g.DrawString(strNoPath, fntNoPath, Brushes.Red, ptTextTL);
                g.DrawString(strNoPath, fntNoPath, Brushes.Yellow, new Point(ptTextTL.X  + intShift, ptTextTL.Y + intShift));

            }

            bmpTemp.MakeTransparent(clrTransparent);
            bmpNoPath = bmpTemp;
        }

        #endregion

        #region AddNewTiles
        void AddTiles_Animate()
        {
            for (int intTileCounter = 0; intTileCounter < lstTilesNew.Count; intTileCounter++)
                lstTilesNew[intTileCounter].Move();
            intCounter_AddTile--;
            if (intCounter_AddTile <= 0)
            {
                for (int intTileCounter = 0; intTileCounter < lstTilesNew.Count; intTileCounter++)
                {
                    classTile_New cTile = lstTilesNew[intTileCounter];
                    Board.Tiles[cTile.pt.X, cTile.pt.Y].Value = cTile.Value;
                }
            }
        }

        void AddRndTiles()
        {
            int intNumNewTiles =  2 + RND_GetInt(2);
            List<Point> lstEmptyTiles = EmptyTiles();

            if (intNumNewTiles > lstEmptyTiles.Count)
                intNumNewTiles = lstEmptyTiles.Count;
            lstTilesNew.Clear();

            do
            {
                int intNew = RND_GetInt(lstEmptyTiles.Count);
                classTile_New cTile = new classTile_New(lstEmptyTiles[intNew]);
                lstEmptyTiles.RemoveAt(intNew);
                lstTilesNew.Add(cTile);
                intNumNewTiles--;
            } while (intNumNewTiles > 0);
            intCounter_AddTile = classTile_New.intNumSteps;
            eFSM = enuHex2048_FSM.Tiles_Adding_Animate;
        }

        #endregion

        #region Game
        void Game_New()
        {
            Board.Populate();
            bool[] arrBool = new bool[classTile.numColors];
            lstFlareTrigger.Clear();
            lstFlareTrigger.AddRange(arrBool.ToList<bool>());
            classChallenge.intChallengesSolved = 0;
            LivesRemaining = 3;
            classChallenge.eState = classChallenge.enuChallengeState.idle;
            DrawGame();
            tsTimeSpan_TotalTimePlayed = new TimeSpan();
            dtStartToday = DateTime.Now;
            Score = 0;
            eFSM = enuHex2048_FSM.Tiles_Adding_Init;
        }
        void DrawGame_Over()
        {
            DrawGame();
            Bitmap bmpGameMap = new Bitmap(bmpForm);

            Color[] clrGame_Over = { Color.Black, Color.Red };

            string strGame_Over = "Game Over";
            Font fntGame_Over = new Font("Ms sans-serif", 32);
            Size szGame_Over = TextRenderer.MeasureText(strGame_Over, fntGame_Over);

            // determine the region of the game bitmap which draws the game map

            double dblARGame_Over = (double)szGame_Over.Height / (double)szGame_Over.Width;
            Size szDraw = new Size((int)recGame.Width, (int)(recGame.Width * dblARGame_Over));
            Rectangle recDraw = new Rectangle(new Point((int)(recGame.Left + (recGame.Width - szDraw.Width) / 2),
                                                        (int)((recGame.Height - szDraw.Height) / 2)), szDraw);

            for (int intDrawGame_Over = 0; intDrawGame_Over <= bmpGame_Over.GetUpperBound(0); intDrawGame_Over++)
            {
                // draw text on separate bitmap  in the difference colors specified by clrGame_Over array
                Bitmap bmpText = new Bitmap(szGame_Over.Width, szGame_Over.Height);
                Color clrTransparent = Color.Purple;
                using (Graphics g = Graphics.FromImage(bmpText))
                {
                    g.FillRectangle(new SolidBrush(clrTransparent), new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                    g.DrawString(strGame_Over, fntGame_Over, new SolidBrush(clrGame_Over[intDrawGame_Over]), new Point());
                }
                bmpText.MakeTransparent(clrTransparent);

                // create a new bitmap copy of the game screen
                Bitmap bmpTemp = new Bitmap(bmpGameMap);

                // draw text over game screen
                using (Graphics g = Graphics.FromImage(bmpTemp))
                    g.DrawImage(bmpText, recDraw, new Rectangle(0, 0, bmpText.Width, bmpText.Height), GraphicsUnit.Pixel);

                bmpGame_Over[intDrawGame_Over] = bmpTemp;
            }
        }
        #endregion 

        #region Tiles
        static void TileSize_Set()
        {
            int intTileSize = (int)(recGame.Width/ (szGame.Width));
            szTile = new Size(intTileSize, intTileSize);

            ptDrawGameGap = new Point((int)(recGame.Width - szGame.Width * intTileSize) / 2,
                                      (int)(recGame.Height - szGame.Height * intTileSize) / 2);
        }

        public static bool TileInBounds(Point pt)
        {
            return pt.X >= 0 && pt.X < szGame.Width && pt.Y >= 0 && pt.Y < szGame.Height;
        }
        List<Point> EmptyTiles()
        {
            List<Point> lstRetVal = new List<Point>();
            for (int intX = 0; intX < szGame.Width; intX++)
                for (int intY = 0; intY < szGame.Height; intY++)
                    if (Board.Tiles[intX, intY].Value == 0)
                        lstRetVal.Add(new Point(intX, intY));
            return lstRetVal;
        }

        public static Point TileTL(Point ptTile)
        {
            int intX = (int)recGame.Left + ptDrawGameGap.X
                            + ptTile.X * szTile.Width;
            int intY = (int)recGame.Top + ptDrawGameGap.Y
                            + ptTile.Y * szTile.Width
                            + (ptTile.X % 2 == 1 ? szTile.Width / 2 : 0);

            return new Point(intX, intY);
        }

        public static Point TileCenter(Point ptTile)
        {
            Point ptTL = TileTL(ptTile);

            return new Point(ptTL.X + szTile.Width / 2, ptTL.Y + szTile.Height / 2);
        }

        public Point Tile_FromPoint(Point pt)
        {
            int intX = (pt.X + szTile.Width - (int)recGame.Left - ptDrawGameGap.X) / szTile.Width - 1;
            int intY = ((pt.Y + szTile.Height - (int)recGame.Top - ptDrawGameGap.Y) - (intX % 2 == 1 ? szTile.Width / 2 : 0)) / szTile.Width - 1;

            if (intX >= szGame.Width)
                intX = -1;
            if (intY >= szGame.Height)
                intY = -1;

            return new Point(intX, intY);
        }

     
        #endregion

        #region Draw

        void Highlight(ref Graphics g, Point ptTile, bool bolHighlight)
        {
            if (!TileInBounds(ptTile)) return;

            classTile cTile = Board.Tiles[ptTile.X, ptTile.Y];

            switch (eFSM)
            {
                case enuHex2048_FSM.Tiles_RemoveFinal:
                    {

                    }
                    break;

                case enuHex2048_FSM.Tiles_GatherLike_Flash:
                    {
                        if (!bolHighlight)
                        {
                            g.FillEllipse(new SolidBrush(classTile.clrArray[0]), new RectangleF(cTile.ptfTL, szTile));
                        }
                    }
                    break;

                default:
                    {
                        Color clr = classTile.clrArray[0];

                        if (bolHighlight)
                        {
                            if ((ptTile.X == ptTileSelected.X && ptTile.Y == ptTileSelected.Y) || eFSM == enuHex2048_FSM.Tiles_GatherLike_Flash)
                                clr = Color.Black;
                            else
                                clr = Color.DarkGray;
                        }
                        else
                            clr = classTile.clrArray[0];

                        g.DrawEllipse(new Pen(clr, 2), new RectangleF(cTile.ptfTL, szTile));
                    }
                    break;
            }
        }


        void DrawGame()
        {
            Bitmap bmpTemp = new Bitmap((int)szForm.Width, (int)szForm.Height);

            Color clrTransparent = Color.Azure;
            Graphics g = Graphics.FromImage(bmpTemp);
            {
                g.FillRectangle(new SolidBrush(clrTransparent), new RectangleF(new Point(), new Size(bmpTemp.Width, bmpTemp.Height)));
                DrawGameBoard(ref g); 
                
                switch (eFSM)
                {
                    case enuHex2048_FSM.Tiles_GatherLike_Flash:
                        {
                            if (intTimerCounter % 3 == 0)
                                bolHighlight = !bolHighlight;
                            for (int intTileCounter = 0; intTileCounter < lstTilesGathered.Count; intTileCounter++)
                                Highlight(ref g, lstTilesGathered[intTileCounter], bolHighlight);
                            intCounter_FlashTiles--;
                            if (intCounter_FlashTiles <= 0)
                                eFSM = enuHex2048_FSM.Tiles_RemoveLike;
                        }
                        break;
                }

                Highlight(ref g, ptTileHighLight, bolHighlight);
                Highlight(ref g, ptTileSelected, bolHighlight);

                Rectangle recSource = new Rectangle(0, 0, bmpFrameSource.Width, bmpFrameSource.Height);
                RectangleF recDestination = recFrame;

                if (Flash_Frame && bytTickCounter < 10)
                {
                    g.DrawImage(bmpFrameSource_Highlight, recDestination, recSource, GraphicsUnit.Pixel);
                }
                else
                {
                    g.DrawImage(bmpFrameSource, recDestination, recSource, GraphicsUnit.Pixel);
                }


                if (bolMouseOverGrab)
                    g.FillRectangle(Brushes.Gray, recGrab);

                if (intCounter_AddTile > 0)
                    DrawAddNewTiles(ref g);
                DrawScore(ref g);
                DrawHighScore(ref g);
                DrawNoPath(ref g);
                DrawTimer(ref g);
                DrawTotalTimePlayed(ref g);
                DrawChallengeTimer(ref g);

                DrawLivesRemaining(ref g);
                DrawChallengesSolved(ref g);
                DrawVolumeControl(ref g);
                classFlare.Animate();
                classSparkle.Animate(ref g);
                classAddScore.Animate(ref g);

                if (bolDrawAbout)
                {
                    float fltARAbout = (float)bmpAbout.Height / (float)bmpAbout.Width;
                    int intAbout_Width =(int)( recFrame.Width / 2);
                    int intAbout_Height = (int)(intAbout_Width * fltARAbout);
                    Point ptFrameCenter = new Point((int)recFrame.Left + (int)(recFrame.Width / 2),(int)recFrame.Top + (int)(recFrame.Height / 2));

                    Rectangle recDestAbout = new Rectangle(ptFrameCenter.X - intAbout_Width / 2, ptFrameCenter.Y - intAbout_Height / 2, intAbout_Width, intAbout_Height);
                    Rectangle recSourceAbout = new Rectangle(0, 0, bmpAbout.Width, bmpAbout.Height);
                    g.DrawImage(bmpAbout, recDestAbout, recSourceAbout, GraphicsUnit.Pixel);
                }


            }
            g.Dispose();

            bmpTemp.MakeTransparent(clrTransparent);
            bmpForm = bmpTemp;
        }
        bool bolDrawAbout = false;

        Bitmap _bmpAbout = null;
        Bitmap bmpAbout
        {
            get
            {
                if (_bmpAbout == null)
                {
                    _bmpAbout = new Bitmap(Properties.Resources.Hex_2048_about); 
                }
                return _bmpAbout;
            }
        }

        void DrawNoPath(ref Graphics g)
        {
            intCounter_NoPath--;
            if (intCounter_NoPath < 0) return;

            Rectangle recSource = new Rectangle(0, 0, bmpNoPath.Width, bmpNoPath.Height);
            g.DrawImage(bmpNoPath, recGame, recSource, GraphicsUnit.Pixel);

        }


        void DrawHighScore(ref Graphics g)
        {
            string strHighScore = intTimerCounter > intTimerCounterMax / 2
                                                  ? Score_Format( intHighScore) 
                                                  : "High Score";
            Size szScore = TextRenderer.MeasureText(strHighScore, fnt);

            double dblAR_Board = (double)recTitle.Height / (double)recTitle.Width;
            double dblAR_Text = (double)szScore.Height / (double)szScore.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recTitle.Height / dblAR_Text);
                szDraw.Height = (int)recTitle.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recTitle.Width * dblAR_Text);
                szDraw.Width = (int)recTitle.Width;
            }

            Bitmap bmpText = new Bitmap(szScore.Width, szScore.Height);
            SolidBrush sbrBackground = new SolidBrush(Score == intHighScore
                                                                ? Color.Yellow
                                                                : Color.Black);
            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(sbrBackground,
                                    new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strHighScore,
                                 fnt,
                                 new SolidBrush(Score == intHighScore
                                                          ? Color.Black
                                                          : Color.Yellow),
                                 new Point());
            }

            Point ptDraw = new Point((int)(recTitle.Left + (recTitle.Width - szDraw.Width) / 2),
                                     (int)(recTitle.Top + (recTitle.Height - szDraw.Height) / 2));

            Rectangle recDest = new Rectangle(ptDraw, szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            g.FillRectangle(sbrBackground, recTitle);
            g.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);

        }
   
        void DrawLivesRemaining(ref Graphics g)
        {
            string strLivesRemaining = LivesRemaining.ToString();
            Size szLivesRemaining = TextRenderer.MeasureText(strLivesRemaining, fnt);

            double dblAR_Board = (double)recLivesRemaining.Height / (double)recLivesRemaining.Width;
            double dblAR_Text = (double)szLivesRemaining.Height / (double)szLivesRemaining.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recLivesRemaining.Height / dblAR_Text);
                szDraw.Height = (int)recLivesRemaining.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recLivesRemaining.Width * dblAR_Text);
                szDraw.Width = (int)recLivesRemaining.Width;
            }

            Bitmap bmpText = new Bitmap(szLivesRemaining.Width, szLivesRemaining.Height);
            Color clrTransparent = Color.Purple;
            SolidBrush sbrBackground = new SolidBrush(clrTransparent);
            SolidBrush sbrForeground = null;
            if (LivesRemaining  > 3)
            {
                sbrForeground = new SolidBrush(Color.Green);
            }
            else if (LivesRemaining >2)
            {
                sbrForeground = new SolidBrush(Color.Blue);
            }
            else if (LivesRemaining > 1)
            {
                sbrForeground = new SolidBrush(Color.Black);
            }
            else
            {
                sbrForeground = new SolidBrush(Color.Red);
            }


            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(sbrBackground,
                                    new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strLivesRemaining,
                                 fnt,
                                 sbrForeground,
                                 new Point());
            }
            bmpText.MakeTransparent(clrTransparent);

            Point ptDraw = new Point((int)(recLivesRemaining.Left + (recLivesRemaining.Width - szDraw.Width) / 2),
                                     (int)(recLivesRemaining.Top + (recLivesRemaining.Height - szDraw.Height) / 2));

            Rectangle recDest = new Rectangle(ptDraw, szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            g.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);
        }
   
        void DrawChallengesSolved(ref Graphics g)
        {
            string strChallengesSolved = classChallenge.intChallengesSolved.ToString();
            Size szChallengesSolved = TextRenderer.MeasureText(strChallengesSolved, fnt);

            double dblAR_Board = (double)recChallengesSolved.Height / (double)recChallengesSolved.Width;
            double dblAR_Text = (double)szChallengesSolved.Height / (double)szChallengesSolved.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recChallengesSolved.Height / dblAR_Text);
                szDraw.Height = (int)recChallengesSolved.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recChallengesSolved.Width * dblAR_Text);
                szDraw.Width = (int)recChallengesSolved.Width;
            }

            Bitmap bmpText = new Bitmap(szChallengesSolved.Width, szChallengesSolved.Height);
            Color clrTransparent = Color.Purple;
            SolidBrush sbrTransparent = new SolidBrush(clrTransparent);
            SolidBrush sbrForeground = new SolidBrush(Color.DarkGray);
            SolidBrush sbrBackground = new SolidBrush(Color.LightGray);

            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(sbrTransparent,
                                    new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.FillEllipse(sbrBackground, new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strChallengesSolved,
                                 fnt,
                                 sbrForeground,
                                 new Point());
            }
            bmpText.MakeTransparent(clrTransparent);
            //bmpText.Save(@"c:\debug\bmpText.png");

            Point ptDraw = new Point((int)(recChallengesSolved.Left + (recChallengesSolved.Width - szDraw.Width) / 2),
                                     (int)(recChallengesSolved.Top + (recChallengesSolved.Height - szDraw.Height) / 2));

            Rectangle recDest = new Rectangle(ptDraw, szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            //g.FillRectangle(sbrBackground, recChallengesSolved);
            g.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);
        //    bmpText.Save(@"c:\debug\bmpChallengecount.png");

        }

        Rectangle recVolumeControl = new Rectangle();
        void DrawVolumeControl(ref Graphics g)
        {

            Size szVolumeControl = new Size((int)recChallengeTimer.Height,(int) recChallengeTimer.Height);
            Point ptVolumeControl = new Point((int)recChallengeTimer.Right, (int)recChallengeTimer.Top);
            recVolumeControl = new Rectangle(ptVolumeControl, szVolumeControl);

            string strDebug = "ptMouseOnForm(" + ptMouseOnForm.X.ToString() + "," + ptMouseOnForm.Y.ToString() + ") recVolume(" + recVolumeControl.Left.ToString() + ", " + recVolumeControl.Top.ToString() + "-(" + recVolumeControl.Right.ToString() + ", " + recVolumeControl.Bottom.ToString() + ")";
            //System.Diagnostics.Debug.Print(strDebug);
            if (MouseIsOverVolumeControl())
            {
                Bitmap bmpOutput = null;
                if (Toggle_Music)
                {
                    Bitmap bmpVolumeRef = bmpVolumeControl;
                    Bitmap bmpVolumeHighlightRef = bmpVolumeControl_highlight;
                    bmpOutput = VolumeControl_BitmapOutput(ref bmpVolumeRef, ref bmpVolumeHighlightRef);
                }
                else
                {
                    Bitmap bmpVolumeMutedRef = bmpVolumeControl_Muted;
                    Bitmap bmpVolumeHighlightMutedRef = bmpVolumeControl_highlight_Muted;
                    bmpOutput = VolumeControl_BitmapOutput(ref bmpVolumeMutedRef, ref bmpVolumeHighlightMutedRef);
                }
                if (bmpOutput != null)
                {
                    Rectangle recSource = new Rectangle(0, 0, bmpOutput.Width, bmpOutput.Height);
                    g.DrawImage(bmpOutput, recVolumeControl, recSource, GraphicsUnit.Pixel);
                }
            }
        }

        Bitmap _bmpVolumeControl = null;
        public Bitmap bmpVolumeControl
        {
            get
            {
                if (_bmpVolumeControl == null)
                {
                    _bmpVolumeControl = (Bitmap)new Bitmap(Properties.Resources.Volume_Control);
                    _bmpVolumeControl.MakeTransparent(_bmpVolumeControl.GetPixel(0, 0));
                }

                return _bmpVolumeControl;
            }
        }

        Bitmap _bmpVolumeControl_Muted = null;
        public Bitmap bmpVolumeControl_Muted
        {
            get
            {
                if (_bmpVolumeControl_Muted == null)
                {
                    _bmpVolumeControl_Muted = (Bitmap)new Bitmap(Properties.Resources.Volume_Control_muted);
                    _bmpVolumeControl_Muted.MakeTransparent(_bmpVolumeControl_Muted.GetPixel(0, 0));
                }

                return _bmpVolumeControl_Muted;
            }
        }
        Bitmap _bmpVolumeControl_highlight = null;
        public Bitmap bmpVolumeControl_highlight
        {
            get
            {
                if (_bmpVolumeControl_highlight == null)
                {
                    _bmpVolumeControl_highlight = (Bitmap)new Bitmap(Properties.Resources.Volume_Control_highlight);
                    _bmpVolumeControl_highlight.MakeTransparent(_bmpVolumeControl_highlight.GetPixel(0, 0));
                }

                return _bmpVolumeControl_highlight;
            }
        }

        Bitmap _bmpVolumeControl_highlight_Muted = null;
        public Bitmap bmpVolumeControl_highlight_Muted
        {
            get
            {
                if (_bmpVolumeControl_highlight_Muted == null)
                {
                    _bmpVolumeControl_highlight_Muted = (Bitmap)new Bitmap(Properties.Resources.Volume_Control_muted_highlight);
                    _bmpVolumeControl_highlight_Muted.MakeTransparent(_bmpVolumeControl_highlight_Muted.GetPixel(0, 0));
                }

                return _bmpVolumeControl_highlight_Muted;
            }
        }

        Bitmap bmpVolumeControlOutput = null;
        Bitmap VolumeControl_BitmapOutput(ref Bitmap bmpTop, ref Bitmap bmpBotton)
        {
            if (bmpTop == null || bmpBotton == null) return null;
            if (bmpVolumeControlOutput == null)
            {
                bmpVolumeControlOutput = new Bitmap(bmpTop);
            }

            using (Graphics g = Graphics.FromImage(bmpVolumeControlOutput))
            {
                int intHeightCut = (int)(bmpTop.Height - bmpTop.Height * Volume);
                Rectangle recTopSource = new Rectangle(0, 0, bmpTop.Width, intHeightCut);
                Rectangle recTopDest = new Rectangle(0, 0, bmpTop.Width, intHeightCut);
                g.DrawImage(bmpTop, recTopDest, recTopSource, GraphicsUnit.Pixel);

                Rectangle recBottomSource = new Rectangle(0, intHeightCut, bmpBotton.Width, bmpBotton.Height - intHeightCut);
                Rectangle recBottomDest = new Rectangle(0, intHeightCut, bmpBotton.Width, bmpBotton.Height - intHeightCut);
                g.DrawImage(bmpBotton, recBottomDest, recBottomSource, GraphicsUnit.Pixel);
            }

            //bmpVolumeControlOutput.Save(@"c:\debug\bmpOutput.png");

            return bmpVolumeControlOutput;
        }


        bool MouseIsOverVolumeControl()
        {
            bool bolRetVal = Math3.classMath3.PointIsInsideARectangle(ptMouseOnForm, recVolumeControl);
            return bolRetVal;
        }


        string Score_Format(int intScore)
        {
            string strScoreValue_Before = Score.ToString();
            string strRetVal = "";
            while (strScoreValue_Before.Length >= 3)
            {
                strRetVal = (strScoreValue_Before.Length > 3 ? "," : "") + strScoreValue_Before.Substring(strScoreValue_Before.Length - 3) + strRetVal;
                strScoreValue_Before = strScoreValue_Before.Substring(0, strScoreValue_Before.Length - 3);
            }
            strRetVal = strScoreValue_Before + strRetVal;
            return strRetVal;
        }

        void DrawScore(ref Graphics g)
        {
            //return;
            Bitmap bmpScoreBoard = new Bitmap(Properties.Resources.scoreboard);


            string strScoreValue_Formatted = Score_Format(Score);
            string strScore = "Score:" + strScoreValue_Formatted;
            Size szScore = TextRenderer.MeasureText(strScore, fnt);

            double dblAR_Board = (double)recScore_default.Height / (double)recScore_default.Width;
            double dblAR_Text = (double)szScore.Height / (double)szScore.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recScore_default.Height / dblAR_Text);
                szDraw.Height = (int)recScore_default.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recScore_default.Width * dblAR_Text);
                szDraw.Width = (int)recScore_default.Width;
            }

            Bitmap bmpText = new Bitmap(szScore.Width, szScore.Height);
            Color clrSample = bmpScoreBoard.GetPixel(bmpScoreBoard.Width / 2, bmpScoreBoard.Height / 2);
            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(new SolidBrush(clrSample), new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strScore, fnt, Brushes.Black, new Point());
            }

            Point ptDraw = new Point((int)(recScore_default.Left + (recScore_default.Width - szDraw.Width) / 2),
                                     (int)(recScore_default.Top + (recScore_default.Height - szDraw.Height) / 2));

            Rectangle recDest = new Rectangle(ptDraw, szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            using (Graphics gScoreBoard = Graphics.FromImage(bmpScoreBoard))
            {
                gScoreBoard.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);
            }
            bmpScoreBoard.MakeTransparent(bmpScoreBoard.GetPixel(0, 0));
            RectangleF recScoreBoard = new RectangleF(ptScoreBoardCenter.X - (int)(bmpScoreBoard.Width * .5 * DeltaDrawSize),
                                                      ptScoreBoardCenter.Y - (int)(bmpScoreBoard.Height * .5 * DeltaDrawSize),
                                                      (float)(bmpScoreBoard.Width * DeltaDrawSize),
                                                      (float)(bmpScoreBoard.Height * DeltaDrawSize));
            g.DrawImage(bmpScoreBoard, recScoreBoard, new Rectangle(0, 0, bmpScoreBoard.Width, bmpScoreBoard.Height), GraphicsUnit.Pixel);
        }

        string DrawText_Timer()
        {
            double dblTemp = 0;
            double dblTotalSeconds_Remaining
                                = dblTemp
                                = (double)lngTimerDelay_Minutes * 60 - elapsedSpan.TotalSeconds;
            double dblHours = Math.Floor(dblTotalSeconds_Remaining / 3600.0);
            dblTotalSeconds_Remaining -= 3600 * dblHours;
            double dblMinutes = Math.Floor(dblTotalSeconds_Remaining / 60.0);
            double dblSeconds = dblTotalSeconds_Remaining - dblMinutes * 60;

           string strRetVal =  dblHours.ToString("00.") + ":" + dblMinutes.ToString("00") + ":" + dblSeconds.ToString("00");
            return strRetVal;
        }
        string DrawText_TotalTimePlayed()
        {
            TimeSpan tsTotaTimePlayed = tsTimeSpan_Total();
            string strRetVal = tsTotaTimePlayed.ToString();
            int intIndexLastColon = strRetVal.LastIndexOf(":");

            int intIndexDecimal = strRetVal.IndexOf(".", intIndexLastColon);
            if (intIndexDecimal >= 0)
                strRetVal = strRetVal.Substring(0, intIndexDecimal);

            return strRetVal;
        }

        string DrawText_ChallengeTimer()
        {
            string strRetVal = classChallenge.tsChallengeTimeRemaining.Minutes.ToString("00") + ":" + classChallenge.tsChallengeTimeRemaining.Seconds.ToString("00");
            return strRetVal;
        }
        void DrawChallengeTimer(ref Graphics g)
        {
            classChallenge.ChallengeTimeRemaining_Set();


            string strChallengeTimer = DrawText_ChallengeTimer();

            Size szChallengeTimer = TextRenderer.MeasureText(strChallengeTimer, fnt);

            double dblAR_Board = (double)recChallengeTimer.Height / (double)recChallengeTimer.Width;
            double dblAR_Text = (double)szChallengeTimer.Height / (double)szChallengeTimer.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recChallengeTimer.Height / dblAR_Text);
                szDraw.Height = (int)recChallengeTimer.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recChallengeTimer.Width * dblAR_Text);
                szDraw.Width = (int)recChallengeTimer.Width;
            }

            Bitmap bmpText = new Bitmap(szChallengeTimer.Width, szChallengeTimer.Height);
            Color clrTransparent = Color.Purple;
            SolidBrush sbrBackground = new SolidBrush(clrTransparent);
            SolidBrush sbrForeground = null;
            if (classChallenge.tsChallengeTimeRemaining.Minutes > 7)
            {
                sbrForeground = new SolidBrush(Color.Green);
            }
            else if (classChallenge.tsChallengeTimeRemaining.Minutes > 4)
            {
                sbrForeground = new SolidBrush(Color.Blue);
            }
            else if (classChallenge.tsChallengeTimeRemaining.Minutes > 1)
            {
                sbrForeground = new SolidBrush(Color.Black);
            }
            else
            {
                sbrForeground = new SolidBrush(Color.Red);
            }


            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(sbrBackground,
                                    new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strChallengeTimer,
                                 fnt,
                                 sbrForeground,
                                 new Point());
            }
            bmpText.MakeTransparent(clrTransparent);
            //bmpText.Save(@"c:\debug\bmpText.png");

            Point ptDraw = new Point((int)(recChallengeTimer.Left + (recChallengeTimer.Width - szDraw.Width) / 2),
                                     (int)(recChallengeTimer.Top + (recChallengeTimer.Height - szDraw.Height) / 2));

            Rectangle recDest = new Rectangle(ptDraw, szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            //g.FillRectangle(sbrBackground, recChallengeTimer);
            g.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);

        }

        void DrawTotalTimePlayed(ref Graphics g)
        {
            if (lngTimerDelay_Minutes >= 0)
            {
                string strTime = DrawText_TotalTimePlayed();
                double dblTemp = (double)lngTimerDelay_Minutes * 60 - elapsedSpan.TotalSeconds;

                Size szTime = TextRenderer.MeasureText(strTime, fnt);
                Bitmap bmpTime = new Bitmap(szTime.Width, szTime.Height);
                RectangleF recFill = new RectangleF(0, 0, bmpTime.Width, bmpTime.Height / 2);
                using (Graphics gTime = Graphics.FromImage(bmpTime))
                {
                    gTime.FillRectangle(Brushes.Black, recFill);
                    gTime.DrawString(strTime,
                                     fnt,
                                     dblTemp > 60
                                             ? Brushes.Green
                                             : dblTemp > 10
                                                       ? Brushes.Yellow
                                                       : Brushes.Red,
                                     recFill.Location);
                }

                double dblAR_Rec = (double)recGrab.Height / (double)recGrab.Width;
                double dblAR_Time = (double)bmpTime.Height / (double)bmpTime.Width;

                if (dblAR_Time > dblAR_Rec)
                {
                    // height is limiting
                    SizeF szDest = new SizeF((float)(recGrab.Height / dblAR_Time), recGrab.Height);
                    recFill = new RectangleF(recGrab.Left + (recGrab.Width - szDest.Width) / 2,
                                            recGrab.Top,
                                            szDest.Width,
                                            szDest.Height);
                }
                else
                {
                    // width is limiting
                    SizeF szDest = new SizeF(recGrab.Width, (float)(recGrab.Width * dblAR_Time));
                    recFill = new RectangleF(recGrab.Left,
                                            recGrab.Top + (recGrab.Height - szDest.Height) / 2,
                                            szDest.Width,
                                            szDest.Height);
                }
                
                Rectangle recSource = new Rectangle(0, 0, bmpTime.Width, bmpTime.Height);
                g.DrawImage(bmpTime, recFill, recSource, GraphicsUnit.Pixel);
            }
        }

        void DrawTimer(ref Graphics g)
        {
            //return;
            Bitmap bmpTimeBoard= new Bitmap(Properties.Resources.TotalTimeBoard);
 
            string strTimePlayed = DrawText_Timer(); 

            Size szTotalTimePlayed = TextRenderer.MeasureText(strTimePlayed, fnt);

            double dblAR_Board = (double)recTotalTimePlayed_default.Height / (double)recTotalTimePlayed_default.Width;
            double dblAR_Text = (double)szTotalTimePlayed.Height / (double)szTotalTimePlayed.Width;
            Size szDraw = new Size();
            if (dblAR_Text > dblAR_Board)
            { // height is limit
                szDraw.Width = (int)(recTotalTimePlayed_default.Height / dblAR_Text);
                szDraw.Height = (int)recTotalTimePlayed_default.Height;
            }
            else
            { // width is limit
                szDraw.Height = (int)(recTotalTimePlayed_default.Width * dblAR_Text);
                szDraw.Width = (int)recTotalTimePlayed_default.Width;
            }

            Bitmap bmpText = new Bitmap(szTotalTimePlayed.Width, szTotalTimePlayed.Height);
            Color clrSample = bmpTimeBoard.GetPixel(bmpTimeBoard.Width / 2, bmpTimeBoard.Height / 2);
            using (Graphics gText = Graphics.FromImage(bmpText))
            {
                gText.FillRectangle(new SolidBrush(clrSample), new RectangleF(0, 0, bmpText.Width, bmpText.Height));
                gText.DrawString(strTimePlayed, fnt, Brushes.Black, new Point());
            }
            //bmpText.Save(@"c:\debug\bmpText.png");
            bmpText.MakeTransparent(bmpText.GetPixel(0, 0));
            Point ptCenterText = new Point(bmpTimeBoard.Width / 2,
                                           bmpTimeBoard.Height/ 2);
            Rectangle recDest = new Rectangle(new Point(ptCenterText.X - szDraw.Width/2, ptCenterText.Y - szDraw.Height/2), szDraw);
            Rectangle recSource = new Rectangle(new Point(), bmpText.Size);
            using (Graphics gScoreBoard= Graphics.FromImage(bmpTimeBoard))
            {
                gScoreBoard.DrawImage(bmpText, recDest, recSource, GraphicsUnit.Pixel);
            } 

            //Point ptTotalTimePlayedBoardCenter = new Point(Width  / 2,
            //                                               (int)(recTotalTimePlayed_default.Top + (recTotalTimePlayed_default.Height - szDraw.Height) / 2));

            //bmpTimeBoard.MakeTransparent(bmpTimeBoard.GetPixel(0, 0));
            float fltFractionSize = 0.75f;
            SizeF szfDraw = new SizeF((float)(bmpTimeBoard.Width * DeltaDrawSize * fltFractionSize),
                                     (float)(bmpTimeBoard.Height * DeltaDrawSize * fltFractionSize));


            RectangleF recTotalTimePlayedBoard = new RectangleF(recTotalTimePlayed.X - (int)(szfDraw.Width /2),
                                                                recTotalTimePlayed.Y,// + (int)(szfDraw.Height /2),
                                                                szfDraw.Width, 
                                                                szfDraw.Height);
            g.DrawImage(bmpTimeBoard, recTotalTimePlayedBoard, new Rectangle(0, 0, bmpTimeBoard.Width, bmpTimeBoard.Height), GraphicsUnit.Pixel);


            return;
            //RectangleF recScoreBoard = new RectangleF(ptScoreBoardCenter.X - (int)(bmpScoreBoard.Width * .5 * DeltaDrawSize),
            //                                   ptScoreBoardCenter.Y - (int)(bmpScoreBoard.Height * .5 * DeltaDrawSize),
            //                                   (float)(bmpScoreBoard.Width * DeltaDrawSize),
            //                                   (float)(bmpScoreBoard.Height * DeltaDrawSize));

        }
        
        public static int LivesRemaining
        {
            get { return classChallenge.LivesRemaining; }
            set { classChallenge.LivesRemaining = value; }
        }

     
        void DrawAddNewTiles(ref Graphics g)
        {
            Size szDraw = new Size(szTile.Width - 2, szTile.Height - 2);
            for (int intTileCounter = 0; intTileCounter < lstTilesNew.Count; intTileCounter++)
            {
                classTile_New cTile = lstTilesNew[intTileCounter];
                Point ptTL = new Point((int)(cTile.ptGraphics.X + 1), 
                                       (int)(cTile.ptGraphics.Y + 1));
                g.FillEllipse(new SolidBrush(classTile.clrArray[cTile.Value]), new RectangleF(ptTL, szDraw));

                int intTile = cTile.Value;
                string strText =classTile.TileText_Get(intTile);
                Size szText = TextRenderer.MeasureText(strText, fnt);
                Point ptCenter = new Point(ptTL.X + szDraw.Width / 2, ptTL.Y + szDraw.Height / 2);
                g.DrawString(strText, fnt, Brushes.Black, new Point(ptCenter.X - szText.Width / 2, ptCenter.Y - szText.Height / 2));
            }
        }
        public static float fltNumberOfTokens = 0f;
        void DrawGameBoard(ref Graphics g)
        {
            fltNumberOfTokens = 0f;
            classChallenge.Animate();
            g.FillRectangle(Brushes.Black, recGame);
            Font fntDebug = new Font("ms sans-serif", 8);
            for (int intX = 0; intX < szGame.Width; intX++)
                for (int intY = 0; intY < szGame.Height; intY++)
                    Board.Tiles[intX, intY].Animate(ref g);
        }
        #endregion

        #region Tiles_GatherLike

        void Tiles_GatherLike()
        {
            for (int intX = 0; intX < szGame.Width; intX++)
            {
                for (int intY = 0; intY < szGame.Height; intY++)
                {
                    lstTilesGathered = Tiles_GatherLike_BFS(new Point(intX, intY));
                    if (lstTilesGathered.Count >= 4)
                    {
                        eFSM = enuHex2048_FSM.Tiles_GatherLike_Flash;
                        bolTilesGathered = true;
                        return;
                    }
                }
            }

            if (bolNewTiles || bolTilesGathered)
            {
                eFSM = (EmptyTiles().Count == 0) ? enuHex2048_FSM.Game_Over : enuHex2048_FSM.idle;
                bolNewTiles = false;
                bolTilesGathered = false;
            }
            else
            {
                eFSM = enuHex2048_FSM.Tiles_Adding_Init;
            }
        }

        List<Point> Tiles_GatherLike_BFS(Point ptSeed)
        {
            if (!TileInBounds(ptSeed)) return new List<Point>();
            int[,] intSeen = new int[szGame.Width, szGame.Height];
            int intSeedValue = Board.Tiles[ptSeed.X, ptSeed.Y].Value;
            if (intSeedValue <= 0) return new List<Point>();

            List<Point> lstQ = new List<Point>();
            lstQ.Add(ptSeed);

            List<Point> lstRetVal = new List<Point>();

            while (lstQ.Count > 0)
            {
                Point ptTest = lstQ[0];
                intSeen[ptTest.X, ptTest.Y] = 1;
                lstQ.RemoveAt(0);
                int intTileValue = Board.Tiles[ptTest.X, ptTest.Y].Value;
                if (intTileValue == intSeedValue)
                {
                    if (!lstRetVal.Contains(ptTest))
                    {
                        lstRetVal.Add(ptTest);
                        for (int intDir = 0; intDir < 6; intDir++)
                        {
                            Point ptNeaghbour = move(ptTest, intDir);
                            if (TileInBounds(ptNeaghbour))
                                if (intSeen[ptNeaghbour.X, ptNeaghbour.Y] == 0)
                                    if (!lstQ.Contains(ptNeaghbour) && !lstRetVal.Contains(ptNeaghbour))
                                        lstQ.Add(ptNeaghbour);
                        }
                    }
                }
            }
            return lstRetVal;
        }
        int intTilesGathered_ThisScore = 0;
        void Tiles_RemoveLike()
        {
            Point ptKeep = new Point(-1, -1);
            // use lstNewTiles & move tile to set location of joined tiles
            if (lstTilesGathered.Contains(ptTileSelected))
                ptKeep = ptTileSelected;
            else
            {
                for (int intNewTilesCounter = 0; intNewTilesCounter < lstTilesNew.Count; intNewTilesCounter++)
                {
                    Point ptTileNew = lstTilesNew[intNewTilesCounter].pt;
                    if (lstTilesGathered.Contains(ptTileNew))
                    {
                        ptKeep = ptTileNew;
                        break;
                    }
                }
            }
            if (ptKeep.X < 0 || ptKeep.Y < 0)
            {
                if (lstTilesGathered.Count >=4)
                {
                    ptKeep = lstTilesGathered[0];
                }else
                {
                    eFSM = enuHex2048_FSM.idle;
                    return;
                }
            }
            int intNewValue = Board.Tiles[ptKeep.X, ptKeep.Y].Value + 2;
            intTilesGathered_ThisScore += lstTilesGathered.Count - 3;

            int intAddScore = (int)Math.Pow(2, intNewValue - 1)*intTilesGathered_ThisScore;
            new classAddScore(intAddScore, TileCenter(ptKeep));

            while (lstTilesGathered.Count > 0)
            {
                Point ptRemove = lstTilesGathered[0];
                Board.Tiles[ptRemove.X, ptRemove.Y].Value = 0;
                lstTilesGathered.RemoveAt(0);
            }

            Board.Tiles[ptKeep.X, ptKeep.Y].Value = intNewValue;
            Board.Tiles[ptKeep.X, ptKeep.Y].Radiate((double)intNewValue / (double)classTile.numColors);


            classChallenge.Challenge_TestComplete();


            if (intNewValue > 11)
            {
                ptFinalTileRemoved = ptKeep;
                classStreamer.ptStart = TileCenter(ptKeep);
                if (intNewValue > 12)
                    classChallenge.LivesRemaining++;
                eFSM = enuHex2048_FSM.Tiles_RemoveFinal;
            }
            else if (!lstFlareTrigger[Board.Tiles[ptKeep.X, ptKeep.Y].Value])
            {
                lstFlareTrigger[Board.Tiles[ptKeep.X, ptKeep.Y].Value] = true;
                classFlare.ptStart = TileCenter(ptKeep);
                eFSM = enuHex2048_FSM.Tiles_GatherLike;
            }
            else
            {
                eFSM = enuHex2048_FSM.Tiles_GatherLike;
            }

            Sparkles_Add(ptKeep, intNewValue * 7);
            return;
        }

        void Sparkles_Add(Point pt, int intNumSparkles)
        {
            Point ptCenter = TileCenter(pt);
            for (int intSparkleCounter = 0; intSparkleCounter < intNumSparkles; intSparkleCounter++)
            {
                int intVelocity = classRND.Get_Int(10, 15);
                Point ptVelocity = classRND.Get_Point(new classMinMax(-intVelocity, intVelocity),
                                                      new classMinMax(-intVelocity, intVelocity));
                new classSparkle(ptCenter, ptVelocity);
            }
        }
        #endregion 
    }

    public class classTile_New
    {
        public PointF ptGraphics = new PointF();
        public Point pt = new Point();
        public int Value = 0;

        public static int intNumSteps = 10;
        Point ptMove = new Point();

        static bool bolRightOrb = false;
        public classTile_New(Point pt)
        {
            bolRightOrb = !bolRightOrb;
            this.pt = pt;

            ptGraphics = new Point((int)(formHex2048.recFrame.Left + formHex2048.recFrame.Width / 2 ),
                                   (int)(formHex2048.recFrame.Top ));

            ptGraphics = formHex2048.ptNewTilesStart;

            ptGraphics.X -= formHex2048.szTile.Width / 2;
            ptGraphics.Y -= formHex2048.szTile.Height / 2;
            Point ptEnd = formHex2048.TileCenter(pt);

            Value = formHex2048.RND_GetInt(4) + 1;

            ptMove.X = (int)(ptEnd.X - ptGraphics.X) / intNumSteps;
            ptMove.Y = (int)(ptEnd.Y - ptGraphics.Y) / intNumSteps;
            
        }

        public void Move()
        {
            ptGraphics.X += (ptMove.X );
            ptGraphics.Y += (ptMove.Y );
        }
    }

    public class formFlash : PerPixelForm.PerPixelAlphaForm
    {
        public static formFlash instance = null;
        public formFlash()
        {
            instance = this;

            Bitmap bmpFlash = (Bitmap)Properties.Resources.Hex_2048_flash;
            bmpFlash.MakeTransparent(bmpFlash.GetPixel(0, 0));
            SetBitmap(bmpFlash);
            Left = (Screen.PrimaryScreen.WorkingArea.Width - bmpFlash.Width) / 2;
            Top = (Screen.PrimaryScreen.WorkingArea.Height - bmpFlash.Height) / 2;
            Show();
        }
    }

}