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

namespace shuchancontrol
{
    /// <summary>
    /// ユーザーの指し手をカメラから取得する。
    /// </summary>
    public class UserPlayGetter : IDisposable
    {
        /// <summary>
        /// 画像認識プログラムのパス。
        /// </summary>
        public readonly string RecgonizeProgram;

        Process recgprog = null;
        Queue<string> unprocessedrecvlines = new Queue<string>();

        public UserPlayGetter(string recgprogram)
        {
            RecgonizeProgram = recgprogram;
            recgprog = new Process();
            recgprog.StartInfo.FileName = RecgonizeProgram;
            recgprog.StartInfo.RedirectStandardError = true;
            recgprog.StartInfo.RedirectStandardInput = true;
            recgprog.StartInfo.RedirectStandardOutput = true;

            recgprog.Start();
            recgprog.StandardInput.NewLine = "\n";
            recgprog.OutputDataReceived += new DataReceivedEventHandler(recgprog_OutputDataReceived);
            recgprog.BeginOutputReadLine();
        }

        void recgprog_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            //行を受信したら作業用のキューに格納。
            if (!string.IsNullOrEmpty(e.Data))
            {
                lock (unprocessedrecvlines)
                {
                    unprocessedrecvlines.Enqueue(e.Data);
                }
            }
        }

        /// <summary>
        /// 盤面の状態を単純に読む。
        /// </summary>
        /// <returns>認識プログラムからの生のRecogFEN。取り込みに失敗したらnull。</returns>
        public string ReadBoard()
        {
            string result = null;
            lock (unprocessedrecvlines)
            {
                unprocessedrecvlines.Clear();
            }
            recgprog.StandardInput.WriteLine("r");
            recgprog.StandardInput.Flush();
            int sleepspan = 100;    //ループの間にスリープする時間(ms)。
            int timeout = 10000;    //結果が返るまでに待つ最大時間(ms)。
            int timeoutctr = timeout / sleepspan;
            while ((timeoutctr--) > 0)
            {
                string line = null;
                lock (unprocessedrecvlines)
                {
                    if (unprocessedrecvlines.Count > 0)
                    {
                        line = unprocessedrecvlines.Dequeue();
                    }
                }

                if (line != null)
                {
                    if (line.StartsWith("R"))
                    {
                        //認識成功
                        if (line.Length == (1 + 8 * 8 + 7))
                        {
                            Trace.WriteLine("Recognize succeeded.");
                            result = line.Substring(1);
                        }
                    }
                    else if (line.StartsWith("F"))
                    {
                        //認識失敗
                        Trace.WriteLine("Recognize failed.");
                        break;
                    }
                }

                Thread.Sleep(sleepspan);
            }

            Trace.WriteLine("Exiting readBoard().");
            return result;
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            if (!recgprog.HasExited)
            {
                //起動しているので終了する。
                recgprog.CancelOutputRead();
                recgprog.StandardInput.WriteLine("q");
                recgprog.StandardInput.Flush();
                recgprog.StandardError.Close();
                recgprog.StandardInput.Close();
                recgprog.StandardOutput.Close();
                if (!recgprog.WaitForExit(1000))
                {
                    recgprog.Kill();
                }
            }
        }

        #endregion
    }
}
