using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;

namespace shuchancontrol
{
    public class ChessMain
    {
        public ShuchanChessBoard Board { get; private set; }
        public ChessProgramTalker Talker { get; private set; }
        public ShuchanHardSequence HardSequence { get; private set; }
        public UserPlayGetter PlayGetter { get; private set; }
        public Thread ShuchanControlThread { get; private set; }
        bool exitthread = false;
        public Queue<string> MessageToGui { get; set; }
        public Queue<PieceMoveToGui> OccuredMoveToGui { get; set; }

        void sendMessageToGui(string message)
        {
            lock (MessageToGui)
            {
                MessageToGui.Enqueue(message);
            }
        }

        void sendOccuredMoveToGui(PieceMoveToGui move)
        {
            lock (OccuredMoveToGui)
            {
                OccuredMoveToGui.Enqueue(move);
            }
        }

        public void ExitChessThread()
        {
            exitthread = true;
            if (!ShuchanControlThread.Join(1000))
            {
                ShuchanControlThread.Abort();
                Talker.Dispose();
                HardSequence.Dispose();
                PlayGetter.Dispose();
            }
        }

        public ChessMain(string comPort, string pyScriptMovePositionPath,
            string chessEnginePath, string chessEngineArguments, string userPlayGetterPath)
        {
            Board = new ShuchanChessBoard(true);
            Talker = new ChessProgramTalker(chessEnginePath, chessEngineArguments);
            HardSequence = new ShuchanHardSequence(new ShuchanHardDriver(comPort));
            PlayGetter = new UserPlayGetter(userPlayGetterPath);
        }

        public void Run()
        {
            ShuchanControlThread = new Thread(new ThreadStart(runMainThread));
            ShuchanControlThread.Start();
        }

        void runMainThread()
        {
            int playstate = 0;
            Talker.StartEngine();

            string lastwhitemove = "", lastblackmove = "";
            string boardrecogfen = null;
            ShuchanChessBoard boardbackup = null;   //ユーザーの手を送ってから、コンピュータの手が返されるまではIllegalの可能性があるので、ユーザーの手を打つ直前の盤を保存しておく。
            string gameresultmessage = null;    //ゲームの結果表示。

            while (!exitthread)
            {
                HardSequence.ShuchanInstance.InvalidateAllSWOncePressed();
                Trace.WriteLine("playstate: " + playstate.ToString(), "runMainThread");
                switch (playstate)
                {
                    case 0:
                        //初期化
                        {
                            sendMessageToGui("初期化中");
                            Board = new ShuchanChessBoard(true);
                            Talker.NewGame();
                            playstate = 1;
                        }
                        break;
                    case 1:
                        //開始ボタンを待つ
                        {
                            sendMessageToGui("開始ボタンが押されるのを待機中");
                            HardSequence.ShuchanInstance.WriteLine("ｼｭｳﾁｬﾝ", 0);
                            HardSequence.UpdateOperationMethodMessage("T:ｹﾞｰﾑｶｲｼ");
                            while (!HardSequence.ShuchanInstance.GetSWIfOncePressed((int)ShuchanHardDriver.SWName.SWA))
                            {
                                Thread.Sleep(100);
                            }
                            playstate = 2;
                        }
                        break;
                    case 2:
                        //ユーザーの手を待つ
                        {
                            sendMessageToGui("ユーザーの手を待機中");
                            HardSequence.UpdateOperationMethodMessage("ﾕｰｻﾞｰﾉ ﾊﾞﾝ T:ｶﾝﾘｮｳ");

                            while (true)
                            {
                                if (HardSequence.ShuchanInstance.GetSWIfOncePressed((int)ShuchanHardDriver.SWName.SWA))
                                {
                                    playstate = 3;
                                    break;
                                }

                                if (HardSequence.ShuchanInstance.GetSWIfOncePressed((int)ShuchanHardDriver.SWName.SWB))
                                {
                                    //リセット
                                    playstate = 0;
                                    break;
                                }
                                Thread.Sleep(100);
                            }

                        }
                        break;
                    case 3:
                        //画像認識
                        {
                            sendMessageToGui("画像認識中");
                            HardSequence.UpdateOperationMethodMessage("ｶﾞｿﾞｳﾆﾝｼｷﾁｭｳ...");
                            boardrecogfen = PlayGetter.ReadBoard();
                            if (boardrecogfen == null)
                            {
                                //取り込み失敗
                                HardSequence.UpdateOperationMethodMessage("ｶﾞｿﾞｳﾆﾝｼｷﾆ ｼｯﾊﾟｲ");
                                playstate = 2;
                            }
                            else
                            {
                                sendMessageToGui("認識結果:" + boardrecogfen);
                                playstate = 4;
                            }
                        }
                        break;
                    case 4:
                        //合法手か調べる
                        {
                            sendMessageToGui("合法手かどうか検証中");
                            HardSequence.UpdateOperationMethodMessage("ｹﾝｼｮｳﾁｭｳ...");
                            boardbackup = new ShuchanChessBoard(Board);
                            ShuchanChessBoard.MoveFromRecogFENResult mfrres = Board.MoveFromRecogFEN(boardrecogfen, ShuchanChessBoard.PieceType.None);
                            if (mfrres.PromotionTargetRequired)
                            {
                                sendMessageToGui("プロモーション先を入力要求");
                                ShuchanChessBoard.PieceType promotiontype = HardSequence.SelectPromotionTarget();
                                mfrres = Board.MoveFromRecogFEN(boardrecogfen, promotiontype);
                            }

                            if (mfrres.IsValid)
                            {
                                //多分合法手
                                sendMessageToGui("合法手と思われるので、チェスエンジンに送信");
                                Talker.DiscardMessages();
                                Talker.SendUserMove(mfrres.MoveSAN);    //チェスエンジンに手を送る。
                                lastwhitemove = mfrres.MoveSAN;
                                HardSequence.UpdateMoveNotation(Board.FullmoveNumber, lastwhitemove, null);
                                if (mfrres.IsCastling)
                                {
                                    if (mfrres.MoveSAN.Contains("O-O-O"))
                                    {
                                        //白のQueenside-castling
                                        sendOccuredMoveToGui(new PieceMoveToGui()
                                        {
                                            MoveFrom = new ShuchanPiecePosition("e1"),
                                            MoveTo = new ShuchanPiecePosition("c1"),
                                            IsCastlingKingMove = true,
                                            IsNextMoveWhite = true,
                                            MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                            MoveSAN = mfrres.MoveSAN,
                                            IsWhiteMove = true,
                                            FullMoveNumber = Board.FullmoveNumber,
                                        });
                                        sendOccuredMoveToGui(new PieceMoveToGui()
                                        {
                                            MoveFrom = new ShuchanPiecePosition("a1"),
                                            MoveTo = new ShuchanPiecePosition("d1"),
                                            IsCastlingKingMove = false,
                                            IsNextMoveWhite = false,
                                            MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                            MoveSAN = null,
                                            IsWhiteMove = true,
                                            FullMoveNumber = Board.FullmoveNumber,
                                        });
                                    }
                                    else
                                    {
                                        //白のKingside-castling
                                        sendOccuredMoveToGui(new PieceMoveToGui()
                                        {
                                            MoveFrom = new ShuchanPiecePosition("e1"),
                                            MoveTo = new ShuchanPiecePosition("g1"),
                                            IsCastlingKingMove = true,
                                            IsNextMoveWhite = true,
                                            MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                            MoveSAN = mfrres.MoveSAN,
                                            IsWhiteMove = true,
                                            FullMoveNumber = Board.FullmoveNumber,
                                        });
                                        sendOccuredMoveToGui(new PieceMoveToGui()
                                        {
                                            MoveFrom = new ShuchanPiecePosition("h1"),
                                            MoveTo = new ShuchanPiecePosition("f1"),
                                            IsCastlingKingMove = false,
                                            IsNextMoveWhite = false,
                                            MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                            MoveSAN = null,
                                            IsWhiteMove = true,
                                            FullMoveNumber = Board.FullmoveNumber,
                                        });
                                    }
                                }
                                else
                                {
                                    sendOccuredMoveToGui(new PieceMoveToGui()
                                    {
                                        MoveFrom = mfrres.MoveFrom,
                                        MoveTo = mfrres.MoveTo,
                                        IsCastlingKingMove = false,
                                        IsNextMoveWhite = false,
                                        MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                        MoveSAN = mfrres.MoveSAN,
                                        PromotionType = mfrres.PromotedTo,
                                        IsWhiteMove = true,
                                        FullMoveNumber = Board.FullmoveNumber,
                                    });
                                }
                                playstate = 5;
                            }
                            else
                            {
                                //確実に違法手
                                sendMessageToGui("違法手だった");
                                Board = boardbackup;
                                HardSequence.UpdateMessageFromChessEngine(mfrres.ErrorMessage);
                                playstate = 3;
                            }
                        }
                        break;
                    case 5:
                        //コンピュータの手を待つ
                        {
                            sendMessageToGui("チェスエンジンの応答待機中");
                            HardSequence.UpdateOperationMethodMessage("ｺﾝﾋﾟｭｰﾀ ｶﾝｶﾞｴﾁｭｳ...");
                            while (true)
                            {
                                if (Talker.HasResponseReceived)
                                {
                                    if (Talker.ErrorMessage != null)
                                    {
                                        //ユーザーの手は違法手(Illegalでないのはプログラムのミスの可能性が大きい)
                                        sendMessageToGui("チェスエンジンからエラーメッセージが帰った");
                                        sendMessageToGui("エンジンからのメッセージ:" + Talker.ErrorMessage);
                                        HardSequence.UpdateMessageFromChessEngine(Talker.ErrorMessage);
                                        Board = boardbackup;    //盤の状態を戻す。
                                        playstate = 2;  //ユーザーに再度手を打たせる。
                                        break;
                                    }


                                    if (Talker.IllegalMoveMessage != null)
                                    {
                                        //ユーザーの手は違法手
                                        sendMessageToGui("チェスエンジンからIllegalが帰った");
                                        sendMessageToGui("エンジンからのメッセージ:" + Talker.ErrorMessage);
                                        HardSequence.UpdateMessageFromChessEngine(Talker.IllegalMoveMessage);
                                        Board = boardbackup;    //盤の状態を戻す。
                                        playstate = 2;  //ユーザーに再度手を打たせる。
                                        break;
                                    }

                                    if (Talker.ComputerMove != null)
                                    {
                                        //コンピュータが手を打った
                                        sendMessageToGui("チェスエンジンが手を打った");
                                        lastblackmove = Talker.ComputerMove;
                                        playstate = 6;
                                        break;
                                    }

                                    if (Talker.ResultMessage != null)
                                    {
                                        sendMessageToGui("ユーザーの手で終了するゲームの結果が返った");
                                        //ゲームが終了(コンピュータが手を打つと同時に終了の場合、上のifでbreakするのでここは呼ばれない)
                                        gameresultmessage = Talker.ResultMessage;
                                        playstate = 99;
                                        break;
                                    }

                                }

                                string enginemessage = Talker.ReadCommentFromEngine();
                                if (enginemessage != null)
                                {
                                    //エンジンがユーザーに(ゲームの進行に関係ない)メッセージを出した
                                    sendMessageToGui("チェスエンジンからのメッセージ:" + enginemessage);
                                    HardSequence.UpdateMessageFromChessEngine(enginemessage);
                                }

                                Thread.Sleep(100);
                            }
                        }
                        break;
                    case 6:
                        //ロボットアームでコンピュータの手を指す
                        {
                            ShuchanChessBoard.MoveByComputerResult mbcres = Board.MoveByComputer(lastblackmove);
                            if (!mbcres.TaskCompleted)
                            {
                                //コンピュータの手が違法手
                                //エンジンとの連携がおかしくなっているので、終了する。
                                sendMessageToGui("チェスエンジンの手がIllegal:通信に矛盾あり");
                                HardSequence.ShuchanInstance.InvalidateAllSWOncePressed();
                                HardSequence.UpdateMessageFromChessEngine("ﾁｪｽｴﾝｼﾞﾝﾄﾉ ﾂｳｼﾝｴﾗｰ");
                                HardSequence.UpdateOperationMethodMessage("T:ﾘｾｯﾄ");
                                while (!HardSequence.ShuchanInstance.GetSWIfOncePressed((int)ShuchanHardDriver.SWName.SWA))
                                {
                                    Thread.Sleep(100);
                                }
                                playstate = 0;
                            }
                            if (mbcres.IsCastling)
                            {
                                if (mbcres.MoveSAN.Contains("O-O-O"))
                                {
                                    //黒のQueenside-castling
                                    sendOccuredMoveToGui(new PieceMoveToGui()
                                    {
                                        MoveFrom = new ShuchanPiecePosition("e8"),
                                        MoveTo = new ShuchanPiecePosition("c8"),
                                        IsCastlingKingMove = true,
                                        IsNextMoveWhite = false,
                                        MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                        MoveSAN = mbcres.MoveSAN,
                                        IsWhiteMove = false,
                                        FullMoveNumber = Board.FullmoveNumber,
                                    });
                                    sendOccuredMoveToGui(new PieceMoveToGui()
                                    {
                                        MoveFrom = new ShuchanPiecePosition("a8"),
                                        MoveTo = new ShuchanPiecePosition("d8"),
                                        IsCastlingKingMove = false,
                                        IsNextMoveWhite = true,
                                        MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                        MoveSAN = null,
                                        IsWhiteMove = false,
                                        FullMoveNumber = Board.FullmoveNumber,
                                    });

                                }
                                else
                                {
                                    //黒のKingside-castling
                                    sendOccuredMoveToGui(new PieceMoveToGui()
                                    {
                                        MoveFrom = new ShuchanPiecePosition("e8"),
                                        MoveTo = new ShuchanPiecePosition("g8"),
                                        IsCastlingKingMove = true,
                                        IsNextMoveWhite = false,
                                        MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                        MoveSAN = mbcres.MoveSAN,
                                        IsWhiteMove = false,
                                        FullMoveNumber = Board.FullmoveNumber,
                                    });
                                    sendOccuredMoveToGui(new PieceMoveToGui()
                                    {
                                        MoveFrom = new ShuchanPiecePosition("h8"),
                                        MoveTo = new ShuchanPiecePosition("f8"),
                                        IsCastlingKingMove = false,
                                        IsNextMoveWhite = true,
                                        MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                        MoveSAN = null,
                                        IsWhiteMove = false,
                                        FullMoveNumber = Board.FullmoveNumber,
                                    });

                                }
                            }
                            else
                            {
                                sendOccuredMoveToGui(new PieceMoveToGui()
                                {
                                    MoveFrom = mbcres.MoveFrom,
                                    MoveTo = mbcres.MoveTo,
                                    IsCastlingKingMove = false,
                                    IsNextMoveWhite = true,
                                    MoveResultBoard = (ShuchanChessBoard)Board.Clone(),
                                    MoveSAN = mbcres.MoveSAN,
                                    PromotionType = mbcres.PromotedTo,
                                    IsWhiteMove = false,
                                    FullMoveNumber = Board.FullmoveNumber,
                                });
                            }

                            sendMessageToGui("チェスエンジンの手:" + mbcres.MoveSAN);
                            lastblackmove = mbcres.MoveSAN;
                            HardSequence.UpdateMoveNotation(Board.FullmoveNumber - 1, lastwhitemove, lastblackmove);

                            //アームを動かしてこまを移動
                            for (int i = 0; i < mbcres.PhysicalMoves.Count; i++)
                            {
                                sendMessageToGui(string.Format("駒の移動:{0}から{1}", mbcres.PhysicalMoves[i].From.ToString(), mbcres.PhysicalMoves[i].To.ToString()));
                                HardSequence.Move(mbcres.PhysicalMoves[i].From, mbcres.PhysicalMoves[i].To, i == (mbcres.PhysicalMoves.Count - 1));
                            }

                            if (Talker.ResultMessage != null)
                            {
                                //ゲームが終了
                                sendMessageToGui("コンピュータの手で終了するゲームの結果が返った");
                                gameresultmessage = Talker.ResultMessage;
                                playstate = 99;
                            }
                            else
                            {
                                //ユーザーの次の手を待つ
                                sendMessageToGui("ユーザーの次の手の待機に移行");
                                playstate = 2;
                            }
                        }
                        break;
                    case 99:
                        //ゲームの決着がついた
                        {
                            sendMessageToGui("ゲーム結果:" + gameresultmessage);
                            HardSequence.UpdateMessageFromChessEngine(gameresultmessage);
                            HardSequence.UpdateOperationMethodMessage("T:ﾘｾｯﾄ");
                            while (!HardSequence.ShuchanInstance.GetSWIfOncePressed((int)ShuchanHardDriver.SWName.SWA))
                            {
                                Thread.Sleep(100);
                            }
                            playstate = 0;
                        }
                        break;
                    default:
                        break;
                }
            }

            Talker.Dispose();
            PlayGetter.Dispose();
            HardSequence.Dispose();
        }
    }
}
