using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FLY.Modbus
{

    /// <summary>
    /// PLC 区管理
    /// </summary>
    public abstract class AreaManager 
    {
        /// <summary>
        /// 现在工作中的读取计划任务
        /// </summary>
        public List<Plan> plan = new List<Plan>();

        /// <summary>
        /// 当true, plan_buffer -> plan
        /// </summary>
        protected bool isPlanChanged = false;

        /// <summary>
        /// 设置读取计划任务时的缓冲，目前线程安全
        /// </summary>
        protected List<Plan> plan_buffer = new List<Plan>();

        /// <summary>
        /// 当前执行的计划任务序号
        /// </summary>
        protected int currIndex = 0;
        /// <summary>
        /// 获取下一个plan，返回下一个plan，若已经到了最后一个，则在开始
        /// 下一个之前，返回一个null
        /// </summary>
        /// <returns></returns>
        public Plan GetNextPlan()
        {
            int cnt = plan.Count();
            if (cnt <= 0) return null;
            if (currIndex >= cnt)
            {
                currIndex = 0;
                return null;
            }
            Plan p = plan.ElementAt(currIndex);
            currIndex += 1;
            return p;
        }

        /// <summary>
        /// 清除doing状态
        /// </summary>
        public void ClearPlanState()
        {
            foreach (Plan p in plan)
            {
                p.isDoing = false;
            }
            currIndex = 0;
        }
        /// <summary>
        /// 一个property 对应 N个 PLC寄存器;
        /// 地址必须是从小到大排列，不能有重叠
        /// </summary>
        public List<DataToRegs> regs = new List<DataToRegs>();

        protected PLCAddressArea area;

        protected int maxOfOneRead = 50;
        public AreaManager(PLCAddressArea area, int maxOfOneRead)
        {
            this.area = area;
            this.maxOfOneRead = maxOfOneRead;

        }
        /// <summary>
        /// 根据DataToRegs列表构建数组
        /// </summary>
        public void BuildArray(List<DataToRegs> DRmap)
        {
            List<DataToRegs> drs = DRmap.FindAll((c) =>
            {
                return c.dataArea == area;
            });
            // 分配寄存器存储空间
            regs.Clear();
            foreach (DataToRegs dr in drs)
            {
                regs.Add(dr);
            }
            regs.Sort((r1, r2) =>
            {
                if (r1.addr < r2.addr)
                    return -1;
                else if (r1.addr > r2.addr)
                    return 1;
                else
                    return 0;
            });
        }
        /// <summary>
        /// 
        /// </summary>
        public void MakePlanReal()
        {
            lock (plan_buffer)
            {
                if (isPlanChanged)
                {
                    isPlanChanged = false;
                    plan.Clear();
                    plan.AddRange(plan_buffer);
                }
            }
        }


        /// <summary>
        /// 根据当前regs的配置指定读寄存器的计划
        /// 只是设置 plan_buffer
        /// </summary>
        public abstract void MakePlan();

        /// <summary>
        /// 从本地PC 属性获取value 更新到 本地PLC 寄存器，
        /// 目的: 当从PLC读取寄存器值时，能比较不同，产生变化事件
        /// </summary>
        public void SetAllIsPlcValueChanged()
        {
            foreach (DataToRegs dr in regs)
            {
                dr.isPlcValueChanged = true;
            }
        }

    }

    /// <summary>
    /// 每次向PLC读取寄存器的任务
    /// </summary>
    public class Plan
    {
        /// <summary>
        /// coil or register
        /// </summary>
        public PLCAddressArea area;

        /// <summary>
        /// 地址
        /// </summary>
        public int addr;

        /// <summary>
        /// 数量
        /// </summary>
        public int num;
        /// <summary>
        /// 是否正在读取
        /// </summary>
        public bool isDoing;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="area"></param>
        /// <param name="addr"></param>
        /// <param name="num"></param>
        public Plan(PLCAddressArea area, int addr, int num)
        {
            this.area = area;
            this.addr = addr;
            this.num = num;
        }
    }


}
