private XElement ParseTypedefTree(ITree tree, Block scope)
        {
            if (foundDomains.ContainsKey(tree))
            {
                return foundDomains[tree];
            }

            if (tree.ChildCount == 0) { return null; }
            ITree tt = tree.GetChild(0);

            XElement domain = null;

            if (tt.Type == BlaiseParser.TYPE_ARRAY)
            {
                ITree realDomain = tt.GetChild(tt.ChildCount - 2); // last child
                return ParseTypedefTree(tt, scope); // array of this type
            }
            else if (tt.Type == BlaiseParser.TYPE_CATEGORY)
            {
                XElement catScheme = Ddi.Element(Ddi.CategoryScheme);
                XElement codeScheme = Ddi.Element(Ddi.CodeScheme);

                this.categorySchemes.Add(catScheme);
                this.codeSchemes.Add(codeScheme);

                domain = Ddi.Element(Ddi.CodeDomain, false);
                domain.Add(Ddi.GetReference(Ddi.CodeSchemeReference, codeScheme));

                // for each child category
                List<string> categoryNames = new List<string>(); // for making up a scheme name
                int codeNumber = 1;

                for (int i = 0; i < tt.ChildCount; ++i)
                {
                    int langCount = 0;
                    ITree cat = tt.GetChild(i);

                    XElement category = Ddi.Element(Ddi.Category);
                    XElement code = Ddi.Element(Ddi.LogicalCode, false);

                    code.Add(Ddi.GetReference(Ddi.CategoryReference, category));

                    catScheme.Add(category);
                    codeScheme.Add(code);

                    string catName= string.Empty;

                    // initialize the category
                    if (cat.ChildCount > 0)
                    {
                        catName = cat.GetChild(0).Text;
                        category.Add(new XElement(Ddi.CategoryName, Ddi.XmlLang(MainLanguage), catName));
                        category.Add(new XElement(Ddi.Label, Ddi.XmlLang(MainLanguage), catName));
                    }

                    for (int c = 1; c < cat.ChildCount; ++c)
                    {
                        ITree test = cat.GetChild(c);

                        // set the current code if present
                        if (test.Type == BlaiseParser.TYPE_CATEGORY_CODE)
                        {
                            if (test.ChildCount == 1)
                            {
                                try
                                {
                                    codeNumber = Int32.Parse(test.GetChild(0).Text);
                                }
                                catch (Exception)
                                {
                                    //TODO
                                }
                            }
                        }

                        //labels for each language
                        else if (test.Type == BlaiseParser.LID_STRING)
                        {
                            LanguageString ls = new LanguageString(test);
                            if (string.IsNullOrEmpty(ls.Language) && langCount < options.DefinedLanguagesOrder.Count)
                            {
                                ls.Language = options.DefinedLanguagesOrder[langCount];
                                langCount++;
                            }
                            BlaiseLanguageMapping mapping = options.GetMapping(ls.Language);
                            if (mapping != null)
                            {
                                category.Add(new XElement(Ddi.Label, Ddi.XmlLang(mapping.Culture), ls.Value));
                            }
                            else
                            {
                                category.Add(new XElement(Ddi.Label, Ddi.XmlLang(MainLanguage), ls.Value));
                            }
                        }

                    }

                    code.Add(new XElement(Ddi.Value, codeNumber.ToString()));
                    codeNumber++;

                    /*
                    if (cat.ChildCount == 1)
                    {
                        code.Value = cat.GetChild(0).Text;
                        category.ItemName.Current = cat.GetChild(0).Text;
                        category.Label.Current = cat.GetChild(0).Text;
                    }
                    else if (cat.ChildCount >= 2)
                    {
                        // TODO for now ignore the (code) coding
                        code.Value = cat.GetChild(0).Text;
                        for (int j = 1; j < cat.ChildCount; ++j)
                        {
                            if (cat.GetChild(j).Text.StartsWith("\""))
                            {
                                category.Label.Current = cat.GetChild(j).Text.Trim(new char[] { '"' });
                                break;
                            }
                        }
                    }
                    */
                    if (categoryNames.Count < 3)
                    {
                        categoryNames.Add(catName);
                    }
                }

                string madeUpName = string.Join(string.Empty, categoryNames.ToArray());
                catScheme.AddFirst(new XElement(Ddi.CategorySchemeName, madeUpName + "Categories"));
                codeScheme.AddFirst(new XElement(Ddi.CodeSchemeName, madeUpName + " Codes"));
            }
            else if (tt.Type == BlaiseParser.TYPE_INTEGER)
            {
                domain = Ddi.Element(Ddi.NumericDomain, false);
                domain.Add(new XAttribute("type", "Integer"));
            }
            else if (tt.Type == BlaiseParser.TYPE_OPEN)
            {
                domain = Ddi.Element(Ddi.TextDomain, false);
            }
            else if (tt.Type == BlaiseParser.TYPE_REAL)
            {
                domain = Ddi.Element(Ddi.NumericDomain, false);
                domain.Add(new XAttribute("type", "Float"));
            }
            else if (tt.Type == BlaiseParser.TYPE_SETOF)
            {
                string temp = tree.ToStringTree();
            }
            else if (tt.Type == BlaiseParser.TYPE_STRING)
            {
                domain = Ddi.Element(Ddi.TextDomain, false);
            }
            else if (tt.Type == BlaiseParser.TYPE_TIMETYPE)
            {
                domain = Ddi.Element(Ddi.DateTimeDomain, false);
            }
            else if (tt.Type == BlaiseParser.REALRANGE)
            {
                domain = Ddi.Element(Ddi.NumericDomain, false);
                domain.Add(new XAttribute("type", "Real"));

                var child0 = tt.GetChild(0);
                string low = null;
                if (child0 != null)
                {
                    if (child0.GetChild(0) != null)
                    {
                        low = child0.GetChild(0).Text;
                    }

                    if (child0.GetChild(1) != null)
                    {
                        low += "." + child0.GetChild(1).Text;
                    }
                }

                var child1 = tt.GetChild(1);
                string high = null;
                if (child1 != null)
                {
                    if (child1.GetChild(0) != null)
                    {
                        high = child1.GetChild(0).Text;
                    }

                    if (child1.GetChild(1) != null)
                    {
                        high += "." + child1.GetChild(1).Text;
                    }
                }

                try
                {
                    bool hasRange = false;
                    XElement range = Ddi.Element(Ddi.NumberRange, false);

                    if (!string.IsNullOrEmpty(low))
                    {
                        range.Add(new XElement(Ddi.Low, low));
                        hasRange = true;
                    }

                    if (!string.IsNullOrEmpty(high))
                    {
                        range.Add(new XElement(Ddi.High, high));
                        hasRange = true;
                    }

                    if (hasRange)
                    {
                        domain.Add(range);
                    }

                    if (child0 != null)
                    {
                        domain.Add(new XAttribute("decimalPositions", child0.GetChild(1).Text.Length));
                    }
                }
                catch (Exception)
                {
                    // TODO warn invalid blaise file
                }
            }
            else if (tt.Type == BlaiseParser.INTRANGE)
            {
                domain = Ddi.Element(Ddi.NumericDomain, false);
                domain.Add(new XAttribute("type", "Integer"));

                //TODO parse reals as reals
                string low = tt.GetChild(0).Text;
                string high = tt.GetChild(1).Text;
                try
                {
                    XElement range = Ddi.Element(Ddi.NumberRange, false);
                    range.Add(new XElement(Ddi.Low, low));
                    range.Add(new XElement(Ddi.High, high));
                    domain.Add(range);
                    domain.Add(new XAttribute("decimalPositions", 0));
                }
                catch (Exception)
                {
                    // TODO warn invalid blaise file
                }
            }
            else if (tt.Type == BlaiseParser.TYPE_USERDEF)
            {
                // find the type and ask again
                if (tt.ChildCount != 0)
                {
                    string usertype = tt.GetChild(0).Text;
                    if (scope.HasBlaiseType(usertype))
                    {
                        ITree usertree = scope.GetBlaiseType(usertype);
                        return ParseTypedefTree(usertree, scope);
                    }
                }
            }

            if (domain == null)
            {
                domain = Ddi.Element(Ddi.TextDomain, false);
            }

            foundDomains.Add(tree, domain);
            return domain;
        }
        private Block WalkSubModel(ITree datamodel, Block scope)
        {
            if (datamodel.ChildCount < 1)
            {
                return null; // TODO maybe warn
            }
            Block block = new Block();
            block.Label = datamodel.GetChild(0);
            blocks[block.Title] = block;

            if (scope != null) // not the top datamodel
            {
                block.Parent = scope;
                scope.AddBlock(block.Title, block);
            }

            ITree submodel = datamodel; // TODO

            // extract all type information for this submodel
            for (int i = 0; i < submodel.ChildCount; ++i)
            {
                ITree subtype = submodel.GetChild(i);
                if (subtype.Type == BlaiseParser.SUB_TYPE)
                {
                    // add all types to this block
                    for (int j = 0; j < subtype.ChildCount; ++j)
                    {
                        ITree typeItem = subtype.GetChild(j);
                        if (typeItem.Type == BlaiseParser.TYPE_ITEM)
                        {
                            if (typeItem.ChildCount == 2 &&
                                typeItem.GetChild(1).Type == BlaiseParser.TYPEDEF)
                            {
                                string typeName = typeItem.GetChild(0).Text;
                                block.AddType(typeName, typeItem.GetChild(1));
                            }
                        }
                    }
                }
            }

            // extract all field information for this submodel
            for (int i = 0; i < submodel.ChildCount; ++i)
            {
                ITree fields = submodel.GetChild(i);
                if (fields.Type == BlaiseParser.FIELDS || fields.Type == BlaiseParser.AUXFIELDS
                   || fields.Type == BlaiseParser.SUB_LOCALS)
                {
                    // add all fields to this block
                    for (int j = 0; j < fields.ChildCount; ++j)
                    {
                        ITree field = fields.GetChild(j);
                        if (field.Type == BlaiseParser.FIELD) // not necessary unless parser changes
                        {
                            if (field.ChildCount < 2) { continue; }
                            ITree idList = field.GetChild(0);
                            List<Field> toadd = new List<Field>(idList.ChildCount);
                            for (int k = 0; k < idList.ChildCount; ++k)
                            {
                                Field f = new Field();
                                f.Title = idList.GetChild(k).Text;
                                toadd.Add(f);
                            }

                            ITree typedef = null;

                            Collection<LanguageString> fieldTexts = new Collection<LanguageString>();
                            Collection<LanguageString> fieldDescriptions = new Collection<LanguageString>();
                            string tag = null;

                            // Do these seperate so we can keep the language ordering
                            int langCount = 0;
                            for (int k = 0; k < field.ChildCount; ++k)
                            {
                                ITree fi = field.GetChild(k);
                                if (fi.Type == BlaiseParser.FIELD_TEXT)
                                {
                                    ITree lidString = fi.GetChild(0);
                                    LanguageString ls = new LanguageString(lidString);
                                    if (string.IsNullOrEmpty(ls.Language) && langCount < options.DefinedLanguagesOrder.Count)
                                    {
                                        ls.Language = options.DefinedLanguagesOrder[langCount];
                                        langCount++;
                                    }
                                    fieldTexts.Add(ls);
                                }
                            }

                            langCount = 0;
                            for (int k = 0; k < field.ChildCount; ++k)
                            {
                                ITree fi = field.GetChild(k);
                                if (fi.Type == BlaiseParser.FIELD_DESC)
                                {
                                    ITree lidString = fi.GetChild(0);
                                    LanguageString ls = new LanguageString(lidString);
                                    if (string.IsNullOrEmpty(ls.Language) && langCount < options.DefinedLanguagesOrder.Count)
                                    {
                                        ls.Language = options.DefinedLanguagesOrder[langCount];
                                        langCount++;
                                    }
                                    fieldDescriptions.Add(ls);
                                }

                                // tack this on here, there is only one of them
                                if (fi.Type == BlaiseParser.TYPEDEF)
                                {
                                    typedef = fi;
                                }
                                else if (fi.Type == BlaiseParser.FIELD_TAG)
                                {
                                    if (fi.ChildCount == 1)
                                    {
                                        ITree tagList = fi.GetChild(0);
                                        for (int ti = 0; ti < tagList.ChildCount; ti++)
                                        {
                                            if (ti == 0) { tag = tagList.GetChild(ti).Text; }
                                            else { tag += "," + tagList.GetChild(ti).Text; }
                                        }

                                    }
                                }
                            }

                            foreach (Field f in toadd)
                            {
                                f.Question = fieldTexts;
                                f.Description = fieldDescriptions;
                                f.Typedef = typedef;
                                f.Parent = block;
                                f.Tag = tag;
                                block.AddField(f.Title, f);
                            }
                        }
                    }
                }
            }

            // extract all child block information for this submodel
            for (int i = 0; i < submodel.ChildCount; ++i)
            {
                ITree child = submodel.GetChild(i);
                if (child.Type == BlaiseParser.BLOCK || child.Type == BlaiseParser.TABLE || child.Type == BlaiseParser.PROCEDURE)
                {
                    WalkSubModel(child, block);
                }
            }

            // extract rules for this submodel
            for (int i = 0; i < submodel.ChildCount; ++i)
            {
                ITree child = submodel.GetChild(i);
                if (child.Type == BlaiseParser.RULES)
                {
                    block.Rules = child;
                }
            }

            return block;
        }
        private void AssignQuestionStrings(XElement question, LanguageString blaiseString, Block scope)
        {
            BlaiseLanguageMapping mapping = options.GetMapping(blaiseString.Language);
            string encodedText = GetQuestionText(blaiseString.Value, scope);

            if (mapping != null)
            {
                if (mapping.MetadataElement == MetadataFieldType.Default)
                {
                    question.Add(new XElement(Ddi.QuestionText, Ddi.XmlLang(mapping.Culture), new XElement(Ddi.LiteralText, new XElement(Ddi.Text, encodedText))));
                }
                else if (mapping.MetadataElement == MetadataFieldType.Description)
                {
                    question.Add(new XElement(Ddi.Description, Ddi.XmlLang(mapping.Culture), encodedText));
                }
                else if (mapping.MetadataElement == MetadataFieldType.QuestionText)
                {
                    question.Add(new XElement(Ddi.QuestionText, Ddi.XmlLang(mapping.Culture), new XElement(Ddi.LiteralText, new XElement(Ddi.Text, encodedText))));
                }
                else if (mapping.MetadataElement == MetadataFieldType.Intent)
                {
                    question.Add(new XElement(Ddi.QuestionIntent, Ddi.XmlLang(mapping.Culture), encodedText));
                }
                else if (mapping.MetadataElement == MetadataFieldType.Label)
                {
                    question.Add(new XElement(Ddi.Label, Ddi.XmlLang(mapping.Culture), encodedText));
                }
                else if (mapping.MetadataElement == MetadataFieldType.Instructions)
                {
                    // TODO
                }
            }
            else
            {
                question.Add(new XElement(Ddi.QuestionText, Ddi.XmlLang(MainLanguage), new XElement(Ddi.LiteralText, new XElement(Ddi.Text, encodedText))));
            }
        }