using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.Collections.Concurrent;
using ButcherFactory.Utils;

namespace ButcherFactory.Controls
{
  public partial class WeightControl : UserControl
  {
    WeightConfig config;
    SerialPort weightPort;
    IDataFormat _dataFormat;
    Thread _inQueryThread;
    private bool _mainProcessIsRun;
    readonly StringBuilder _dataStrBuilder = new StringBuilder();
    private bool switchOn;

    [Browsable(true), Description("称设置标识")]
    public string WeightFalg { get; set; }

    [Browsable(true), Description("读取到值的事件")]
    public event Action<decimal> ReceivedValue;

    [Browsable(false)]
    public decimal Weight
    {
      get
      {
        var v = decimal.Parse(lblChengZhong.Text);
        if (config != null)
          v -= (config.Discont ?? 0);
        return v;
      }
    }

    public WeightControl()
    {
      InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);
      var parentForm = ControlsUtil.GetParentFormm(this);
      if (parentForm != null)
      {
        parentForm.FormClosing += delegate
        {
          if (_inQueryThread != null && _inQueryThread.IsAlive)
          {
            DisableWeight();
          }
        };
      }
    }

    private void settingBtn_Click(object sender, EventArgs e)
    {
      if (new WeightSettingFrom(WeightFalg).ShowDialog() == DialogResult.OK)
        config = WeightConfig.Init(WeightFalg);
    }

    private void weightSwitch_Click(object sender, EventArgs e)
    {
      try
      {
        if (config == null)
        {
          config = WeightConfig.Init(WeightFalg);
          WeighAvgControl.config = config;
        }
        settingBtn.Enabled = switchOn;
        switchOn = !switchOn;
        ChangeWeightBtnState();
        if (switchOn)
        {
          OpenSerialPort();
          _mainProcessIsRun = true;
          ReadData();
        }
        else
        {
          DisableWeight();
        }
      }
      catch
      {
        switchOn = !switchOn;
        settingBtn.Enabled = !switchOn;
        ChangeWeightBtnState();
        throw;
      }
      finally
      {
        this.Focus();
      }
    }

    void ChangeWeightBtnState()
    {
      if (switchOn)
        weightSwitch.Text = "停止称重";
      else
        weightSwitch.Text = "启用称重";
    }

    void OpenSerialPort()
    {
      if (!switchOn)
        return;
      if (config.RateSet == null)
        throw new Exception("请先配置称相关信息");
      weightPort = new SerialPort();
      weightPort.PortName = config.ComSet;
      weightPort.BaudRate = config.RateSet.Value;
      weightPort.DataBits = config.BitSet.Value;
      weightPort.ReadBufferSize = 4096 * 100;
      if (!string.IsNullOrEmpty(config.Format))
        format = "{0:{format}}".Replace("{format}", config.Format);

      switch (config.WeightSet)
      {
        case "IND560":
          _dataFormat = new IND560DataFormat();
          break;
        case "AHS3000":
          _dataFormat = new AoHaoSi3000DataFormat();
          break;
        case "Xk3124":
        case "IND231":
          _dataFormat = new Xk3124DataFormat();
          break;
        case "Xk3190A9":
          _dataFormat = new Xk3190A9DataFormat();
          break;
        case "JST2018S":
          _dataFormat = new JST2018SDataFormat();
          break;
        case "一体称"://02 10 53 54 2C 47 53 3A 20 30 30 30 31 2E 37 31 6B 67 A8 FE 00 49 01 00 AC 99 A8 FE 00
          _dataFormat = new CombineWeightDataFormat();
          break;
        default:
          _dataFormat = new Xk3190D10DataFormat();
          break;
      }
      if (!weightPort.IsOpen)
      {
        try
        {
          weightPort.Open();
        }
        catch (InvalidOperationException)
        {
          throw new Exception("指定的端口已打开");
        }
        catch (UnauthorizedAccessException)
        {
          throw new Exception("对端口的访问被拒绝");
        }
      }
    }

    void ReadData()
    {
      _inQueryThread = new Thread(InQuery);
      _inQueryThread.Start();
    }

    string format = "{0:0.00}";

    private void InQuery()
    {
      while (_mainProcessIsRun)
      {
        int availableCount = weightPort.BytesToRead;
        if (availableCount == 0)
        {
          Thread.Sleep(10);
        }
        byte[] buffer = new byte[availableCount];
        weightPort.Read(buffer, 0, availableCount);
        foreach (var b in buffer)
        {
          var c = (char)b;
          if (c == _dataFormat.Beginchar)
          {
            _dataStrBuilder.Clear();
            _dataStrBuilder.Append(c);
          }
          //针对JST2018不合适，需要改成&&
          else if (c == _dataFormat.Endchar || _dataStrBuilder.Length >= _dataFormat.Bufsize)
          {
            _dataStrBuilder.Append(c);
            bool isStatic;
            string str;
            if (_dataFormat.ParseAscii(_dataStrBuilder.ToString(), out str, out isStatic))
            {
              if (config.WeightType == 0)
              {
                if (string.IsNullOrEmpty(str))
                  str = "0";
                this.Invoke(new Action(delegate()
                {
                  decimal v = 0;
                  decimal.TryParse(str, out v);
                  lblChengZhong.Text = string.Format(format, v);
                  if (str != "0")
                  {
                    if (ReceivedValue != null)
                      ReceivedValue(v - (config.Discont ?? 0));
                  }
                }));
              }
              else
              {
                decimal num = 0;
                if (decimal.TryParse(str, out num))
                {
                  this.Invoke(new Action(delegate()
                  {
                    lblChengZhong.Text = string.Format(format, num);
                  }));
                  WeighAvgControl.Add(num, isStatic);
                }
                if (WeighAvgControl.TryGetValue(out num))
                {
                  this.Invoke(new Action(delegate()
                  {
                    if (str != "0")
                    {
                      if (ReceivedValue != null)
                        ReceivedValue(num - (config.Discont ?? 0));
                    }
                  }));
                }
              }
            }
            _dataStrBuilder.Clear();
          }
          else
          {
            _dataStrBuilder.Append(c);
          }
        }
      }
    }

    private class WeighAvgControl
    {
      public static WeightConfig config;
      public static bool TryGetValue(out decimal result)
      {
        List<Tuple<decimal, bool>> list;
        if (mWeighList.TryDequeue(out list))
        {
          var r = list.Where(x => x.Item2).Select(x => x.Item1).GroupBy(x => x);
          var firstOrDefault = r.OrderByDescending(x => x.Count()).FirstOrDefault();
          if (firstOrDefault != null)
          {
            result = firstOrDefault.Key;
            return true;
          }
          result = 0;
          return false;
        }
        result = 0;
        return false;
      }

      static ConcurrentQueue<List<Tuple<decimal, bool>>> mWeighList = new ConcurrentQueue<List<Tuple<decimal, bool>>>();

      static List<Tuple<decimal, bool>> _list = new List<Tuple<decimal, bool>>();

      public static void Add(decimal value, bool isStatic)
      {
        if (value >= config.MinWeight && value <= config.MaxWeight)
        {
          _list.Add(new Tuple<decimal, bool>(value, isStatic));
        }
        else
        {
          if (_list.Count > 0)
          {
            mWeighList.Enqueue(_list);
            _list = new List<Tuple<decimal, bool>>();
          }
        }
      }
    }

    void DisableWeight()
    {
      _mainProcessIsRun = false;
      lblChengZhong.Text = string.Format(format, 0);
      format = "{0:0.00}";
      Thread.Sleep(10);
      if (_inQueryThread.IsAlive)
      {
        _inQueryThread.Abort();
      }
      if (weightPort.IsOpen)
        weightPort.Close();
    }

    private void roundPanel2_Paint(object sender, PaintEventArgs e)
    {

    }
  }
}
