using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Media.Converters;
using Aitex.Core.RT.IOCore;
using MECF.Framework.Common.IOCore;
using MECF.Framework.RT.Core.IoProviders;

namespace MECF.Framework.Simulator.Core.IoProviders
{
    public class SimulatorIO : MCProtocolPlcSimulator
    {

        private readonly bool _isInit = false;

        private Random _rd = new Random();
        private const int BufferSize = 640;
        public ObservableCollection<NotifiableIoItem> DiItemList { get; private set; }
        public ObservableCollection<NotifiableIoItem> DoItemList { get; private set; }
        public ObservableCollection<NotifiableIoItem> AiItemList { get; private set; }
        public ObservableCollection<NotifiableIoItem> AoItemList { get; private set; }

        private string _source;

        public SimulatorIO(int port, string source, string ioMapPathFile)
            : base("127.0.0.1", port)
        {
            _source = source;

            // DI Buffer1
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize], Type = IoType.DI, Offset = 0, Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            // DI Buffer2
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize], Type = IoType.DI, Offset = 1, Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            // DI Buffer3
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize],
                Type = IoType.DI,
                Offset = 2,
                Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            // DO Buffer1
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize], Type = IoType.DO, Offset = 0, Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            // DO Buffer2
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize],
                Type = IoType.DO,
                Offset = 1,
                Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            // DO Buffer3
            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize],
                Type = IoType.DO,
                Offset = 2,
                Size = BufferSize,
                BoolValue = new bool[BufferSize]
            });

            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize], Type = IoType.AI, Offset = 0, Size = BufferSize,
                ShortValue = new ushort[BufferSize]
            });

            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize],
                Type = IoType.AI,
                Offset = 1,
                Size = BufferSize,
                ShortValue = new ushort[BufferSize]
            });

            _buffers.Add(new PlcBuffer()
            {
                Buffer = new byte[BufferSize], Type = IoType.AO, Offset = 0, Size = BufferSize,
                ShortValue = new ushort[BufferSize]
            });

            var lstBuffers = new List<IoBlockItem>
            {
                new IoBlockItem() { Type = IoType.DI, Index = 0, Size = BufferSize }, // DI Block1
                new IoBlockItem() { Type = IoType.DI, Index = 1, Size = BufferSize }, // DI Block2
                new IoBlockItem() { Type = IoType.DI, Index = 2, Size = BufferSize }, // DI Block3
                new IoBlockItem() { Type = IoType.DO, Index = 0, Size = BufferSize }, // DO Block1
                new IoBlockItem() { Type = IoType.DO, Index = 1, Size = BufferSize }, // DO Block2
                new IoBlockItem() { Type = IoType.DO, Index = 2, Size = BufferSize }, // DO Block3
                new IoBlockItem() { Type = IoType.AI, Index = 0, Size = BufferSize },
                new IoBlockItem() { Type = IoType.AI, Index = 1, Size = BufferSize },
                new IoBlockItem() { Type = IoType.AO, Index = 0, Size = BufferSize }
            };

            SimulatorIoManager.Instance.CreateBuffer(source, lstBuffers);
            SimulatorIoManager.Instance.SetIoMap(source, ioMapPathFile);

            Init();

            _isInit = true;
        }

        void Init()
        {
            if (DiItemList == null)
            {
                List<DIAccessor> diItems = IO.GetDiList(_source);
                if (diItems != null)
                {
                    DiItemList = new ObservableCollection<NotifiableIoItem>();
                    foreach (var diItem in diItems)
                    {
                        NotifiableIoItem item = new NotifiableIoItem()
                        {
                            Name = diItem.Name,
                            Index = diItem.Index,
                            Description = diItem.Description,
                            BoolValue = diItem.Value,
                            Address = diItem.Addr,
                            BlockOffset = diItem.BlockOffset,
                            BlockIndex = diItem.Index,
                        };
                        DiItemList.Add(item);
                    }
                }
            }

            if (DoItemList == null)
            {
                List<DOAccessor> doItems = IO.GetDoList(_source);
                if (doItems != null)
                {
                    DoItemList = new ObservableCollection<NotifiableIoItem>();
                    foreach (var ioItem in doItems)
                    {
                        NotifiableIoItem item = new NotifiableIoItem()
                        {
                            Name = ioItem.Name,
                            Index = ioItem.Index,
                            Description = ioItem.Description,
                            BoolValue = ioItem.Value,
                            Address = ioItem.Addr,
                            BlockOffset = ioItem.BlockOffset,
                            BlockIndex = ioItem.Index,
                        };
                        DoItemList.Add(item);
                    }
                }
            }

            if (AiItemList == null)
            {
                List<AIAccessor> aiItems = IO.GetAiList(_source);
                if (aiItems != null)
                {
                    AiItemList = new ObservableCollection<NotifiableIoItem>();
                    foreach (var ioItem in aiItems)
                    {
                        NotifiableIoItem item = new NotifiableIoItem()
                        {
                            Name = ioItem.Name,
                            Index = ioItem.Index,
                            Description = ioItem.Description,
                            ShortValue = (short)ioItem.Value,
                            Address = ioItem.Addr,
                            BlockOffset = ioItem.BlockOffset,
                            BlockIndex = ioItem.Index,
                        };
                        AiItemList.Add(item);
                    }
                }
            }

            if (AoItemList == null)
            {
                List<AOAccessor> aoItems = IO.GetAoList(_source);
                if (aoItems != null)
                {
                    AoItemList = new ObservableCollection<NotifiableIoItem>();
                    foreach (var ioItem in aoItems)
                    {
                        NotifiableIoItem item = new NotifiableIoItem()
                        {
                            Name = ioItem.Name,
                            Index = ioItem.Index,
                            Description = ioItem.Description,
                            ShortValue = (short)ioItem.Value,
                            Address = ioItem.Addr,
                            BlockOffset = ioItem.BlockOffset,
                            BlockIndex = ioItem.Index,
                        };
                        AoItemList.Add(item);
                    }
                }
            }
        }

        protected override bool OnTimer()
        {
            if (!_isInit) return true;

            if (DiItemList != null)
            {
                foreach (var notifiableIoItem in DiItemList)
                {
                    if (notifiableIoItem.HoldValue)
                    {
                        IO.DI[notifiableIoItem.Name].Value = notifiableIoItem.BoolValue;
                    }

                    notifiableIoItem.BoolValue = IO.DI[notifiableIoItem.Name].Value;
                    notifiableIoItem.InvokePropertyChanged("BoolValue");
                }
            }

            if (DoItemList != null)
            {
                foreach (var notifiableIoItem in DoItemList)
                {
                    if (notifiableIoItem.HoldValue)
                    {
                        var _plcBuff = _buffers.FirstOrDefault(x => x.Type == IoType.DO);
                        if (_plcBuff != null)
                            _plcBuff.BoolValue[notifiableIoItem.Index] = notifiableIoItem.BoolValue;

                        IO.DO[notifiableIoItem.Name].Value = notifiableIoItem.BoolValue;
                    }
                    else
                        notifiableIoItem.BoolValue = IO.DO[notifiableIoItem.Name].Value;
                    
                    notifiableIoItem.InvokePropertyChanged("BoolValue");
                }
            }

            if (AiItemList != null)
            {
                foreach (var notifiableIoItem in AiItemList)
                {
                    if (notifiableIoItem.HoldValue)
                    {
                        IO.AI[notifiableIoItem.Name].Value = notifiableIoItem.ShortValue;
                    }

                    notifiableIoItem.ShortValue = (short)IO.AI[notifiableIoItem.Name].Value;
                    notifiableIoItem.InvokePropertyChanged("ShortValue");
                }
            }

            if (AoItemList != null)
            {
                foreach (var notifiableIoItem in AoItemList)
                {
                    if (notifiableIoItem.HoldValue)
                        IO.AO[notifiableIoItem.Name].Value = notifiableIoItem.ShortValue;
                    else
                        notifiableIoItem.ShortValue = (short)IO.AO[notifiableIoItem.Name].Value;
                    notifiableIoItem.InvokePropertyChanged("ShortValue");
                }
            }

            foreach (var plcBuffer in _buffers)
            {
                //IO修改 ---> PLC  
                if (plcBuffer.Type == IoType.DI)
                {
                    var ioBuffers = SimulatorIoManager.Instance.GetDiBuffer(_source);
                    if (ioBuffers != null)
                    {
                        foreach (var ioBuffer in ioBuffers)
                        {
                            if (plcBuffer.Offset == ioBuffer.Key)
                            {
                                plcBuffer.BoolValue = ioBuffer.Value;
                            }
                        }
                    }
                }

                // PLC --> IO
                if (plcBuffer.Type == IoType.DO)
                {
                    var ioBuffers = SimulatorIoManager.Instance.GetDoBuffer(_source);
                    if (ioBuffers != null)
                    {
                        foreach (var buffer in ioBuffers)
                        {
                            if (plcBuffer.Offset == buffer.Key)
                            {
                                SimulatorIoManager.Instance.SetDoBuffer(_source, plcBuffer.Offset, plcBuffer.BoolValue);
                            }
                        }
                    }
                }

                //IO修改 ---> PLC  
                if (plcBuffer.Type == IoType.AI)
                {
                    var ioBuffers = SimulatorIoManager.Instance.GetAiBuffer(_source);
                    if (ioBuffers != null)
                    {
                        foreach (var buffer in ioBuffers)
                        {
                            if (plcBuffer.Offset == buffer.Key)
                            {
                                plcBuffer.ShortValue = Array.ConvertAll(buffer.Value, Convert.ToUInt16);
                            }
                        }
                    }
                }

                // PLC --> IO
                if (plcBuffer.Type == IoType.AO)
                {
                    var ioBuffers = SimulatorIoManager.Instance.GetAoBuffer(_source);
                    if (ioBuffers != null)
                    {
                        foreach (var buffer in ioBuffers)
                        {
                            if (plcBuffer.Offset == buffer.Key)
                            {
                                SimulatorIoManager.Instance.SetAoBuffer(_source, plcBuffer.Offset,
                                    Array.ConvertAll(plcBuffer.ShortValue, x => (float)x));
                            }
                        }
                    }
                }
            }

            return true;
        }
    }
}