/// <summary>
 /// Serialize fields to a stream.
 /// </summary>
 /// <param name="stream">The stream where serialized instance will be wrote.</param>
 /// <returns>Bytes written to the stream.</returns>
 public override int Serialize(Stream stream)
 {
     int bytesWriren = 0;
     this.stream = stream;
     this.deserializedcommandList = null;
     this.stack = new CommonByteStack();
     this.globcntRangeList = GetGLOBCNTRange(this.GLOBCNTList);
     this.isAllGLOBCNTInGLOBSET = true;
     this.isAllGLOBCNTRanged = true;
     this.isDisjointGLOBCNTMadeIntoSingleton = true;
     this.hasAllDuplicateGLOBCNTRemoved = true;
     this.hasGLOBCNTGroupedIntoRanges = true;
     bytesWriren += this.Compress(0, this.globcntRangeList.Count - 1);
     bytesWriren += this.End(stream);
     return bytesWriren;
 }
        /// <summary>
        /// Deserialize fields in this class from a stream.
        /// </summary>
        /// <param name="stream">Stream contains a serialized instance of this class.</param>
        /// <param name="size">How many bytes can read if -1, no limitation.MUST be -1.</param>
        /// <returns>Bytes have been read from the stream.</returns>
        public override int Deserialize(Stream stream, int size)
        {
            AdapterHelper.Site.Assert.AreEqual(-1, size, "The size value MUST be -1, but the actual value is {0}.", size);

            int bytesRead = 0;
            this.stream = stream;
            this.globcntList = new List<GLOBCNT>();
            this.globcntRangeList = new List<GLOBCNTRange>();
            this.stack = new CommonByteStack();
            this.deserializedcommandList = new List<Command>();
            Operation op = this.ReadOperation();
            bytesRead += 1;
            while (op != Operation.End)
            {
                switch (op)
                {
                    case Operation.Bitmask:
                        if (this.stack.Bytes != 5)
                        {
                            AdapterHelper.Site.Assert.Fail("The deserialization operation should be successful.");
                        }
                        else
                        {
                            byte[] commonBytes = stack.GetCommonBytes();
                            byte startValue, bitmask;
                            bytesRead += ReadBitmaskValue(out startValue, out bitmask);
                            List<GLOBCNTRange> tmp = FromBitmask(commonBytes, startValue, bitmask);
                            BitmaskCommand bmCmd =
                                new BitmaskCommand((byte)op, startValue, bitmask)
                                {
                                    CorrespondingGLOBCNTRangeList = tmp
                                };
                            deserializedcommandList.Add(bmCmd);
                            for (int i = 0; i < tmp.Count; i++)
                            {
                                globcntRangeList.Add(tmp[i]);
                            }

                            tmp = null;
                        }

                        break;
                    case Operation.End:
                        this.deserializedcommandList.Add(new EndCommand((byte)op));
                        return bytesRead;
                    case Operation.Pop:
                        this.deserializedcommandList.Add(new PopCommand((byte)op));
                        this.stack.Pop();
                        break;
                    case Operation.Range:
                        {
                            byte[] lowValue, highValue;
                            bytesRead += this.ReadRangeValue(out lowValue, out highValue);
                            GLOBCNTRange range = this.FromRange(
                                this.stack.GetCommonBytes(),
                                lowValue,
                                highValue);
                            List<GLOBCNTRange> rangeList = new List<GLOBCNTRange>
                            {
                                range
                            };
                            RangeCommand rngCmd =
                                new RangeCommand((byte)op, lowValue, highValue)
                                {
                                    CorrespondingGLOBCNTRangeList = rangeList
                                };

                            this.deserializedcommandList.Add(rngCmd);
                            this.globcntRangeList.Add(range);
                        }

                        break;
                    case Operation.Push1:
                    case Operation.Push2:
                    case Operation.Push3:
                    case Operation.Push4:
                    case Operation.Push5:
                    case Operation.Push6:
                        int pushByteCount = (int)op;
                        byte[] pushBytes;
                        bytesRead += this.ReadPushedValue(pushByteCount, out pushBytes);
                        PushCommand pshCmd = new PushCommand((byte)op, pushBytes);
                        if (6 == pushByteCount + this.stack.Bytes)
                        {
                            List<GLOBCNTRange> rangeList = new List<GLOBCNTRange>();
                            GLOBCNTRange range = this.FromPush(this.stack.GetCommonBytes(), pushBytes);
                            rangeList.Add(range);
                            this.globcntRangeList.Add(range);
                            pshCmd.CorrespondingGLOBCNTRangeList = rangeList;
                            this.deserializedcommandList.Add(pshCmd);
                            break;
                        }

                        this.deserializedcommandList.Add(pshCmd);
                        this.stack.Push(pushBytes);
                        break;
                    default:
                        AdapterHelper.Site.Assert.Fail("The operation get from the stream is invalid, its value is {0}.", op);
                        break;
                }

                op = this.ReadOperation();
                bytesRead += 1;
            }

            if (op == Operation.End)
            {
                this.deserializedcommandList.Add(new EndCommand((byte)op));
            }

            this.isAllGLOBCNTInGLOBSET = true;
            this.isAllGLOBCNTRanged = true;
            this.isDisjointGLOBCNTMadeIntoSingleton = true;
            this.hasAllDuplicateGLOBCNTRemoved = true;
            this.hasGLOBCNTGroupedIntoRanges = true;
            return bytesRead;
        }
        /// <summary>
        /// Get the data from stack.
        /// </summary>
        /// <param name="maxIndex">The max index in the command list.</param>
        /// <param name="commandList">The data source of command list.</param>
        /// <param name="comByteStack">The initial result returned.</param>
        /// <returns>The final data get from the data source based on the initial data.</returns>
        private CommonByteStack GetCommonByteStack(int maxIndex, List<Command> commandList, CommonByteStack comByteStack)
        {
            CommonByteStack commonByteStack = comByteStack;

            // Check the commands before current one in DeserializedCommandList,
            for (int j = 0; j < maxIndex; j++)
            {
                if (commandList[j] is PushCommand)
                {
                    // if  encounter PushCommand, push the element into comByteStack
                    commonByteStack.Push((commandList[j] as PushCommand).CommonBytes);

                    // When a Push command places a sixth byte onto the common byte stack, 
                    // it tells the decoder the next GLOBCNT pair has all six bytes in common
                    if (commonByteStack.Bytes == 6)
                    {
                        commonByteStack.Pop();
                    }
                }
                else if (commandList[j] is PopCommand)
                {
                    // If encounter PopCommand, pop the elements out of comByteStac
                    commonByteStack.Pop();
                }
            }

            return commonByteStack;
        }