/// <summary>
        /// Deserialize the class data from a stream.
        /// </summary>
        /// <param name="reader">The <see cref="IPrimitiveReader"/> that extracts used to extra data from a stream.</param>
        /// <param name="version">The value of <see cref="CurrentVersion"/> that was written to the stream when it was originally serialized to a stream;
        /// the version of the <paramref name="reader"/> data.</param>
        public void Deserialize(IPrimitiveReader reader, int version)
        {
            ushort len;

            //Metadata or MetadataPropertyCollection
            if (InDeserializationContext.IsMetadataPropertyCollection)
            {
                //MetadataPropertyCollection
                len = reader.ReadUInt16();
                if (len > 0)
                {
                    MetadataPropertyCollection = new MetadataPropertyCollection();
                    string propertyName;
                    byte[] propertyValue;
                    ushort propertyValueLen;

                    for (ushort i = 0; i < len; i++)
                    {
                        propertyName     = reader.ReadString();
                        propertyValueLen = reader.ReadUInt16();
                        propertyValue    = null;
                        if (propertyValueLen > 0)
                        {
                            propertyValue = reader.ReadBytes(propertyValueLen);
                        }
                        MetadataPropertyCollection.Add(propertyName, propertyValue);
                    }
                }
            }
            else
            {
                //Metadata
                len = reader.ReadUInt16();
                if (len > 0)
                {
                    Metadata = reader.ReadBytes(len);
                }
            }

            //VirtualCount
            if (version >= 2)
            {
                virtualCount = reader.ReadInt32();
            }

            //Count
            outDeserializationContext = new OutDeserializationContext {
                TotalCount = reader.ReadInt32()
            };

            if (InDeserializationContext.DeserializeHeaderOnly)
            {
                //Note: If InDeserializationContext.DeserializeHeaderOnly property is set then InDeserializationContext.PartialByteArray shall hold all CacheIndexInternal
                //payload except metadata and header (just virtual count for now). This code path will only be used if just
                //header info like virtual count needs to be updated keeping rest of the index untouched.
                //InDeserializationContext.PartialByteArray shall be used in Serialize code
                outDeserializationContext.UnserializedCacheIndexInternal =
                    new byte[(int)reader.BaseStream.Length - (int)reader.BaseStream.Position];
                reader.BaseStream.Read(outDeserializationContext.UnserializedCacheIndexInternal, 0, outDeserializationContext.UnserializedCacheIndexInternal.Length);
            }
            else
            {
                int actualItemCount = outDeserializationContext.TotalCount;

                //this.InDeserializationContext.MaxItemsPerIndex = 0 indicates need to extract all items
                //this.InDeserializationContext.MaxItemsPerIndex > 0 indicates need to extract only number of items indicated by InDeserializationContext.MaxItemsPerIndex
                if (InDeserializationContext.MaxItemsPerIndex > 0)
                {
                    if (InDeserializationContext.MaxItemsPerIndex < outDeserializationContext.TotalCount)
                    {
                        actualItemCount = InDeserializationContext.MaxItemsPerIndex;
                    }
                }

                #region Populate InternalItemList

                InternalItem internalItem;
                bool         enterConditionPassed = false;

                InternalItemList = new InternalItemList();
                GroupByResult    = new GroupByResult(new BaseComparer(InDeserializationContext.PrimarySortInfo.IsTag, InDeserializationContext.PrimarySortInfo.FieldName, InDeserializationContext.PrimarySortInfo.SortOrderList));

                // Note: ---- Termination condition of the loop
                // For full index extraction loop shall terminate because of condition : internalItemList.Count + GroupByResult.Count < actualItemCount
                // For partial index extraction loop shall terminate because of following conditions
                //				a)  i < InDeserializationContext.TotalCount (when no sufficient items are found) OR
                //				b)  internalItemList.Count < actualItemCount (Item extraction cap is reached)
                int i = 0;
                while (GroupByResult.Count + InternalItemList.Count < actualItemCount && i < outDeserializationContext.TotalCount)
                {
                    i++;

                    #region Deserialize ItemId

                    len = reader.ReadUInt16();
                    if (len > 0)
                    {
                        internalItem = new InternalItem
                        {
                            ItemId = reader.ReadBytes(len)
                        };
                    }
                    else
                    {
                        throw new Exception("Invalid ItemId - is null or length is zero for IndexId : " +
                                            IndexCacheUtils.GetReadableByteArray(InDeserializationContext.IndexId));
                    }

                    #endregion

                    #region Process IndexCondition
                    if (InDeserializationContext.EnterCondition != null || InDeserializationContext.ExitCondition != null)
                    {
                        #region Have Enter/Exit Condition

                        if (InDeserializationContext.PrimarySortInfo.IsTag == false)
                        {
                            #region Sort by ItemId

                            if (InDeserializationContext.EnterCondition != null && enterConditionPassed == false)
                            {
                                #region enter condition processing

                                if (FilterPassed(internalItem, InDeserializationContext.EnterCondition))
                                {
                                    if (InDeserializationContext.ExitCondition != null && !FilterPassed(internalItem, InDeserializationContext.ExitCondition))
                                    {
                                        // no need to search beyond this point
                                        break;
                                    }

                                    enterConditionPassed = true;
                                    DeserializeTags(internalItem, InDeserializationContext, OutDeserializationContext, reader);
                                    ApplyFilterAndAddItem(internalItem);
                                }
                                else
                                {
                                    SkipDeserializeInternalItem(reader);
                                    // no filter processing required
                                }

                                #endregion
                            }
                            else if (InDeserializationContext.ExitCondition != null)
                            {
                                #region exit condition processing

                                if (FilterPassed(internalItem, InDeserializationContext.ExitCondition))
                                {
                                    // since item passed exit filter, we keep it.
                                    DeserializeTags(internalItem, InDeserializationContext, OutDeserializationContext, reader);
                                    ApplyFilterAndAddItem(internalItem);
                                }
                                else
                                {
                                    // no need to search beyond this point
                                    break;
                                }

                                #endregion
                            }
                            else if (InDeserializationContext.EnterCondition != null && enterConditionPassed && InDeserializationContext.ExitCondition == null)
                            {
                                #region enter condition processing when no exit condition exists

                                DeserializeTags(internalItem, InDeserializationContext, OutDeserializationContext, reader);
                                ApplyFilterAndAddItem(internalItem);

                                #endregion
                            }

                            #endregion
                        }
                        else
                        {
                            #region Sort by Tag

                            #region Deserialize InternalItem and fetch PrimarySortTag value

                            byte[] tagValue;
                            DeserializeTags(internalItem, InDeserializationContext, OutDeserializationContext, reader);
                            if (!internalItem.TryGetTagValue(InDeserializationContext.PrimarySortInfo.FieldName, out tagValue))
                            {
                                throw new Exception("PrimarySortTag Not found:  " + InDeserializationContext.PrimarySortInfo.FieldName);
                            }

                            #endregion

                            if (InDeserializationContext.EnterCondition != null && enterConditionPassed == false)
                            {
                                #region enter condition processing

                                if (FilterPassed(internalItem, InDeserializationContext.EnterCondition))
                                {
                                    if (InDeserializationContext.ExitCondition != null && !FilterPassed(internalItem, InDeserializationContext.ExitCondition))
                                    {
                                        // no need to search beyond this point
                                        break;
                                    }

                                    enterConditionPassed = true;
                                    ApplyFilterAndAddItem(internalItem);
                                }

                                #endregion
                            }
                            else if (InDeserializationContext.ExitCondition != null)
                            {
                                #region exit condition processing

                                if (FilterPassed(internalItem, InDeserializationContext.ExitCondition))
                                {
                                    // since item passed exit filter, we keep it.
                                    ApplyFilterAndAddItem(internalItem);
                                }
                                else
                                {
                                    // no need to search beyond this point
                                    break;
                                }

                                #endregion
                            }
                            else if (InDeserializationContext.EnterCondition != null && enterConditionPassed && InDeserializationContext.ExitCondition == null)
                            {
                                #region enter condition processing when no exit condition exists

                                ApplyFilterAndAddItem(internalItem);

                                #endregion
                            }

                            #endregion
                        }

                        #endregion
                    }
                    else
                    {
                        #region No Enter/Exit Condition

                        DeserializeTags(internalItem, InDeserializationContext, OutDeserializationContext, reader);
                        ApplyFilterAndAddItem(internalItem);

                        #endregion
                    }

                    #endregion
                }

                //Set ReadItemCount on OutDeserializationContext
                outDeserializationContext.ReadItemCount = i;

                #endregion
            }
        }
 /// <summary>
 /// Converts an InternalItem to an IndexItem.
 /// </summary>
 /// <param name="internalItem">The internal item.</param>
 /// <param name="inDeserializationContext">The in deserialization context.</param>
 /// <returns></returns>
 internal static IndexItem ConvertToIndexItem(InternalItem internalItem, InDeserializationContext inDeserializationContext)
 {
     return(new IndexItem(internalItem.ItemId, ConvertToTagDictionary(internalItem.TagList, inDeserializationContext)));
 }