using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using BLuELinkageBase.Common;
using BLuELinkageBase.Main;
using Common;

namespace BLuESample.UI
{
    /// <summary>
    /// メイン画面
    /// </summary>
    public partial class MainForm : Form
    {
        #region フィールド
        /// <summary>
        /// 連携管理
        /// </summary>
        private ApLinkageManager _LinkageManager = null;

        /// <summary>
        /// メッセージ文字列
        /// </summary>
        private StringBuilder _Message = new StringBuilder();
        #endregion

        #region コンストラクタ
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            // 連携管理を生成する
            this._LinkageManager = ApLinkageManager.Create();

            // コントロールの活性制御を行う
            SetControlEnabled();
        }
        #endregion

        #region ボタンのイベントハンドラ
        /// <summary>
        /// 連携開始／終了ボタンのClickイベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="e">EventArgs</param>
        private void btnLink_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(this._LinkageManager.LinkageNo))
                {
                    // 連携番号が無い場合（未連携の場合）は、連携モードを取得する
                    LinkageMode mode = LinkageMode.Push;
                    if (this.radRequest.Checked)
                    {
                        // 要求モードの場合
                        mode = LinkageMode.Request;
                    }

                    // 連携開始（StartLinkage）コマンドを呼ぶ
                    var result = this._LinkageManager.StartLinkage(mode, this.txtApName.Text);

                    if (result.Result == ResultStatus.Success)
                    {
                        // 連携開始に成功した場合は、イベントハンドラを追加する
                        this._LinkageManager.ChangeLinkage -= new CommonResultEventHandler<bool>(OnChangeLinkage);
                        this._LinkageManager.ChangeLinkage += new CommonResultEventHandler<bool>(OnChangeLinkage);
                        this._LinkageManager.ChangeStatus -= new CommonResultEventHandler<List<Dictionary<string, string>>>(OnChangeStatus);
                        this._LinkageManager.ChangeStatus += new CommonResultEventHandler<List<Dictionary<string, string>>>(OnChangeStatus);
                        this._LinkageManager.PushValue -= new CommonResultEventHandler<List<ValueObjectEx>>(OnPushValue);
                        this._LinkageManager.PushValue += new CommonResultEventHandler<List<ValueObjectEx>>(OnPushValue);

                        // コントロールの活性制御を行う
                        SetControlEnabled();
                    }
                    else
                    {
                        // 連携開始に失敗した場合
                        MessageBox.Show(string.Format("連携開始失敗\r\nエラーコード {0}", result.ErrorCode));
                    }
                }
                else
                {
                    // 連携番号がある場合（連携中の場合）は、連携終了（StopLinkage）コマンドを呼ぶ
                    var result = this._LinkageManager.StopLinkage();

                    if (result.Result == ResultStatus.Success)
                    {
                        // 連携終了に成功した場合は、イベントハンドラを削除する
                        this._LinkageManager.ChangeLinkage -= new CommonResultEventHandler<bool>(OnChangeLinkage);
                        this._LinkageManager.ChangeStatus -= new CommonResultEventHandler<List<Dictionary<string, string>>>(OnChangeStatus);
                        this._LinkageManager.PushValue -= new CommonResultEventHandler<List<ValueObjectEx>>(OnPushValue);

                        // コントロールの活性制御を行う
                        SetControlEnabled();
                    }
                    else
                    {
                        // 連携終了に失敗した場合
                        MessageBox.Show(string.Format("連携終了失敗\r\nエラーコード {0}", result.ErrorCode));
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// 測定値要求ボタンのClickイベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="e">EventArgs</param>
        private void btnRequest_Click(object sender, EventArgs e)
        {
            try
            {
                if (!string.IsNullOrEmpty(this._LinkageManager.LinkageNo))
                {
                    // 連携番号がある場合（連携中の場合）は、測定値要求ボタンを非活性にする
                    this.btnRequest.Enabled = false;

                    // 測定値要求（RequestValue）コマンドを呼ぶ
                    this._LinkageManager.RequestValue();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        /// <summary>
        /// クリアボタンのClickイベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="e">EventArgs</param>
        private void btnClear_Click(object sender, EventArgs e)
        {
            // メッセージ文字列をクリアする
            this._Message.Clear();

            // メッセージテキストボックスをクリアする
            this.txtMessage.Text = string.Empty;
        }
        #endregion

        #region 非公開メソッド
        /// <summary>
        /// コントロールの活性制御を行う
        /// </summary>
        private void SetControlEnabled()
        {
            if (string.IsNullOrEmpty(this._LinkageManager.LinkageNo))
            {
                // 連携番号が無い場合（未連携の場合）
                this.lblLink.Text = "未連携";
                this.lblLink.BackColor = Color.LightGray;

                // モードラジオボタンを活性にする
                this.grpMode.Enabled = true;
                // アプリ名称テキストボックスを活性にする
                this.txtApName.Enabled = true;

                // 測定値要求ボタンを非活性にする
                this.btnRequest.Enabled = false;
            }
            else
            {
                // 連携番号がある場合（連携中の場合）
                this.lblLink.Text = "連携番号 " + this._LinkageManager.LinkageNo;
                if (this.radRequest.Checked)
                {
                    // 要求モードの場合
                    this.lblLink.BackColor = Color.LightPink;
                }
                else
                {
                    // 押込モードの場合
                    this.lblLink.BackColor = Color.LightGreen;
                }

                // モードラジオボタンを非活性にする
                this.grpMode.Enabled = false;
                // アプリ名称テキストボックスを非活性にする
                this.txtApName.Enabled = false;

                if (this.radRequest.Checked)
                {
                    // 要求モードの場合は、測定値要求ボタンを活性にする
                    this.btnRequest.Enabled = true;
                }
            }
        }

        /// <summary>
        /// 測定器の文字列を取得する
        /// </summary>
        /// <param name="deviceList">測定器のリスト</param>
        /// <returns>測定器の文字列</returns>
        private string GetDeviceString(List<Dictionary<string, string>> deviceList)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("状態変更イベント発生");
            if (deviceList != null)
            {
                foreach (Dictionary<string, string> device in deviceList)
                {
                    sb.AppendLine("Ln[" + (device.ContainsKey("Ln") ? device["Ln"] : string.Empty) + "]");
                    sb.AppendLine("Model[" + (device.ContainsKey("Model") ? device["Model"] : string.Empty) + "]");
                    sb.AppendLine("Serial[" + (device.ContainsKey("Serial") ? device["Serial"] : string.Empty) + "]");
                    sb.AppendLine("UniqueNo[" + (device.ContainsKey("UniqueNo") ? device["UniqueNo"] : string.Empty) + "]");
                    sb.AppendLine("Kind[" + (device.ContainsKey("Kind") ? device["Kind"] : string.Empty) + "]");
                    sb.AppendLine("Calibration[" + (device.ContainsKey("Calibration") ? device["Calibration"] : string.Empty) + "]");
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// 測定値オブジェクトの文字列を取得する
        /// </summary>
        /// <param name="objList">測定値オブジェクトのリスト</param>
        /// <returns>測定値オブジェクトの文字列</returns>
        private string GetValueObjectString(List<ValueObjectEx> objList)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("測定値押込イベント発生");
            if (objList != null)
            {
                foreach (ValueObjectEx obj in objList)
                {
                    sb.AppendLine(string.Format("Ln[{0}] Model[{1}] Serial[{2}] UniqueNo[{3}]", obj.Ln, obj.Model, obj.Serial, obj.UniqueNo));
                    sb.AppendLine("Label[" + obj.Label + "]");
                    sb.AppendLine(string.Format("Value[{0}] AuxUnit[{1}] Unit[{2}]", obj.Value, obj.AuxUnit, obj.Unit));
                    sb.AppendLine("Function[" + obj.Function + "]");
                    sb.AppendLine("Timestamp[" + obj.Timestamp + "]");

                    foreach (KeyValuePair<string, string> pair in obj.ExtraData)
                    {
                        sb.AppendLine(string.Format("Key[{0}]  Value[{1}]", pair.Key, pair.Value));
                    }
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// メッセージ文字列を追加する
        /// </summary>
        /// <param name="str">追加する文字列</param>
        private void AppendMessage(string str)
        {
            lock (this._Message)
            {
                this._Message.AppendLine(str);

                this.Invoke((Action)(() =>
                {
                    // 画面に表示する
                    this.txtMessage.Text = this._Message.ToString();
                }));
            }
        }
        #endregion

        #region イベントハンドラ
        /// <summary>
        /// 連携状態変更イベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="args">連携状態EventArgs</param>
        private void OnChangeLinkage(object sender, CommonResultEventArgs<bool> args)
        {
            Task task = Task.Factory.StartNew(() =>
            {
                this.Invoke((Action)(() =>
                {
                    // コントロールの活性制御を行う
                    SetControlEnabled();
                }));
            });
        }

        /// <summary>
        /// 状態変更イベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="args">測定器のリストEventArgs</param>
        private void OnChangeStatus(object sender, CommonResultEventArgs<List<Dictionary<string, string>>> args)
        {
            Task task = Task.Factory.StartNew(() =>
            {
                AppendMessage(GetDeviceString(args.Result));
            });
        }

        /// <summary>
        /// 測定値押込イベントハンドラ
        /// </summary>
        /// <param name="sender">イベント発生元</param>
        /// <param name="args">測定値オブジェクトのリストEventArgs</param>
        private void OnPushValue(object sender, CommonResultEventArgs<List<ValueObjectEx>> args)
        {
            Task task = Task.Factory.StartNew(() =>
            {
                if (string.IsNullOrEmpty(args.ErrorCode))
                {
                    // 正常の場合
                    AppendMessage(GetValueObjectString(args.Result));
                }
                else
                {
                    // エラーの場合
                    AppendMessage("ErrorCode[" + args.ErrorCode + "]");
                }

                this.Invoke((Action)(() =>
                {
                    // コントロールの活性制御を行う
                    SetControlEnabled();
                }));
            });
        }
        #endregion
    }
}
