using BO;
using BO.BO.BaseInfo;
using BO.BO.Bill;
using BO.Utils;
using BO.Utils.BillRpc;
using BWP.WinFormControl;
using BWP.WinFormControl.WeightDataFormat;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WeighAndGrading
{
  public partial class GradeFrom : Form, IAfterLogin
  {
    #region IAfterLogin
    public string RoleName
    {
      get { return "定级员"; }
    }

    public Form Generate()
    {
      if (string.IsNullOrEmpty(ButcherAppContext.Context.UrlConfig.OfflineSqlConnection))
        throw new Exception("请先设置离线数据库并保存");
      if (!LocalDmoSession.ConnectionTest())
        throw new Exception("离线数据库连接失败");
      return this;
    }
    #endregion
    private delegate void InvokeHandler();
    public const string DATA_PATH = "G_A_W_Data";
    const short TANG_TECH = 0;
    const short MAO_TECH = 1;
    List<GradeAndWeight> tangList;
    List<GradeAndWeight> maoList;
    BindingList<GradeAndWeight_Detail> details;
    string discontPath = Path.Combine(DATA_PATH, "Disconts.xml");

    bool connection = false;
    SerialPort weightPort;
    int maxIndex = 0;
    readonly ConcurrentQueue<GradeAndWeight_Detail> noLivestockList;

    bool onWorking = false;
    Thread syncTangGrid, syncMaoGrid, syncDetailGrid;
    Thread syncToServer;
    #region weightNeed
    private IDataFormat _dataFormat;
    private Thread _inQueryThread;
    private bool _mainProcessIsRun;
    readonly StringBuilder _dataStrBuilder = new StringBuilder();
    #endregion

    public GradeFrom()
    {
      InitializeComponent();
      butcherTimeInput.Date = DateTime.Today;
      tangGridView.AutoGenerateColumns = false;
      tangGridView.DataSource = null;
      maoGridView.AutoGenerateColumns = false;
      maoGridView.DataSource = null;
      historyGrid.AutoGenerateColumns = false;
      historyGrid.DataSource = null;
      tangList = new List<GradeAndWeight>();
      maoList = new List<GradeAndWeight>();
      noLivestockList = new ConcurrentQueue<GradeAndWeight_Detail>();
      if (!Directory.Exists(DATA_PATH))
        Directory.CreateDirectory(DATA_PATH);
      connection = ButcherAppContext.Context.UserConfig.Connection;
      AddLivestockBtn();
      BuildDiscontPanel(true);
      weightPort = new SerialPort();
      this.FormClosing += delegate
      {
        if (_inQueryThread != null && _inQueryThread.IsAlive)
          DisableWeight();
        if (syncTangGrid != null && syncTangGrid.IsAlive)
          syncTangGrid.Abort();
        if (syncMaoGrid != null && syncMaoGrid.IsAlive)
          syncMaoGrid.Abort();
        if (syncDetailGrid != null && syncDetailGrid.IsAlive)
          syncDetailGrid.Abort();
        if (syncToServer != null && syncToServer.IsAlive)
          syncToServer.Abort();
      };
    }

    GradeAndWeight_Detail modifyDetail;
    void JiBieButtonClick(Button btn)
    {
      if (details == null)
      {
        MessageBox.Show("请先同步数据");
        return;
      }
      btn.Enabled = false;

      var livestockTag = btn.Tag as CTuple<long, string, short, string>;
      var tech = livestockTag.Item3 == TANG_TECH ? "烫褪" : "毛剥";

      if (modifyDetail == null)
      {
        StartPrintEntity(tech, null);
        AddDetail(livestockTag);
      }
      else
      {
        UpdateDetial(modifyDetail, livestockTag);
        cancelBtn_Click(btn, EventArgs.Empty);
      }
      SetlblSucessVisibleTrue();
      SetlblSucessVisibleFalse();
      btn.Enabled = true;
    }

    void UpdateDetial(GradeAndWeight_Detail detail, CTuple<long, string, short, string> btnTag)
    {
      var techIsEmpty = detail.Technics == null;
      var diff = detail.Technics.HasValue && detail.Technics != btnTag.Item3;
      detail.Livestock_ID = btnTag.Item1;
      detail.Livestock_Name = btnTag.Item2;
      detail.Technics = btnTag.Item3;
      detail.Technics_Name = detail.Technics == 0 ? "烫褪" : "毛剥";
      var current = btnTag.Item3 == 0 ? tangEntity : maoEntity;
      if (current != null)
      {
        detail.Order = current.Order;
        detail.OrderDetail_ID = current.OrderDetail_ID;
      }
      else
      {
        detail.Order = null;
        detail.OrderDetail_ID = null;
      }
      var updateFileNames = new List<string> { "Order", "OrderDetail_ID", "Livestock_ID", "Livestock_Name", "Technics", "Technics_Name" };
      if (techIsEmpty)
        ResetQueue();
    }

    void ResetQueue()
    {
      ClearQuery();
      var stack = new Stack<GradeAndWeight_Detail>();
      foreach (var item in details)
      {
        if (item.Technics == null)
          stack.Push(item);
        else
          break;
      }
      while (stack.Count > 0)
      {
        var bottom = stack.Pop();
        if (bottom.Weight.HasValue)
          noLivestockList.Enqueue(bottom);
      }
    }

    void AddLivestockBtn()
    {
      var livestocks = new List<CTuple<long, string, short, string>>();
      var fileName = Path.Combine(DATA_PATH, "Livestocks.xml");
      if (connection)
      {
        livestocks = BaseInfoRpcUtil.GetLivestockList();
        XmlUtil.SerializerObjToFile(livestocks, fileName);
      }
      else
        livestocks = XmlUtil.DeserializeFromFile<List<CTuple<long, string, short, string>>>(fileName);
      foreach (var item in livestocks)
      {
        var btn = new Button() { Name = "_" + item.Item1, Text = item.Item2, Tag = item, Size = new Size(90, 75), TextAlign = ContentAlignment.MiddleCenter, Margin = new Padding { All = 15 }, Font = new Font("宋体", 18) };

        btn.Click += (sender, e) =>
        {
          JiBieButtonClick(btn);
        };
        if (item.Item3 == TANG_TECH)
        {
          ttPanel.Controls.Add(btn);
        }
        else
        {
          mbPanel.Controls.Add(btn);
        }
      }
      SetMargin(ttPanel);
      SetMargin(mbPanel);
    }

    void SetMargin(FlowLayoutPanel panel)
    {
      for (var i = 0; i < panel.Controls.Count; i++)
      {
        var c = panel.Controls[i];
        if (i % 3 == 0)//left
          c.Margin = new Padding(0, c.Margin.Top, c.Margin.Right, c.Margin.Bottom);
        if ((i + 1) % 3 == 0)//right
          c.Margin = new Padding(c.Margin.Left, c.Margin.Top, 0, c.Margin.Bottom);
        if (i <= 2)//firstRow
          c.Margin = new Padding(c.Margin.Left, 0, c.Margin.Right, c.Margin.Bottom);
      }
    }

    private void syncBtn_Click(object sender, EventArgs e)
    {
      details = LocalGradeAndWeightBL.GetDetails(butcherTimeInput.Date.Value, 50);
      ResetQueue();
      if (details.Any())
        maxIndex = details.First().Index;
      ThreadStartOrAbort(ref syncTangGrid, RefreshTangData);
      ThreadStartOrAbort(ref syncMaoGrid, RefreshMaoData);
      ThreadStartOrAbort(ref syncDetailGrid, RefreshDetailData);
      ChangeSyncBtnState();
    }

    void ThreadStartOrAbort(ref Thread thread, ThreadStart task)
    {
      if (thread == null || !thread.IsAlive)
      {
        thread = new Thread(task);
        thread.Start();
      }
      else
      {
        thread.Abort();
      }
    }

    void ChangeSyncBtnState()
    {
      onWorking = !onWorking;
      butcherTimeInput.Enabled = !onWorking;
      if (onWorking)
      {
        syncBtn.BackColor = Color.FromArgb(15, 215, 107);
        syncBtn.ForeColor = Color.White;
      }
      else
      {
        syncBtn.BackColor = Color.FromKnownColor(KnownColor.Control);
        syncBtn.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
      }
    }

    void RefreshTangData()
    {
      while (true)
      {
        GetLeftList(ref tangList, true);
        this.Invoke(new InvokeHandler(delegate
        {
          BindTangGrid();
        }));
        Thread.Sleep(5000);
      }
    }

    void RefreshMaoData()
    {
      while (true)
      {
        GetLeftList(ref maoList, false);
        this.Invoke(new InvokeHandler(delegate
        {
          BindMaoGrid();
        }));
        Thread.Sleep(5000);
      }
    }

    void RefreshDetailData()
    {
      details = LocalGradeAndWeightBL.GetDetails(butcherTimeInput.Date.Value, 50);
      this.Invoke(new InvokeHandler(delegate
      {
        BindDetailGrid();
      }));
    }

    //void BindLeftByClick(List<GradeAndWeight> list, bool tang)
    //{
    //  GetLeftList(ref list, tang);
    //  if (tang)
    //    BindTangGrid();
    //  else
    //    BindMaoGrid();
    //}

    //void BindDetailByClick()
    //{
    //  details = LocalGradeAndWeightBL.GetDetails(butcherTimeInput.Date.Value, 50);
    //  BindDetailGrid();
    //}

    void GetLeftList(ref List<GradeAndWeight> list, bool tang)
    {
      VerifyConnection();
      if (connection)
      {
        try
        {
          list = GradeAndWeightRpc.GetGradeAndWeightList(butcherTimeInput.Date.Value, tang);
        }
        catch (TimeoutException) { }
      }
    }

    void BindTangGrid()
    {
      tangGridView.DataSource = tangList.OrderBy(x => x.Order).OrderBy(x => x.Finish).ToList();
      if (tangEntity == null && tangGridView.CurrentRow != null)
      {
        tangEntity = tangGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      }
      foreach (DataGridViewRow row in tangGridView.Rows)
      {
        if ((bool)row.Cells["T_Finish"].Value)
          row.DefaultCellStyle.BackColor = Color.YellowGreen;
        if (tangEntity != null && tangEntity.OrderDetail_ID == (long)row.Cells["T_OrderDetail_ID"].Value)
        {
          tangEntity = row.DataBoundItem as GradeAndWeight;
          if (tangEntity.Finish)
            row.DefaultCellStyle.BackColor = Color.Yellow;
          else
            row.DefaultCellStyle.BackColor = tangGridView.RowsDefaultCellStyle.SelectionBackColor;
        }
      }
      InitTangScrollBar();
      tangGridView.ClearSelection();
      try
      {
        if (tangRoll != -1)
          tangGridView.FirstDisplayedScrollingRowIndex = tangRoll;
      }
      catch
      {
        tangRoll = -1;
      }
      tangGridView.Refresh();
    }

    void BindMaoGrid()
    {
      maoGridView.DataSource = maoList.OrderBy(x => x.Order).OrderBy(x => x.Finish).ToList();
      if (maoEntity == null && maoGridView.CurrentRow != null)
      {
        maoEntity = maoGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      }
      foreach (DataGridViewRow row in maoGridView.Rows)
      {
        if ((bool)row.Cells["M_Finish"].Value)
          row.DefaultCellStyle.BackColor = Color.YellowGreen;
        if (maoEntity != null && maoEntity.OrderDetail_ID == (long)row.Cells["M_OrderDetail_ID"].Value)
        {
          maoEntity = row.DataBoundItem as GradeAndWeight;
          if (maoEntity.Finish)
            row.DefaultCellStyle.BackColor = Color.Yellow;
          else
            row.DefaultCellStyle.BackColor = maoGridView.RowsDefaultCellStyle.SelectionBackColor;
        }
      }
      InitMaoScrollBar();
      maoGridView.ClearSelection();
      try
      {
        if (maoRoll != -1)
          maoGridView.FirstDisplayedScrollingRowIndex = maoRoll;
      }
      catch
      {
        maoRoll = -1;
      }
      maoGridView.Refresh();
    }

    void BindDetailGrid()
    {
      historyGrid.DataSource = details;
      foreach (DataGridViewRow row in historyGrid.Rows)
      {
        if ((bool)row.Cells["H_IsDrop"].Value)
          row.DefaultCellStyle.BackColor = Color.Red;
        if (modifyDetail != null && modifyDetail.SID == (long)row.Cells["H_SID"].Value)
        {
          modifyDetail = row.DataBoundItem as GradeAndWeight_Detail;
          row.DefaultCellStyle.BackColor = maoGridView.RowsDefaultCellStyle.SelectionBackColor;
        }
      }
      InitDetailScrollBar();
      historyGrid.ClearSelection();
      try
      {
        if (rightRoll != -1)
          historyGrid.FirstDisplayedScrollingRowIndex = rightRoll;
      }
      catch
      {
        rightRoll = -1;
      }
      historyGrid.Refresh();
    }

    void StartPrintEntity(string gongyijibie, int? index)
    {
      if (!isPrintCheckBox.Checked)
      {
        return;
      }
      if (string.IsNullOrWhiteSpace(gongyijibie))
      {
        return;
      }
      var entity = CreatePrintEntity(gongyijibie, index);
      WeightGradePrint.Print(entity);
    }

    PrintEntity CreatePrintEntity(string jibie, int? index)
    {
      string goodsName;
      if (jibie == "烫褪")
      {
        goodsName = "带皮白条";
      }
      else
      {
        goodsName = "去皮白条";
      }

      var entity = new PrintEntity();
      entity.AccountingUnit_Name = "青岛万福集团股份有限公司";
      entity.Goods_Name = goodsName;
      entity.Date = DateTime.Today;
      entity.Checker = "";
      entity.StoreCondition = "0-4℃";
      entity.Place = "青岛莱西市";
      entity.TelNumber = "0532-88488888";

      int maxindex;
      if (index == null)
      {
        maxindex = LocalGradeAndWeightBL.GetTodayTotalCount(butcherTimeInput.Date.Value);
        maxindex++;
      }
      else
      {
        maxindex = index.Value;
      }

      string indexCode = maxindex.ToString("D6");

      entity.BarCode = string.Format("WF{0}{1:00000}", DateTime.Today.ToString("yyyyMMdd"), indexCode);
      entity._2DQRCode = string.Format("http://www.bwpsoft.com?code={0}&name={1}", entity.BarCode, goodsName);

      return entity;
    }

    private void printBtn_Click(object sender, EventArgs e)
    {
      if (modifyDetail == null)
      {
        return;
      }
      StartPrintEntity(modifyDetail.Technics_Name, modifyDetail.Index);
      cancelBtn_Click(sender, EventArgs.Empty);
    }

    private void configBtn_Click(object sender, EventArgs e)
    {
      new GradeSettingFrom().ShowDialog();
    }

    GradeAndWeight tangEntity;
    GradeAndWeight maoEntity;

    #region weightNeed
    void OpenSerialPort()
    {
      if (GradeContext.Config.RateSet == null)
        throw new Exception("请先配置称相关信息");

      weightPort.PortName = GradeContext.Config.ComSet;
      weightPort.BaudRate = GradeContext.Config.RateSet.Value;
      weightPort.DataBits = GradeContext.Config.BitSet.Value;
      weightPort.ReadBufferSize = 4096 * 100;
      if (!string.IsNullOrEmpty(GradeContext.Config.Format))
        format = "{0:{format}}".Replace("{format}", GradeContext.Config.Format);

      switch (GradeContext.Config.WeightSet)
      {
        case "IND560":
          _dataFormat = new IND560DataFormat();
          break;
        case "Xk3124":
          _dataFormat = new Xk3124DataFormat();
          break;
        case "Xk3190A9":
          _dataFormat = new Xk3190A9DataFormat();
          break;
        default:
          _dataFormat = new Xk3190D10DataFormat();
          break;
      }
      if (!weightPort.IsOpen)
      {
        try
        {
          weightPort.Open();
        }
        catch (InvalidOperationException)
        {
          MessageBox.Show(@"指定的端口已打开");
        }
        catch (UnauthorizedAccessException)
        {
          MessageBox.Show(@"对端口的访问被拒绝");
        }
      }
    }

    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(1);
        }
        char[] buffer = new char[availableCount];
        if (!weightPort.IsOpen)
        {
          continue;
        }
        weightPort.Read(buffer, 0, availableCount);
        foreach (var c in buffer)
        {
          if (c == _dataFormat.Beginchar)
          {
            _dataStrBuilder.Clear();
            _dataStrBuilder.Append(c);
          }
          else if (c == _dataFormat.Endchar && _dataStrBuilder.Length == _dataFormat.DataLength - 1)
          {
            _dataStrBuilder.Append(c);
            bool isStatic;
            string str;
            if (_dataFormat.ParseAscii(_dataStrBuilder.ToString(), out str, out isStatic))
            {
              //稳定后发送
              if (GradeContext.Config.WeightType == 0)
              {
                if (string.IsNullOrEmpty(str))
                  str = "0";
                this.Invoke(new InvokeHandler(delegate()
                {
                  lblChengZhong.Text = string.Format(format, decimal.Parse(str));
                  if (str != "0")
                  {
                    AddWeightDetail(decimal.Parse(lblChengZhong.Text));
                  }
                }));
              }
              //连续发送
              else
              {
                decimal num = 0;
                if (decimal.TryParse(str, out num))
                {
                  this.Invoke(new InvokeHandler(delegate()
                  {
                    lblChengZhong.Text = string.Format(format, num);
                  }));
                  WeighAvgControl.Add(num, isStatic);
                }
                if (WeighAvgControl.TryGetValue(out num))
                {
                  this.Invoke(new InvokeHandler(delegate()
                  {
                    //lblChengZhong.Text = string.Format(format, num);
                    if (str != "0")
                    {
                      //
                      AddWeightDetail(decimal.Parse(string.Format(format, num)));
                    }
                  }));
                }
              }
            }
            _dataStrBuilder.Clear();
          }
          else if (_dataStrBuilder.Length != 0)
          {
            _dataStrBuilder.Append(c);
          }
        }
      }
    }

    private class WeighAvgControl
    {
      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 >= GradeContext.Config.MinWeight && value <= GradeContext.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();
    }

    public void btnStartWeight_Click(object sender, EventArgs e)
    {
      if (details == null)
      {
        MessageBox.Show("请先同步数据");
        return;
      }
      btnStartWeight.Enabled = false;
      OpenSerialPort();
      _mainProcessIsRun = true;
      ReadData();

      btnStopWeight.Enabled = true;


    }
    public void btnStopWeight_Click(object sender, EventArgs e)
    {
      btnStartWeight.Enabled = true;
      DisableWeight();
      btnStopWeight.Enabled = false;
    }
    #endregion

    static object _obj = new object();
    void AddDetail(CTuple<long, string, short, string> livestock)
    {
      lock (_obj)
      {
        var n = 0;
        var currentRow = livestock.Item3 == 0 ? tangEntity : maoEntity;
        var tech = livestock.Item3 == TANG_TECH ? "烫褪" : "毛剥";

        GradeAndWeight_Detail first;
        //TryPeek 尝试返回 ConcurrentQueue<T> 开头处的对象但不将其移除
        //TryDequeue 尝试移除并返回并发队列开头处的对象。
        if (noLivestockList.TryDequeue(out first))
        {
          if (currentRow != null)
          {
            first.OrderDetail_ID = currentRow.OrderDetail_ID;
            first.Order = currentRow.Order;
            n = currentRow.Already;
          }
          first.Date = butcherTimeInput.Date.Value;
          first.Livestock_ID = livestock.Item1;
          first.Livestock_Name = livestock.Item2;
          first.Technics = livestock.Item3;
          first.Technics_Name = tech;
          if (disBtn != null)
          {
            first.Weight = (first.Weight ?? 0) - Convert.ToDecimal(disBtn.Tag);
            SetBtnUnCheck(disBtn);
            disBtn = null;
          }
          LocalGradeAndWeightBL.Update(first, "OrderDetail_ID", "Order", "Date", "Livestock_ID", "Livestock_Name", "Technics", "Technics_Name", "Weight");
          var tag = details.FirstOrDefault(x => x.SID == first.SID);
          if (first != null)
          {
            tag.Date = first.Date;
            tag.Livestock_ID = first.Livestock_ID;
            tag.Livestock_Name = first.Livestock_Name;
            tag.Order = first.Order;
            tag.OrderDetail_ID = first.OrderDetail_ID;
            tag.Technics = first.Technics;
            tag.Technics_Name = first.Technics_Name;
            tag.Weight = first.Weight;
            historyGrid.Refresh();
          }
        }
        else//add  
        {
          maxIndex++;
          var entity = new GradeAndWeight_Detail();
          entity.Index = maxIndex;
          if (currentRow != null)
          {
            entity.OrderDetail_ID = currentRow.OrderDetail_ID;
            entity.Order = currentRow.Order;
            n = currentRow.Already;
          }
          entity.Date = butcherTimeInput.Date.Value;
          entity.Livestock_ID = livestock.Item1;
          entity.Livestock_Name = livestock.Item2;
          entity.Technics = livestock.Item3;
          entity.Technics_Name = tech;
          entity.Time = DateTime.Now;
          entity.Date = butcherTimeInput.Date.Value;
          if (disBtn != null)
          {
            entity.Weight = -Convert.ToDecimal(disBtn.Tag);
            SetBtnUnCheck(disBtn);
            disBtn = null;
          }
          entity.Weight = 0;
          LocalGradeAndWeightBL.Insert(entity);
          details.Insert(0, entity);
          AfterAddBindDetailGrid();
        }

        if (currentRow != null)
        {
          currentRow.Already = n + 1;
          orderLabel.Text = currentRow.Order.ToString();
          alreadyLabel.Text = currentRow.Already.ToString();
          if (tech == "烫褪")
            tangGridView.Refresh();
          else
            maoGridView.Refresh();
        }
        else
        {
          orderLabel.Text = string.Empty;
          alreadyLabel.Text = string.Empty;
        }
      }
    }

    void SetlblSucessVisibleFalse()
    {
      this.Invoke(new InvokeHandler(delegate
      {
        Thread.Sleep(1000);
        lblSucessed.Visible = false;
      }));
    }

    void SetlblSucessVisibleTrue()
    {
      this.Invoke(new InvokeHandler(delegate
      {
        lblSucessed.Visible = true;
        Application.DoEvents();
      }));
    }

    void AfterAddBindDetailGrid()
    {
      if (details.Count > 50)
        details.RemoveAt(50);
      BindDetailGrid();
      // BindDetailByClick();
    }

    void AddWeightDetail(decimal weight)
    {
      lock (_obj)
      {
        weight -= (GradeContext.Config.Discont ?? 0);
        maxIndex++;
        var entity = new GradeAndWeight_Detail();
        entity.Index = maxIndex;
        entity.Weight = (entity.Weight ?? 0) + weight;
        entity.Time = DateTime.Now;
        entity.Date = butcherTimeInput.Date.Value;

        LocalGradeAndWeightBL.Insert(entity);
        details.Insert(0, entity);
        noLivestockList.Enqueue(entity);
      }
      AfterAddBindDetailGrid();
    }

    private void cancelBtn_Click(object sender, EventArgs e)
    {
      foreach (DataGridViewRow row in historyGrid.Rows)
      {
        if (modifyDetail.SID == (long)row.Cells["H_SID"].Value)
        {
          row.DefaultCellStyle.BackColor = historyGrid.RowsDefaultCellStyle.BackColor;
          break;
        }
      }
      historyGrid.Refresh();
      modifyDetail = null;
      modifyPanel.Hide();
    }

    //删除选中
    private void btnDeleteSelected_Click(object sender, EventArgs e)
    {
      if (MessageBox.Show("确定删除选中的称重记录？", "删除选中", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
        return;

      if (modifyDetail == null)
      {
        UMessageBox.Show("请选中要删除的记录");
        return;
      }
      //删除选中  更新 IsDeleted 和 Sync
      lock (_obj)
      {
        modifyDetail.IsDeleted = true;
        LocalGradeAndWeightBL.Update(modifyDetail, "IsDeleted");
        details.Remove(modifyDetail);
        if (modifyDetail.Technics == null)
        {
          ResetQueue();
        }
        BindDetailGrid();
        modifyDetail = null;
        modifyPanel.Hide();
      }
      if (details.Any())
        historyGrid.DataSource = details;
      historyGrid.Refresh();
      //BindDetailByClick();
    }

    private void historyGrid_CellClick(object sender, DataGridViewCellEventArgs e)
    {
      if (e.RowIndex < 0)
        return;
      var currentRow = historyGrid.CurrentRow.DataBoundItem as GradeAndWeight_Detail;
      if (modifyDetail != null)
      {
        foreach (DataGridViewRow row in historyGrid.Rows)
        {
          if (modifyDetail.SID == (long)row.Cells["H_SID"].Value)
          {
            row.DefaultCellStyle.BackColor = historyGrid.RowsDefaultCellStyle.BackColor;
            break;
          }
        }
      }
      modifyDetail = currentRow;
      historyGrid.Refresh();
      stateLabel.Text = string.Format("您正在修改序号为 {0} 的信息", modifyDetail.Index);
      modifyPanel.Show();
    }

    private void tangGridView_CellClick(object sender, DataGridViewCellEventArgs e)
    {
      if (e.RowIndex == -1)
        return;
      if (e.ColumnIndex == tangGridView.Columns.Count - 1)
        return;
      var entity = tangGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      if (tangEntity != null)
      {
        foreach (DataGridViewRow row in tangGridView.Rows)
        {
          if (tangEntity.OrderDetail_ID == (long)row.Cells["T_OrderDetail_ID"].Value)
          {
            row.DefaultCellStyle.BackColor = tangEntity.Finish ? Color.YellowGreen : tangGridView.RowsDefaultCellStyle.BackColor;
            break;
          }
        }
      }
      tangEntity = entity;
      tangGridView.CurrentRow.DefaultCellStyle.SelectionBackColor = tangEntity.Finish ? Color.Yellow : tangGridView.RowsDefaultCellStyle.SelectionBackColor;
      tangGridView.Refresh();
    }

    private void tangGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
      if (e.RowIndex == -1)
        return;
      if (e.ColumnIndex != tangGridView.ColumnCount - 1)
        return;
      var entity = tangGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      if (entity.Finish)
      {
        tangEntity = null;
        return;
      }
      entity.Finish = true;
      LocalGradeAndWeightBL.SetGradeFinish(entity.Order, butcherTimeInput.Date.Value, TANG_TECH);
      tangEntity = null;
      BindTangGrid();
    }

    private void maoGridView_CellClick(object sender, DataGridViewCellEventArgs e)
    {
      if (e.RowIndex == -1)
        return;
      if (e.ColumnIndex == maoGridView.Columns.Count - 1)
        return;
      var entity = maoGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      if (maoEntity != null)
      {
        foreach (DataGridViewRow row in maoGridView.Rows)
        {
          if (maoEntity.OrderDetail_ID == (long)row.Cells["M_OrderDetail_ID"].Value)
          {
            row.DefaultCellStyle.BackColor = maoEntity.Finish ? Color.YellowGreen : maoGridView.RowsDefaultCellStyle.BackColor;
            break;
          }
        }
      }
      maoEntity = entity;
      maoGridView.CurrentRow.DefaultCellStyle.SelectionBackColor = maoEntity.Finish ? Color.Yellow : maoGridView.RowsDefaultCellStyle.SelectionBackColor;
      maoGridView.Refresh();
    }

    private void maoGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
      if (e.RowIndex == -1)
        return;
      if (e.ColumnIndex != maoGridView.ColumnCount - 1)
        return;
      var entity = maoGridView.CurrentRow.DataBoundItem as GradeAndWeight;
      if (entity.Finish)
      {
        maoEntity = null;
        return;
      }
      entity.Finish = true;
      LocalGradeAndWeightBL.SetGradeFinish(entity.Order, butcherTimeInput.Date.Value, MAO_TECH);
      maoEntity = null;
      BindMaoGrid();
    }

    private void discontBtn_Click(object sender, EventArgs e)
    {
      if (new BodyDiscontSetting().ShowDialog() == DialogResult.OK)
        BuildDiscontPanel(false);
    }

    Button disBtn = null;
    void BuildDiscontPanel(bool firstLoad)
    {
      var disconts = new List<BodyDiscontItem>();
      if (!firstLoad)
        VerifyConnection();
      if (connection)
      {
        disconts = GradeAndWeightRpc.GetBodyDiscontItem();
        XmlUtil.SerializerObjToFile(disconts, discontPath);
      }
      else
        disconts = XmlUtil.DeserializeFromFile<List<BodyDiscontItem>>(discontPath);

      disconts = disconts.Where(x => x.Discont > 0).OrderBy(x => x.ID).ToList();
      discontPanel.Controls.Clear();
      foreach (var item in disconts)
      {
        var btn = new Button() { Name = "_D" + item.ID, Text = item.Name, Tag = item.Discont, Size = new Size(70, 60), TextAlign = ContentAlignment.MiddleCenter, Margin = new Padding { Bottom = 30 }, Font = new Font("宋体", 15) };
        btn.Click += (sender, e) =>
        {
          if (modifyDetail == null)
          {
            if (disBtn != btn)
            {
              disBtn = btn;
            }
            else
              disBtn = null;
          }
          else
          {
            modifyDetail.Weight = (modifyDetail.Weight ?? 0) - Convert.ToDecimal(btn.Tag);
            LocalGradeAndWeightBL.Update(modifyDetail, "Weight");
            cancelBtn_Click(sender, EventArgs.Empty);
          }
        };
        discontPanel.Controls.Add(btn);
      }
    }

    private void dropPigBtn_Click(object sender, EventArgs e)
    {
      if (modifyDetail == null)
      {
        UMessageBox.Show("请先选择要设置为掉猪的行", "错误");
        return;
      }
      else
      {
        modifyDetail.IsDrop = !modifyDetail.IsDrop;
        LocalGradeAndWeightBL.Update(modifyDetail, "IsDrop");
        cancelBtn_Click(sender, EventArgs.Empty);
      }
    }

    private void dataConfirmBtn_Click(object sender, EventArgs e)
    {
      new DataConfirm().ShowDialog();
    }

    readonly Color btnSelectForeColor = Color.FromArgb(255, 255, 255);
    readonly Color btnSelectBackColor = Color.FromArgb(66, 163, 218);
    Color btnUnSelectForeColor = SystemColors.ControlText;
    Color btnUnSelectBackColor = Color.FromArgb(225, 225, 225);
    void SetBtnChecked(Button btn)
    {
      btn.BackColor = btnSelectBackColor;
      btn.ForeColor = btnSelectForeColor;
    }

    void SetBtnUnCheck(Button btn)
    {
      btn.BackColor = btnUnSelectBackColor;
      btn.ForeColor = btnUnSelectForeColor;
    }

    #region scrollBar
    int tangRoll = -1;
    private void InitTangScrollBar()
    {
      tangScrollBar.Maximum = (tangGridView.RowCount - tangGridView.DisplayedRowCount(false) + 30) * tangGridView.RowTemplate.Height;
      tangScrollBar.Minimum = 0;
      tangScrollBar.SmallChange = tangGridView.RowTemplate.Height;
      tangScrollBar.LargeChange = tangGridView.RowTemplate.Height * 30;
      this.tangScrollBar.Scroll += (sender, e) =>
      {
        tangRoll = e.NewValue / tangGridView.RowTemplate.Height;
        tangGridView.FirstDisplayedScrollingRowIndex = tangRoll;
      };
    }

    int maoRoll = -1;
    private void InitMaoScrollBar()
    {
      maoScrollBar.Maximum = (maoGridView.RowCount - maoGridView.DisplayedRowCount(false) + 30) * maoGridView.RowTemplate.Height;
      maoScrollBar.Minimum = 0;
      maoScrollBar.SmallChange = maoGridView.RowTemplate.Height;
      maoScrollBar.LargeChange = maoGridView.RowTemplate.Height * 30;
      maoScrollBar.Scroll += (sender, e) =>
      {
        maoRoll = e.NewValue / maoGridView.RowTemplate.Height;
        maoGridView.FirstDisplayedScrollingRowIndex = maoRoll;
      };
    }

    int rightRoll = -1;
    private void InitDetailScrollBar()
    {
      vScrollBar2.Maximum = (historyGrid.RowCount - historyGrid.DisplayedRowCount(false) + 30) * historyGrid.RowTemplate.Height;
      vScrollBar2.Minimum = 0;
      vScrollBar2.SmallChange = historyGrid.RowTemplate.Height;
      vScrollBar2.LargeChange = historyGrid.RowTemplate.Height * 30;

      this.vScrollBar2.Scroll += (sender, e) =>
      {
        rightRoll = e.NewValue / historyGrid.RowTemplate.Height;
        historyGrid.FirstDisplayedScrollingRowIndex = rightRoll;
      };
    }
    #endregion

    bool? last = null;
    void VerifyConnection()
    {
      connection = LoginRpcUtil.TestConnection();
      if (last == connection)
        return;
      var png = "stop.png";
      if (connection)
        png = "working.png";
      var imgPath = Path.Combine(Application.StartupPath, "BWP.WinFormControl.dll");
      var s = Assembly.LoadFile(imgPath).GetManifestResourceStream("BWP.WinFormControl.Images." + png);
      if (this.InvokeRequired)
      {
        this.BeginInvoke(new Action(() =>
        {
          statePic.Image = Image.FromStream(s);
          statePic.Refresh();
        }));
      }
      else
      {
        statePic.Image = Image.FromStream(s);
        statePic.Refresh();
      }

      last = connection;
    }

    private void GradeFrom_Load(object sender, EventArgs e)
    {
      syncToServer = new Thread(ToServerTask);
      syncToServer.Start();
    }

    void ToServerTask()
    {
      while (true)
      {
        VerifyConnection();
        if (connection)
        {
          try
          {
            SyncDiscontToSever();
            LocalGradeAndWeightBL.Sync();
          }
          catch (Exception ex)
          {
            File.WriteAllText(string.Format("{0:yyyyMMddHHmmss}log.txt", DateTime.Now), "错误：" + ex.Message + " \n详细信息：" + ex.StackTrace);
          }
        }
        Thread.Sleep(2000);
      }
    }

    void SyncDiscontToSever()
    {
      var changeFlagPath = Path.Combine(GradeFrom.DATA_PATH, "DiscontsChanged.txt");
      bool existFile = File.Exists(changeFlagPath);
      if (existFile)
      {
        var list = XmlUtil.DeserializeFromFile<List<BodyDiscontItem>>(discontPath);
        var changes = new List<CTuple<long, decimal?>>();
        foreach (var item in list)
          changes.Add(new CTuple<long, decimal?>(item.ID, item.Discont));
        GradeAndWeightRpc.SaveBodyDiscontItem(changes);
        File.Delete(changeFlagPath);
      }
    }

    void ClearQuery()
    {
      if (noLivestockList == null)
        return;
      GradeAndWeight_Detail r;
      while (noLivestockList.TryDequeue(out r))
      { }
    }
  }
}
