/// <summary>
        /// Create domain index file.
        /// </summary>
        /// <param name="scriptFile">Script file.</param>
        /// <param name="domainList">Domain list.</param>
        /// <param name="uif">Name indexed unit features.</param>
        public void Create(XmlScriptFile scriptFile, DomainConfigList domainList, UnitIndexingFile uif)
        {
            // Parameters Validation
            if (scriptFile == null)
            {
                throw new ArgumentNullException("scriptFile");
            }

            if (domainList == null)
            {
                throw new ArgumentNullException("domainList");
            }

            if (uif == null)
            {
                throw new ArgumentNullException("uif");
            }

            Dictionary<string, DomainIndexItem> items =
                new Dictionary<string, DomainIndexItem>(StringComparer.Ordinal);

            _language = scriptFile.Language;
            _tag = domainList.FontTag;
            Phoneme phoneme = Localor.GetPhoneme(_language);
            SliceData sliceData = Localor.GetSliceData(_language);
            foreach (ScriptItem scriptItem in scriptFile.Items)
            {
                if (!domainList.Contains(scriptItem.Id))
                {
                    continue;
                }

                Collection<TtsUnit> itemUnits = scriptItem.GetUnits(phoneme, sliceData);
                Collection<ScriptWord> allPronouncedNormalWords = scriptItem.AllPronouncedNormalWords;
                for (int i = 0; i < allPronouncedNormalWords.Count; i++)
                {
                    ScriptWord word = allPronouncedNormalWords[i];

                    string text;
                    if (domainList.Domain == ScriptDomain.Number)
                    {
                        text = GetNumberDomainWordText(word, scriptItem.Id, i,
                            (domainList as NumberDomainConfigList).Digitals);
                    }
                    else if (domainList.Domain == ScriptDomain.Acronym)
                    {
                        text = GetAcronymDomainWordText(word, scriptItem.Id, i,
                            (domainList as AcronymDomainConfigList).Acronyms);
                    }
                    else if (domainList.Domain == ScriptDomain.Letter)
                    {
                        // Use pronunciation phone ids as key
                        text = GetPhoneIds(word);
                    }
                    else
                    {
                        text = word.Grapheme.ToUpperInvariant();
                    }

                    if (items.ContainsKey(text) &&
                        domainList.Domain != ScriptDomain.Letter)
                    {
                        // Skip duplicate word, except Letter domain
                        continue;
                    }

                    DomainIndexItem item = null;
                    if (!items.ContainsKey(text))
                    {
                        item = new DomainIndexItem();
                        item.Word = text;
                    }
                    else
                    {
                        item = items[text];
                    }

                    bool skipped = false;
                    Collection<TtsUnit> wordUnits = word.GetUnits(phoneme, sliceData);
                    for (int wordUnitIndex = 0; wordUnitIndex < wordUnits.Count; wordUnitIndex++)
                    {
                        TtsUnit unit = wordUnits[wordUnitIndex];
                        FeatureDataItem featureItem = new FeatureDataItem();

                        int indexOfNonSilence = itemUnits.IndexOf(unit);
                        Debug.Assert(indexOfNonSilence >= 0 && indexOfNonSilence < itemUnits.Count);

                        int unitOffset = uif.SearchCandidateOffset(unit.MetaUnit.Name, scriptItem.Id, (uint)indexOfNonSilence);
                        if (unitOffset == -1)
                        {
                            // Skip this word
                            skipped = true;
                            break;
                        }

                        if (item.FeatureItems.Count == wordUnitIndex)
                        {
                            featureItem.UnitIndexes.Add(unitOffset);
                            item.FeatureItems.Add(featureItem); // [].UnitIndexes.Add(unitOffset);
                        }
                        else
                        {
                            item.FeatureItems[wordUnitIndex].UnitIndexes.Add(unitOffset);
                        }
                    }

                    if (!skipped && !items.ContainsKey(item.Word))
                    {
                        items.Add(item.Word, item);
                    }
                }
            }

            _items = BuildHashTable(items.Values);
        }
        /// <summary>
        /// Load feature data.
        /// </summary>
        /// <param name="data">Feature data bytes.</param>
        /// <param name="offset">Start offset in feature data chunck.</param>
        public void LoadFeatureData(byte[] data, int offset)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            uint count = BitConverter.ToUInt32(data, offset);
            checked
            {
                offset += sizeof(uint);
            }

            for (int i = 0; i < count; i++)
            {
                FeatureDataItem item = new FeatureDataItem();

                // Number of Unit Indexes
                ushort unitCount = BitConverter.ToUInt16(data, offset);
                checked
                {
                    offset += sizeof(ushort);
                }

                // UnitIndexes (uint)
                item.UnitIndexes.Clear();
                for (ushort unitIndex = 0; unitIndex < unitCount; unitIndex++)
                {
                    int index = BitConverter.ToInt32(data, offset);
                    item.UnitIndexes.Add(index);
                    checked
                    {
                        offset += sizeof(int);
                    }
                }

                _featureItems.Add(item);
            }
        }