using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using HengShan_Pump_NonIC;
using HengShan_Pump_NonIC.MessageEntity;
using Edge.Core.Parser.BinaryParser.Util;
using System.Collections.Generic;

namespace Test_HengShan_Pump_NonIC
{
    [TestClass]
    public class StateMachineMessageCutterUnitTest
    {
        public static bool ValueEquals(IEnumerable<byte> array1, IEnumerable<byte> array2)
        {
            if (array1 == null && array2 == null)
            {
                return true;
            }

            if ((array1 == null) || (array2 == null))
            {
                return false;
            }

            if (array1.Count() != array2.Count())
            {
                return false;
            }
            if (array1.Equals(array2))
            {
                return true;
            }
            else
            {
                for (int Index = 0; Index < array1.Count(); Index++)
                {
                    if (!Equals(array1.ElementAt(Index), array2.ElementAt(Index)))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        [TestMethod]
        public void ShouldCutTest1()
        {
            var cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            byte[] msg = null;
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                msg = cutter.Message;
            };

            string rawRealCompleteMsg = "FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0";
            cutter.Feed(rawRealCompleteMsg.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 1);
            Assert.AreEqual(true, ValueEquals(msg, rawRealCompleteMsg.ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest2()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            byte[] msg = null;
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                msg = cutter.Message;
            };

            string rawRealCompleteMsg = "FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0";
            string rawRealMsgWithExtraHeaderAndTail = "aa" + rawRealCompleteMsg + "bb";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 1);
            Assert.AreEqual(true, ValueEquals(msg, rawRealCompleteMsg.ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest3()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            byte[] msg = null;
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                msg = cutter.Message;
            };

            string rawRealCompleteMsg = "FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0";
            string rawRealMsgWithExtraHeaderAndTail = "aa" + rawRealCompleteMsg + "bb";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 1);
            Assert.AreEqual(true, ValueEquals(msg, rawRealCompleteMsg.ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest4()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            List<byte[]> messages = new List<byte[]>();
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                messages.Add(cutter.Message);
            };

            List<string> rawRealCompleteMessages = new List<string>();
            rawRealCompleteMessages.Add("FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0");
            rawRealCompleteMessages.Add("FF 03 A0 00 00");
            string rawRealMsgWithExtraHeaderAndTail = "aabc"
                + rawRealCompleteMessages.Aggregate((acc, n) => acc + n)
                + "1122bb";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == rawRealCompleteMessages.Count);
            for (int i = 0; i < rawRealCompleteMessages.Count; i++)
                Assert.AreEqual(true, ValueEquals(messages[i], rawRealCompleteMessages[i].ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest5()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            List<byte[]> messages = new List<byte[]>();
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                messages.Add(cutter.Message);
            };
            List<string> rawRealCompleteMessages = new List<string>();
            rawRealCompleteMessages.Add("FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0");
            rawRealCompleteMessages.Add("FF 03 A0 00 00");
            rawRealCompleteMessages.Add("FF 05 A0 00 00 00 00");
            rawRealCompleteMessages.Add("FF 10 A0 00 00 10 A0 00 00 10 A0 00 00 10 A0 00 00 10");
            string rawRealMsgWithExtraHeaderAndTail = "aabc"
                + rawRealCompleteMessages.Aggregate((acc, n) => acc + n)
                + "1122bb";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == rawRealCompleteMessages.Count);
            for (int i = 0; i < rawRealCompleteMessages.Count; i++)
                Assert.AreEqual(true, ValueEquals(messages[i], rawRealCompleteMessages[i].ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest_Negative0()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            byte[] msg = null;
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                msg = cutter.Message;
            };

            string rawRealCompleteMsg = "FF 12 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A0";
            // dumplicate `FF` will make cutter consider the 2nd `FF` as the MsgLen.
            string rawRealMsgWithExtraHeaderAndTail = "FF" + rawRealCompleteMsg + "bbFF";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 0);
            Assert.AreEqual(true, msg == null);
        }

        [TestMethod]
        public void ShouldCutTest_Negative1()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            byte[] msg = null;
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                msg = cutter.Message;
            };

            string rawRealCompleteMsg = "FF 03 A0 00 00";
            string rawRealMsgWithExtraHeaderAndTail = "FF03aa" + rawRealCompleteMsg;
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 1);
            Assert.AreEqual(true, ValueEquals(msg, "FF03aa FF 03".ToBytes()));
        }

        [TestMethod]
        public void ShouldCutTest_Negative2()
        {
            StateMachineMessageCutter cutter = new StateMachineMessageCutter();
            int onMsgCutCalledTimes = 0;
            List<byte[]> messages = new List<byte[]>();
            cutter.OnMessageCut += (s, a) =>
            {
                onMsgCutCalledTimes++;
                messages.Add(cutter.Message);
            };

            /* the 2nd `FF` will be caught and included in a msg body, the next cut will only start by seeing another `FF`*/
            string rawRealCompleteMsg = "FF 03 A0 00 00";
            string rawRealMsgWithExtraHeaderAndTail = "FF02aa" + rawRealCompleteMsg + "FF0100";
            cutter.Feed(rawRealMsgWithExtraHeaderAndTail.ToBytes());
            Assert.AreEqual(true, onMsgCutCalledTimes == 2);
            Assert.AreEqual(true, ValueEquals(messages[0], "FF02aa FF".ToBytes()));
            Assert.AreEqual(true, ValueEquals(messages[1], "FF0100".ToBytes()));
        }
    }
}