using PlcSiemens.O;
using PlcSiemens.Protocol.Common;
using PlcSiemens.Protocol.Header;

namespace PlcSiemens.Protocol.Iso
{
    public class IsoControlPdu : IBuildRequest, IBuildResponse
    {
        public TpktHeader Tpkt { get; set; } // TPKT Header
        public CotpHeader Cotp { get; set; } // COPT Header for CONNECTION stuffs

        public int IsoPduSize { get; set; }
        public ushort SrcRef { get; set; }
        public ushort DstRef { get; set; }
        public ushort SrcTSap { get; set; }
        public ushort DstTSap { get; set; }

        /// <summary>
        /// 该报发送长度和接受长度都应该为22
        /// </summary>
        public IsoControlPdu()
        {
            Tpkt = new TpktHeader();
            Cotp = new CotpHeader();
        }

        public void Build()
        {
            Cotp.Params.PduSizeCode = 0xC0;
            Cotp.Params.PduSizeLen = 0x01;
            switch (IsoPduSize)
            {
                case 128:
                    Cotp.Params.PduSizeVal = 0x07;
                    break;

                case 256:
                    Cotp.Params.PduSizeVal = 0x08;
                    break;

                case 512:
                    Cotp.Params.PduSizeVal = 0x09;
                    break;

                case 1024:
                    Cotp.Params.PduSizeVal = 0x0A;
                    break;

                case 2048:
                    Cotp.Params.PduSizeVal = 0x0B;
                    break;

                case 4096:
                    Cotp.Params.PduSizeVal = 0x0C;
                    break;

                case 8192:
                    Cotp.Params.PduSizeVal = 0x0D;
                    break;

                default:
                    Cotp.Params.PduSizeVal = 0x0B;  // Our Default
                    break;
            }
            // Build TSAPs
            Cotp.Params.Tsap[0] = 0xC1;   // code that identifies source TSAP
            Cotp.Params.Tsap[1] = 2;      // source TSAP Len
            Cotp.Params.Tsap[2] = (byte)((SrcTSap >> 8) & 0xFF); // HI part
            Cotp.Params.Tsap[3] = (byte)(SrcTSap & 0xFF); // LO part

            Cotp.Params.Tsap[4] = 0xC2; // code that identifies dest TSAP
            Cotp.Params.Tsap[5] = 2;    // dest TSAP Len
            Cotp.Params.Tsap[6] = (byte)((DstTSap >> 8) & 0xFF); // HI part
            Cotp.Params.Tsap[7] = (byte)(DstTSap & 0xFF); // LO part
            Cotp.Params.TsapLenth = 8;
            // Params length
            var parLen = 11;            // 2 Src TSAP (Code+field Len)      +
                                        // 2 Src TSAP len                   +
                                        // 2 Dst TSAP (Code+field Len)      +
                                        // 2 Src TSAP len                   +
                                        // 3 PDU size (Code+field Len+Val)  = 11
                                        // Telegram length
            var isoLen = 4 + // TPKT Header
                    7 +           // COTP Header Size without params
                    parLen;       // COTP params

            Tpkt.Version = StaticConst.IsoTcpVersion;
            Tpkt.Reserved = 0;
            Tpkt.Length = (ushort)isoLen;

            Cotp.Length = (byte)(parLen + 6);  // <-- 6 = 7 - 1 (COTP Header size - 1)
            Cotp.PduType = (byte)PduType.ConnRequest; // Connection Request
            Cotp.DstRef = DstRef;      // Destination reference
            Cotp.SrcRef = SrcRef;      // Source reference
            Cotp.CoR = 0x00;        // Class + Option : RFC0983 states that it must be always 0x40
                                    // but for some equipment (S7) must be 0 in disaccord of specifications !!!
        }

        public void Build(byte[] resBytes)
        {
            ByteBuffer buffer = ByteBuffer.Allocate();
            buffer.Push(resBytes);
            Tpkt.InitByBuffer(buffer);
            Cotp.InitByBuffer(buffer);
        }

        public ByteBuffer GetBuffer()
        {
            ByteBuffer buffer = ByteBuffer.Allocate();
            Tpkt.GetByteBuffer(buffer);
            Cotp.GetByteBuffer(buffer);
            return buffer;
        }

        public void Build(ByteBuffer buffer)
        {
            Tpkt.InitByBuffer(buffer);
            Cotp.InitByBuffer(buffer);
        }
    }
}
