using System;
using System.Collections.Generic;
using System.Text;
using Yanesdk.Draw;
using Yanesdk.Math;

namespace Yamalib.Draw.Effect
{
    /// <summary>
    /// Hex状態を表すデータクラス
    /// </summary>
    public class HexFlag
    {
        public class Wall
        {
            public bool w = true;
            public bool b = false;
        };

        public HexFlag()
        {
            for (int i = 0; i < walls.Length; i++)
            {
                walls[i] = new Wall();
            }
        }

        public readonly Wall[] walls = new Wall[6];
        public bool show = false;
    };

    /// <summary>
    /// Hex内を移動するオブジェクトの基底クラス
    /// </summary>
    public abstract class HexExplorer
    {

        protected const int a = 0;
        protected const int b = 1;
        protected const int c = 2;
        protected const int d = 3;
        protected const int e = 4;
        protected const int f = 5;

        /// static 縦×横の大きさの設定
        public static void SetHexSize(int x, int y)
        {
            int sx, sy;
            for (int i = 0; i < y; i++)
            {
                hex.Add(new List<HexFlag>());
            }

            for (int i = 0; i < hex.Count; i++)
            {
                for (int j = 0; j < x; j++)
                {
                    hex[j].Add(new HexFlag());
                }
            }


            // 上辺を固める
            for (int i = 0; i < hex[0].Count; ++i)
            {
                hex[0][i].walls[a].b = true;
                hex[0][i].walls[b].b = true;
            }

            // 左辺を固める
            for (int i = 0; i < hex.Count; ++i)
            {
                hex[i][0].walls[a].b = true;
                hex[i][0].walls[e].b = true;
                hex[i][0].walls[f].b = true;
            }

            // 下辺を固める
            sy = hex.Count - 1;
            for (int i = 0; i < hex[sy].Count; ++i)
            {
                hex[sy][i].walls[d].b = true;
                hex[sy][i].walls[e].b = true;
            }

            // 右辺を固める
            sx = hex[0].Count - 1;
            for (int i = 0; i < hex.Count; ++i)
            {
                hex[i][sx].walls[b].b = true;
                hex[i][sx].walls[c].b = true;
                hex[i][sx].walls[d].b = true;
            }
        }

        /// static 状態の初期化
        public static void Reset()
        {
            for (int y = 0; y < hex.Count; ++y)
            {
                for (int x = 0; x < hex[y].Count; ++x)
                {
                    hex[y][x].show = false;
                    hex[y][x].walls[a].w = true;
                    hex[y][x].walls[b].w = true;
                    hex[y][x].walls[c].w = true;
                    hex[y][x].walls[d].w = true;
                    hex[y][x].walls[e].w = true;
                    hex[y][x].walls[f].w = true;
                }
            }
        }

        /// static 構築中のHexFlagを返す
        public static List<List<HexFlag>> GetHexFlag()
        {
            return hex;
        }

        //!< ＨＥＸの状態をあらわす配列
        protected readonly static List<List<HexFlag>> hex = new List<List<HexFlag>>();
        protected HexFlag hf;
    }

    /// <summary>
    /// HexMapを破壊していく破壊子
    /// </summary>
    public class HexDeletor : HexExplorer
    {
        #region フィールドメンバ

        private List<System.Drawing.Point> route;		//!< 探査中の経路
        private int x;
        private int y;					//!< 探査子の位置
        private Rand rand;

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public HexDeletor()
            : base()
        {
            rand = new Rand();
            rand.Randomize();
            route = new List<System.Drawing.Point>();
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = 0;
            tmp.Y = 0;
            route.Add(tmp);
        }

        /// 現在地の設定
        public void SetPos(int x, int y)
        {
            if (y >= hex.Count) y = hex.Count - 1;
            if (x >= hex[y].Count) x = hex[y].Count - 1;

            this.x = x; this.y = y;
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = x;
            tmp.Y = y;
            route.Add(tmp);
        }

        /// リセット
        public void Clear()
        {
            route.Clear();
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = 0;
            tmp.Y = 0;
            route.Add(tmp);
        }

        public bool SearchNext()
        {
            if (route.Count == 0) return false;

            int num;

        CONTINUE: ;
            hf = hex[y][x];
            num = 0;

            if (!hf.walls[a].w && !hf.walls[a].b)
            {
                num++;
            }
            if (!hf.walls[b].w && !hf.walls[b].b)
            {
                num++;
            }
            if (!hf.walls[c].w && !hf.walls[c].b)
            {
                num++;
            }
            if (!hf.walls[d].w && !hf.walls[d].b)
            {
                num++;
            }
            if (!hf.walls[e].w && !hf.walls[e].b)
            {
                num++;
            }
            if (!hf.walls[f].w && !hf.walls[f].b)
            {
                num++;
            }
            if (num == 0)
            {
                // 破壊完了　新しい地域を探す
                int i = 0, j = 0;
                bool found = false;
                if (route.Count == 0)
                {
                    for (i = 0; i < hex.Count; ++i)
                    {
                        for (j = 0; j < hex[i].Count; ++j)
                        {
                            if (hex[j][i].show)
                            {
                                found = true;
                                break;
                            }
                        }
                    }
                }

                if (found)
                {
                    x = j;
                    y = i;
                    System.Drawing.Point tmp = new System.Drawing.Point();
                    tmp.X = x;
                    tmp.Y = y;
                    route.Add(tmp);
                    goto CONTINUE;
                }
                else
                {
                    return false;
                }
            }

            int rnd = rand.GetRand(num);
            if (!hf.walls[0].w && !hf.walls[a].b)
            {
                if (rnd == 0)
                {
                    if (!Go2a())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (!hf.walls[b].w && !hf.walls[b].b)
            {
                if (rnd == 0)
                {
                    if (!Go2b())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (!hf.walls[c].w && !hf.walls[c].b)
            {
                if (rnd == 0)
                {
                    if (!Go2c())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (!hf.walls[d].w && !hf.walls[d].b)
            {
                if (rnd == 0)
                {
                    if (!Go2d())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (!hf.walls[e].w && !hf.walls[e].b)
            {
                if (rnd == 0)
                {
                    if (!Go2e())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (!hf.walls[f].w && !hf.walls[f].b)
            {
                if (rnd == 0)
                {
                    if (!Go2f())
                    {
                        goto CONTINUE;
                    }
                }
            }

            {
                // 移動経路をpush
                System.Drawing.Point tmp = new System.Drawing.Point();
                tmp.X = x;
                tmp.Y = y;
                route.Add(tmp);
            }

            return true;
        }

        #region 非公開メンバ

        private bool Go2a()
        {
            // 自身のaを壊す
            hex[y][x].walls[a].w = true;
            y = y - 1;
            if ((y % 2) == 0)
            {
                x = x - 1;
            }
            // 移動先のdを壊す
            hex[y][x].walls[d].w = true;

            if (!hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }
        private bool Go2b()
        {
            // 自身のbを壊す
            hex[y][x].walls[b].w = true;
            y = y - 1;
            if ((y % 2) != 0)
            {
                x = x + 1;
            }

            // 移動先のeを壊す
            hex[y][x].walls[e].w = true;

            if (!hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }
        private bool Go2c()
        {
            // 自身のcを壊す
            hex[y][x].walls[c].w = true;
            x = x + 1;

            // 移動先のfを壊す
            hex[y][x].walls[f].w = true;

            if (!hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }
        private bool Go2d()
        {
            // 自身のdを壊す
            hex[y][x].walls[d].w = true;
            y = y + 1;
            if ((y % 2) != 0)
            {
                x = x + 1;
            }

            // 移動先のaを壊す
            hex[y][x].walls[a].w = true;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }
        private bool Go2e()
        {
            // 自身のeを壊す
            hex[y][x].walls[e].w = true;
            y = y + 1;
            if ((y % 2) == 0)
            {
                x = x - 1;
            }

            // 移動先のbを壊す
            hex[y][x].walls[b].w = true;

            if (!hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }
        private bool Go2f()
        {
            // 自身のfを壊す
            hex[y][x].walls[f].w = true;
            x = x - 1;

            // 移動先のcを壊す
            hex[y][x].walls[c].w = true;

            if (!hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = false;
                return true;
            }
        }

        #endregion
    }


    /// <summary>
    /// HexMapを探査していく探査子
    /// </summary>
    public class HexSearcher : HexExplorer
    {
        #region フィールドメンバ

        private readonly List<System.Drawing.Point> route = new List<System.Drawing.Point>();		//!< 探査中の経路
        private int x;
        private int y;					//!< 探査子の位置
        private Rand rand;

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public HexSearcher()
        {
            rand = new Rand();
            rand.Randomize();
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = 0;
            tmp.Y = 0;
            route.Add(tmp);
        }

        /// <summary>
        /// クリアする
        /// </summary>
        public void Clear()
        {
            route.Clear();
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = 0;
            tmp.Y = 0;
            route.Add(tmp);
        }

        /// <summary>
        /// 位置の設定
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        public void SetPos(int x, int y)
        {
            if (y >= hex.Count) y = hex.Count - 1;
            if (x >= hex[y].Count) x = hex[y].Count - 1;

            this.x = x; this.y = y;
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = x;
            tmp.Y = y;
            route.Add(tmp);
        }

        /// <summary>
        /// 次を探査し移動する
        /// </summary>
        /// <returns></returns>
        public bool SearchNext()
        {
            if (route.Count == 0) return false;

            int num;

        CONTINUE: ;
            HexFlag hf = hex[y][x];
            num = 0;

            if (hf.walls[a].w && !hf.walls[a].b)
            {
                num++;
            }
            if (hf.walls[b].w && !hf.walls[b].b)
            {
                num++;
            }
            if (hf.walls[c].w && !hf.walls[c].b)
            {
                num++;
            }
            if (hf.walls[d].w && !hf.walls[d].b)
            {
                num++;
            }
            if (hf.walls[e].w && !hf.walls[e].b)
            {
                num++;
            }
            if (hf.walls[f].w && !hf.walls[f].b)
            {
                num++;
            }
            if (num == 0)
            {
                // 行くべきところがない

                // 描画完了
                if (route.Count == 0) return false;

                //char[512] buf;
                System.Drawing.Point pt = route[route.Count - 1];
                route.Remove(pt);
                x = (int)pt.X;
                y = (int)pt.Y;
                goto CONTINUE;
            }

            int rnd = rand.GetRand(num);
            if (hf.walls[a].w && !hf.walls[a].b)
            {
                if (rnd == 0)
                {
                    if (!Go2a())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (hf.walls[b].w && !hf.walls[b].b)
            {
                if (rnd == 0)
                {
                    if (!Go2b())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (hf.walls[c].w && !hf.walls[c].b)
            {
                if (rnd == 0)
                {
                    if (!Go2c())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (hf.walls[d].w && !hf.walls[d].b)
            {
                if (rnd == 0)
                {
                    if (!Go2d())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (hf.walls[e].w && !hf.walls[e].b)
            {
                if (rnd == 0)
                {
                    if (!Go2e())
                    {
                        goto CONTINUE;
                    }
                }
                --rnd;
            }
            if (hf.walls[f].w && !hf.walls[f].b)
            {
                if (rnd == 0)
                {
                    if (!Go2f())
                    {
                        goto CONTINUE;
                    }
                }
            }

            // 移動経路をpush
            System.Drawing.Point tmp = new System.Drawing.Point();
            tmp.X = x;
            tmp.Y = y;
            route.Add(tmp);

            return true;
        }


        #region 非公開メンバ

        private bool Go2a()
        {
            // 自身のaを壊す
            hex[y][x].walls[a].w = false;
            y = y - 1;
            if ((y % 2) == 0)
            {
                x = x - 1;
            }
            // 移動先のdを壊す
            hex[y][x].walls[d].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }
        private bool Go2b()
        {
            // 自身のbを壊す
            hex[y][x].walls[b].w = false;
            y = y - 1;
            if ((y % 2) != 0)
            {
                x = x + 1;
            }

            // 移動先のeを壊す
            hex[y][x].walls[e].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }
        private bool Go2c()
        {
            // 自身のcを壊す
            hex[y][x].walls[c].w = false;
            x = x + 1;

            // 移動先のfを壊す
            hex[y][x].walls[f].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }
        private bool Go2d()
        {
            // 自身のdを壊す
            hex[y][x].walls[d].w = false;
            y = y + 1;
            if ((y % 2) != 0)
            {
                x = x + 1;
            }

            // 移動先のaを壊す
            hex[y][x].walls[a].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }
        private bool Go2e()
        {
            // 自身のeを壊す
            hex[y][x].walls[e].w = false;
            y = y + 1;
            if ((y % 2) == 0)
            {
                x = x - 1;
            }

            // 移動先のbを壊す
            hex[y][x].walls[b].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }
        private bool Go2f()
        {
            // 自身のfを壊す
            hex[y][x].walls[f].w = false;
            x = x - 1;

            // 移動先のcを壊す
            hex[y][x].walls[c].w = false;

            if (hex[y][x].show)
            {
                return false;
            }
            else
            {
                hex[y][x].show = true;
                return true;
            }
        }

        #endregion
    }

    /// <summary>
    /// ヘックスが増殖して描画されるクラス
    /// </summary>
    public class HexPropagate
    {
        #region フィールドメンバ

        private readonly Rand rnd;
        private TextureLoader tl;			//!< テクスチャローダ
        private int hexX;
        private int hexY;
        private uint count;					//!< ループ回数
        private int tx, ty;					//!< テクスチャのサイズ
        private int tno;					//!< テクスチャナンバ
        private HexSearcher[] searcher;		//!< 探査子
        private HexDeletor del;			//!< 破壊子

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public HexPropagate()
        {
            rnd = new Rand();
            rnd.Randomize();
            searcher = new HexSearcher[1];
            searcher[0] = new HexSearcher();
            del = new HexDeletor();
            count = 0;

        }

        /// <summary>
        /// 画像のセット
        /// </summary>
        /// <param name="tex"></param>
        /// <param name="no"></param>
        public void SetTextureLoader(TextureLoader tex, int no)
        {
            tl = tex;
            tno = no;
            tx = (int)tl.GetTexture(no).Width;
            ty = (int)tl.GetTexture(no).Height;
        }

        public void SetHexSize(int x, int y)
        {
            HexExplorer.SetHexSize(x, y);
            for (int i = 0; i < searcher.Length; ++i)
            {
                searcher[i].SetPos(rnd.GetRand(x), rnd.GetRand(y));
            }
            //del.SetHexSize(X, Y);

            del.SetPos(20, 20);
            hexX = x;
            hexY = y;
        }

        /// <summary>
        /// 状態のクリア
        /// </summary>
        /// <param name="bRandPos"></param>
        public void Clear(bool bRandPos)
        {
            HexSearcher.Reset();

            foreach (HexSearcher hs in searcher)
            {
                hs.Clear();
                if (bRandPos)
                {
                    hs.SetPos(rnd.GetRand(hexX), rnd.GetRand(hexY));
                }
            }
        }

        /// <summary>
        /// 描画処理
        /// </summary>
        /// <param name="screen"></param>
        public void OnPaint(IScreen screen)
        {
            if (tl == null) return;

            count++;

            List<List<HexFlag>> hex = HexSearcher.GetHexFlag();

            for (int i = 0; i < searcher.Length; ++i)
            {
                searcher[i].SearchNext();
            }

            if (count > 1000)
            {
                del.SearchNext();
            }

            int x, y, ox, sy;
            // 次行の高さ
            sy = (int)((ty / 4.0f) * 3);
            for (y = 0; y < hex.Count; ++y)
            {
                if (0 != (y % 2)) ox = tx / 2;
                else ox = 0;
                for (x = 0; x < hex[y].Count; ++x)
                {
                    if (hex[y][x].show)
                    {
                        screen.Blt(tl.GetTexture(tno), tx * x + ox, sy * y);
                    }
                }
            }
        }
    }
}
