using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Text.RegularExpressions;

namespace SpellListing {
    public partial class Form1 : Form {
        spells data;
        string namefilter = null;
        string classfilter = null;
        string schoolfilter = null;
        string descriptorfilter = null;
        string descriptionfilter = null;
        int levelfilter = -1;

        public Form1() {
            InitializeComponent();
            System.IO.FileStream stream;
            try {
                stream = System.IO.File.OpenRead("spells.xml");
            } catch(System.IO.FileNotFoundException) {
                createXml();
                stream = System.IO.File.OpenRead("spells.xml");
            }
            
            XmlSerializer serializer = new XmlSerializer(typeof(spells));
            data = (spells)serializer.Deserialize(stream);
            foreach(var spell in data.spell) {
                spell.short_text = spell.name.Trim();
                if(spell.short_description.Trim().Length != 0)
                    spell.short_text += ": " + spell.short_description.Trim();
                if(spell.source.Trim().Length != 0)
                    spell.short_text += " [From " + spell.source.Trim() + "]";
            }
            Array.Sort(data.spell, delegate(spellsSpell sp1, spellsSpell sp2) { return sp1.name.CompareTo(sp2.name); });
            populate();
        }

        private void populate() {
            // Spell list
            SpellName.DataSource = data.spell;
            SpellName.DisplayMember = "short_text";
            
            // Class list
            ClassFilter.Items.Add("any class");
            var classprops = data.spell[0].spell_level.GetType().GetProperties();
            List<string> strclassprops = new List<string>();
            foreach(var prop in classprops) {
                if(prop.Name != "humanreadable")
                    strclassprops.Add(prop.Name);
            }
            strclassprops.Sort();
            ClassFilter.Items.AddRange(strclassprops.ToArray());
            ClassFilter.SelectedIndex = 0;
            
            // Spell level list
            LevelFilter.Items.Add("any level");
            LevelFilter.Items.Add("0th level");
            LevelFilter.Items.Add("1st level");
            LevelFilter.Items.Add("2nd level");
            LevelFilter.Items.Add("3rd level");
            LevelFilter.Items.Add("4th level");
            LevelFilter.Items.Add("5th level");
            LevelFilter.Items.Add("6th level");
            LevelFilter.Items.Add("7th level");
            LevelFilter.Items.Add("8th level");
            LevelFilter.Items.Add("9th level");
            LevelFilter.SelectedIndex = 0;

            // Descriptor list
            DescriptorFilter.Items.Add("any descriptor");
            var descriptorprops = data.spell[0].descriptor.GetType().GetProperties();
            List<string> strdescprops = new List<string>();
            foreach(var prop in descriptorprops) {
                if(prop.Name != "humanreadable")
                    strdescprops.Add(prop.Name.Replace('_', '-'));
            }
            strdescprops.Sort();
            DescriptorFilter.Items.AddRange(strdescprops.ToArray());
            DescriptorFilter.SelectedIndex = 0;

            // Source list
            SourceFilter.Items.Add("all sources");
            List<string> sources = new List<string>();
            foreach(var spell in data.spell) {
                if(!sources.Contains(spell.source.Trim().ToLowerInvariant())) {
                    sources.Add(spell.source.Trim().ToLowerInvariant());
                }
            }
            sources.Sort();
            SourceFilter.Items.AddRange(sources.ToArray());
            SourceFilter.SelectedIndex = 0;

            // School list
            SchoolFilter.Items.Add("all schools");
            List<string> schools = new List<string>();
            foreach(var spell in data.spell) {
                if(!schools.Contains(spell.school.Trim().ToLowerInvariant()) && spell.school.Trim().ToLowerInvariant() != "see text") {
                    schools.Add(spell.school.Trim().ToLowerInvariant());
                }
            }
            schools.Sort();
            SchoolFilter.Items.AddRange(schools.ToArray());
            SchoolFilter.SelectedIndex = 0;

            // Domain list
            DomainFilter.Items.Add("no (sub)domain filter");
            List<string> domains = new List<string>();
            foreach(var spell in data.spell) {
                foreach(var domain in spell.domain.Split(',')) {
                    if(!domains.Contains(domain.Trim().ToLowerInvariant()) && domain.Trim().Length > 0)
                        domains.Add(domain.Trim().ToLowerInvariant());
                }
            }
            domains.Sort();
            DomainFilter.Items.AddRange(domains.ToArray());
            DomainFilter.SelectedIndex = 0;

            // Bloodline list
            BloodlineFilter.Items.Add("no bloodline filter");
            List<string> bloodlines = new List<string>();
            foreach(var spell in data.spell) {
                foreach(var bloodline in spell.bloodline.Split(',')) {
                    if(!bloodlines.Contains(bloodline.Trim().ToLowerInvariant()) && bloodline.Trim().Length > 0)
                        bloodlines.Add(bloodline.Trim().ToLowerInvariant());
                }
            }
            bloodlines.Sort();
            BloodlineFilter.Items.AddRange(bloodlines.ToArray());
            BloodlineFilter.SelectedIndex = 0;
        }

        private void repopulate() {
            var prevfilt = data.spell.ToList();

            // Filter on name.
            if(namefilter != null) {
                var filtered = from spell in prevfilt
                               where namefilter.Split(' ').All(s => spell.name.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0)
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter on description.
            if(descriptionfilter != null) {
                var filtered = from spell in prevfilt
                               where spell.description.full_text.IndexOf(descriptionfilter, StringComparison.OrdinalIgnoreCase) >= 0
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter on class/level
            if(classfilter != null) {
                if(levelfilter > -1) {
                    var filtered = from spell in prevfilt
                                   where ((string)spell.spell_level.GetType().GetProperty(classfilter).GetValue(spell.spell_level)) == levelfilter.ToString()
                                   select spell;
                    prevfilt = filtered.ToList();
                } else {
                    var filtered = from spell in prevfilt
                                   where ((string)spell.spell_level.GetType().GetProperty(classfilter).GetValue(spell.spell_level)) != "-1"
                                   select spell;
                    prevfilt = filtered.ToList();
                }
            } else {
                if(levelfilter > -1) {
                    var filtered = from spell in prevfilt
                                   where spell.spell_level.humanreadable.Contains(levelfilter.ToString())
                                   select spell;
                    prevfilt = filtered.ToList();
                }
            }

            // Filter on components
            if(VFilter.CheckState != CheckState.Indeterminate) {
                string state = VFilter.CheckState == CheckState.Checked ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.verbal == state
                               select spell;
                prevfilt = filtered.ToList();
            }

            if(SFilter.CheckState != CheckState.Indeterminate) {
                string state = SFilter.CheckState == CheckState.Checked ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.somatic == state
                               select spell;
                prevfilt = filtered.ToList();
            }

            if(MFilter.CheckState != CheckState.Indeterminate) {
                string state = MFilter.CheckState == CheckState.Checked ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.material == state
                               select spell;
                prevfilt = filtered.ToList();
            }

            if(FFilter.CheckState != CheckState.Indeterminate) {
                string state = FFilter.CheckState == CheckState.Checked ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.focus == state
                               select spell;
                prevfilt = filtered.ToList();
            }

            if(DFFilter.CheckState != CheckState.Indeterminate) {
                string state = DFFilter.CheckState == CheckState.Checked ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.divine_focus == state
                               select spell;
                prevfilt = filtered.ToList();
            }

            if((EMFilter.Enabled && EMFilter.CheckState != CheckState.Indeterminate) || (EFFilter.Enabled && EFFilter.CheckState != CheckState.Indeterminate)) {
                // If either are enabled and checked.
                bool filterMaterials = EMFilter.Enabled;
                bool allowMaterials = EMFilter.CheckState == CheckState.Checked;
                bool filterFoci = EFFilter.Enabled;
                bool allowFoci = EFFilter.CheckState == CheckState.Checked;
                bool removeMaterials = filterMaterials && !allowMaterials;
                bool removeFoci = filterFoci && !allowFoci;
                string state = (filterMaterials && allowMaterials) || (filterFoci && allowFoci) ? "1" : "0";
                var filtered = from spell in prevfilt
                               where spell.components.costly_components.Value == state
                               select spell;
                List<spellsSpell> subFiltered = filtered.ToList();
                List<int> indicesToRemove = new List<int>();
                if(removeFoci != removeMaterials) {
                    for(int i = 0; i < subFiltered.Count; ++i) {
                        var spell = subFiltered.ElementAt(i);
                        Regex either = new Regex(@"(M/F|M/DF|M/F/DF|M/DF/F|F/M|F/DF/M|DF/M|DF/F/M) \((.*?)\)");
                        if(either.IsMatch(spell.components.humanreadable)) {
                            continue;
                        }

                        Regex materials = new Regex(@"(M) \((.*?)\)");
                        Regex foci = new Regex(@"(DF|F|F/DF|DF/F) \((.*?)\)");
                        var materialsMatch = materials.Match(spell.components.humanreadable);
                        var fociMatch = foci.Match(spell.components.humanreadable);
                        // Case one: Only show foci.
                        // Case two: Only show materials.
                        // Case three: Don't care.
                        if(removeMaterials) {
                            if(materialsMatch.Success && (materialsMatch.Groups[2].Value.Contains(" gp") || materialsMatch.Groups[2].Value.Contains(" sp") || materialsMatch.Groups[2].Value.Contains(" cp"))) {
                                indicesToRemove.Add(i);
                            }
                        } else {
                            if(fociMatch.Success && (fociMatch.Groups[2].Value.Contains(" gp") || fociMatch.Groups[2].Value.Contains(" sp") || fociMatch.Groups[2].Value.Contains(" cp"))) {
                                indicesToRemove.Add(i);
                            }
                        }
                    }
                }
                indicesToRemove.Sort();
                indicesToRemove.Reverse();
                foreach(var i in indicesToRemove) {
                    subFiltered.RemoveAt(i);
                }
                prevfilt = subFiltered;
            }

            // Filter source
            if(SourceFilter.SelectedItems.Count != 0 && !SourceFilter.SelectedIndices.Contains(0)) {
                var filtered = from spell in prevfilt
                               where SourceFilter.SelectedItems.Contains(spell.source.Trim().ToLowerInvariant())
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter school
            if(schoolfilter != null) {
                var filtered = from spell in prevfilt
                               where spell.school.IndexOf(schoolfilter, StringComparison.OrdinalIgnoreCase) >= 0
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter descriptor
            if(descriptorfilter != null) {
                var filtered = from spell in prevfilt
                               where ((string)spell.descriptor.GetType().GetProperty(descriptorfilter).GetValue(spell.descriptor)) == "1"
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter cleric domain
            if(DomainFilter.SelectedIndex > 0) {
                var filtered = from spell in prevfilt
                               where spell.domain.IndexOf((string)DomainFilter.SelectedItem, StringComparison.OrdinalIgnoreCase) >= 0
                               select spell;
                prevfilt = filtered.ToList();
            }

            // Filter sorcerer bloodline
            if(BloodlineFilter.SelectedIndex > 0) {
                var filtered = from spell in prevfilt
                               where spell.bloodline.IndexOf((string)BloodlineFilter.SelectedItem, StringComparison.OrdinalIgnoreCase) >= 0
                               select spell;
                prevfilt = filtered.ToList();
            }

            SpellName.DataSource = prevfilt;
            SpellName.DisplayMember = "short_text";
        }

        private void NameFilter_TextChanged(object sender, EventArgs e) {
            Console.Out.WriteLine(NameFilter.Text);
            namefilter = NameFilter.Text;
            repopulate();
        }

        private void SpellName_SelectedValueChanged(object sender, EventArgs e) {
            if(SpellName.SelectedItems.Count == 0) {
                SpellInfo.DocumentText = "<h1>Select a spell...</h1>";
                return;
            }
            spellsSpell spell = (spellsSpell)SpellName.SelectedItems[0];
            string style = "div { padding-top: 0px; padding-bottom: 0px; padding-right: 0px; padding-left: 30px; }\ndiv.heading { padding-top: 0px; padding-bottom: 0px; padding-right: 0px; padding-left: 0px; background-color: #000000; height: 30px; }\nh1 { margin-bottom: 0; margin-top: 0; text-indent: -20px; font-size: 22; font-family: arial; }\nh2 { margin-bottom: 0; margin-top: 0; text-indent: -20px; font-size: 22; font-family: arial; text-align:left; }\nh3 { margin-bottom: 0; margin-top: 0; margin-left: -20px; text-indent: 0px; font-size: 16; font-weight: normal; font-family: arial; }\nh4 { margin-bottom: 0px; margin-top: 0px; margin-left: -20px; text-indent: 0px; text-align: justify; font-size: 16; font-weight: normal; font-family: arial; }\np { margin-top: 0px; margin-right: 0px; margin-bottom: 1px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 1px; padding-left: 0px; }\nh5 { margin-bottom: 0; margin-top: 0; text-indent: -20px; font-size: 16; font-weight: normal; font-family: arial; }\nh6 { margin-bottom: 0; margin-top: 0; margin-left: 30px; text-indent: -20px; font-size: 16; font-weight: normal; font-family: arial; }\np.alignleft { margin-left: 0px; text-indent: 10px; float: left; text-align:left; font-weight: bold; font-size: 22; font-family: arial; color: white; }\np.alignright { float: right; text-align:right; font-weight: bold; font-size: 22; font-family: arial; color: white; }\nli{ text-indent: 0px; }\n";
            string html = "<html><head><title>" + spell.name + "</title><style>\n" + style + "</style></head><body>";
            foreach(var item in SpellName.SelectedItems) {
                html += ((spellsSpell)item).description.full_text.Replace("<link rel=\"stylesheet\"href=\"PF.css\">", "") + "<br />";
            }
            html += "</body></html>";
            SpellInfo.DocumentText = html;
        }

        private void ClassFilter_SelectedValueChanged(object sender, EventArgs e) {
            if(ClassFilter.SelectedIndex >= 1)
                classfilter = (string)ClassFilter.SelectedItem;
            else
                classfilter = null;
            repopulate();
        }

        private void LevelFilter_SelectedValueChanged(object sender, EventArgs e) {
            levelfilter = LevelFilter.SelectedIndex - 1;
            repopulate();
        }

        private void button1_Click(object sender, EventArgs e) {
            NameFilter.Text = "";
            namefilter = null;
            DescFilter.Text = "";
            descriptionfilter = null;
            ClassFilter.SelectedIndex = 0;
            classfilter = null;
            LevelFilter.SelectedIndex = 0;
            levelfilter = -1;
            VFilter.CheckState = CheckState.Indeterminate;
            SFilter.CheckState = CheckState.Indeterminate;
            MFilter.CheckState = CheckState.Indeterminate;
            FFilter.CheckState = CheckState.Indeterminate;
            DFFilter.CheckState = CheckState.Indeterminate;
            SourceFilter.SelectedIndex = -1;
            SourceFilter.SelectedIndex = 0;
            SchoolFilter.SelectedIndex = 0;
            schoolfilter = null;
            DescriptorFilter.SelectedIndex = 0;
            descriptorfilter = null;
            DomainFilter.SelectedIndex = 0;
            BloodlineFilter.SelectedIndex = 0;
            repopulate();
        }

        private void CheckedChanged(object sender, EventArgs e) {
            EMFilter.Enabled = MFilter.Checked;
            EFFilter.Enabled = FFilter.Checked || DFFilter.Checked;
            repopulate();
        }

        private void SourceFilter_SelectedValueChanged(object sender, EventArgs e) {
            repopulate();
        }

        private void Form1_Shown(object sender, EventArgs e) {
            WindowState = FormWindowState.Maximized;
            SpellName.SetSelected(0, true);
            SpellName.Select();
        }

        private void DomainFilter_SelectedIndexChanged(object sender, EventArgs e) {
            if(SchoolFilter.SelectedIndex >= 1)
                schoolfilter = (string)SchoolFilter.SelectedItem;
            else
                schoolfilter = null;
            repopulate();
        }

        private void DescriptorFilter_SelectedIndexChanged(object sender, EventArgs e) {
            if(DescriptorFilter.SelectedIndex >= 1)
                descriptorfilter = ((string)DescriptorFilter.SelectedItem).Replace('-', '_');
            else
                descriptorfilter = null;
            repopulate();
        }

        private void createXml() {
            // Allocate data for spells.
            spells newData = new spells();
            int lineCount = 0;
            try {
                lineCount = System.IO.File.ReadLines("spells.csv").Count();
            } catch(Exception) {
                MessageBox.Show("This program requires either a spells.xml or a spells.csv to generate it.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(-1);
                return;
            }
            newData.spell = new spellsSpell[lineCount - 1];

            // Parse spells.
            System.IO.StreamReader file = new System.IO.StreamReader("spells.csv");
            string line;
            bool first = true;
            int ctr = 0;
            List<string> keys = null;
            while((line = file.ReadLine()) != null) {
                // Get the keys.
                if(first) {
                    keys = new List<string>(line.Split(','));
                    first = false;
                    continue;
                }

                // Get the values line by line.
                List<string> values = new List<string>(line.Replace("NULL", "-1").Split(','));
                for(int i = 0; i < values.Count-1; i++) {
                    // If a value starts with " we need to concatenate some fields.
                    if(values[i].StartsWith("\"") && !values[i].EndsWith("\"")) {
                        int remove = 0;
                        for(int j = i+1; j < values.Count; j++) {
                            values[i] = values[i] + "," + values[j];
                            ++remove;
                            if(values[j].EndsWith("\""))
                                break;
                        }

                        // Remove the extras.
                        values.RemoveRange(i + 1, remove);
                    }
                }

                // Strip the ".
                for(int i = 0; i < values.Count; i++) {
                    if(values[i].StartsWith("\"") && values[i].EndsWith("\""))
                        values[i] = values[i].Substring(1, values[i].Length - 2);
                    values[i] = values[i].Replace("\"\"", "\"");
                }

                // Assert no error.
                if(keys.Count != values.Count) {
                    MessageBox.Show("Error in csv, amount of fields per line not consistent.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    Environment.Exit(-1);
                }

                Dictionary<string, string> dict = new Dictionary<string, string>();
                for(int i = 0; i < keys.Count; ++i)
                    dict.Add(keys[i], values[i]);
                newData = AddDictToSpells(dict, ctr, newData);
                ++ctr;
            }
            file.Close();

            System.IO.Stream stream = System.IO.File.Open("spells.xml", System.IO.FileMode.Create);
            XmlSerializer ser = new XmlSerializer(typeof(spells));
            ser.Serialize(stream, newData);
            stream.Flush();
            stream.Close();

            System.IO.File.Delete("spells.csv");
        }

        spells AddDictToSpells(Dictionary<string, string> dict, int num, spells newData) {
            string value = null;
            spellsSpell spell = new spellsSpell();

            try {
                dict.TryGetValue("name", out value);
                spell.name = value;
            } catch(NullReferenceException) {
                MessageBox.Show("Unnamed spell in csv.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(-1);
            }

            dict.TryGetValue("school", out value);
            spell.school = value;

            dict.TryGetValue("subschool", out value);
            spell.subschool = value;

            dict.TryGetValue("casting_time", out value);
            spell.casting_time = value;

            dict.TryGetValue("range", out value);
            spell.range = value;

            dict.TryGetValue("area", out value);
            spell.area = value;

            dict.TryGetValue("effect", out value);
            spell.effect = value;

            dict.TryGetValue("targets", out value);
            spell.targets = value;

            dict.TryGetValue("duration", out value);
            spell.duration = value;

            dict.TryGetValue("saving_throw", out value);
            spell.saving_throw = value;

            dict.TryGetValue("spell_resistance", out value);
            spell.spell_resistance = value;

            dict.TryGetValue("source", out value);
            spell.source = value;

            dict.TryGetValue("deity", out value);
            spell.deity = value;

            dict.TryGetValue("domain", out value);
            spell.domain = value;

            dict.TryGetValue("short_description", out value);
            spell.short_description = value;

            dict.TryGetValue("bloodline", out value);
            spell.bloodline = value;

            dict.TryGetValue("patron", out value);
            spell.patron = value;

            dict.TryGetValue("linktext", out value);
            spell.hyperlink = value;

            dict.TryGetValue("dismissible", out value);
            spell.dismissible = value;

            dict.TryGetValue("shapeable", out value);
            spell.shapeable = value;

            dict.TryGetValue("id", out value);
            spell.id = value;

            spell.descriptor = new spellsSpellDescriptor();
            dict.TryGetValue("descriptor", out value);
            spell.descriptor.humanreadable = value;
            dict.TryGetValue("acid", out value);
            spell.descriptor.acid = value;
            dict.TryGetValue("air", out value);
            spell.descriptor.air = value;
            dict.TryGetValue("chaotic", out value);
            spell.descriptor.chaotic = value;
            dict.TryGetValue("cold", out value);
            spell.descriptor.cold = value;
            dict.TryGetValue("curse", out value);
            spell.descriptor.curse = value;
            dict.TryGetValue("darkness", out value);
            spell.descriptor.darkness = value;
            dict.TryGetValue("death", out value);
            spell.descriptor.death = value;
            dict.TryGetValue("disease", out value);
            spell.descriptor.disease = value;
            dict.TryGetValue("earth", out value);
            spell.descriptor.earth = value;
            dict.TryGetValue("electricity", out value);
            spell.descriptor.electricity = value;
            dict.TryGetValue("emotion", out value);
            spell.descriptor.emotion = value;
            dict.TryGetValue("evil", out value);
            spell.descriptor.evil = value;
            dict.TryGetValue("fear", out value);
            spell.descriptor.fear = value;
            dict.TryGetValue("fire", out value);
            spell.descriptor.fire = value;
            dict.TryGetValue("force", out value);
            spell.descriptor.force = value;
            dict.TryGetValue("good", out value);
            spell.descriptor.good = value;
            dict.TryGetValue("language_dependent", out value);
            spell.descriptor.language_dependent = value;
            dict.TryGetValue("lawful", out value);
            spell.descriptor.lawful = value;
            dict.TryGetValue("light", out value);
            spell.descriptor.light = value;
            dict.TryGetValue("mind_affecting", out value);
            spell.descriptor.mind_affecting = value;
            dict.TryGetValue("pain", out value);
            spell.descriptor.pain = value;
            dict.TryGetValue("poison", out value);
            spell.descriptor.poison = value;
            dict.TryGetValue("shadow", out value);
            spell.descriptor.shadow = value;
            dict.TryGetValue("sonic", out value);
            spell.descriptor.sonic = value;
            dict.TryGetValue("water", out value);
            spell.descriptor.water = value;
            dict.TryGetValue("ruse", out value);
            spell.descriptor.ruse = value;
            dict.TryGetValue("draconic", out value);
            spell.descriptor.draconic = value;
            dict.TryGetValue("meditative", out value);
            spell.descriptor.meditative = value;

            spell.description = new spellsSpellDescription();

            dict.TryGetValue("description", out value);
            spell.description.unformatted = value;
            dict.TryGetValue("description_formated", out value);
            spell.description.formatted = value;
            try {
                dict.TryGetValue("full_text", out value);
                spell.description.full_text = value;
            } catch(NullReferenceException) {
                MessageBox.Show("Spell without html description in csv.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(-1);
            }


            spell.spell_level = new spellsSpellSpell_level();

            dict.TryGetValue("spell_level", out value);
            spell.spell_level.humanreadable = value;
            dict.TryGetValue("sor", out value);
            spell.spell_level.sorcerer = value;
            dict.TryGetValue("wiz", out value);
            spell.spell_level.wizard = value;
            dict.TryGetValue("cleric", out value);
            spell.spell_level.cleric = value;
            dict.TryGetValue("druid", out value);
            spell.spell_level.druid = value;
            dict.TryGetValue("ranger", out value);
            spell.spell_level.ranger = value;
            dict.TryGetValue("bard", out value);
            spell.spell_level.bard = value;
            dict.TryGetValue("paladin", out value);
            spell.spell_level.paladin = value;
            dict.TryGetValue("alchemist", out value);
            spell.spell_level.alchemist = value;
            dict.TryGetValue("summoner", out value);
            spell.spell_level.summoner = value;
            dict.TryGetValue("witch", out value);
            spell.spell_level.witch = value;
            dict.TryGetValue("inquisitor", out value);
            spell.spell_level.inquisitor = value;
            dict.TryGetValue("oracle", out value);
            spell.spell_level.oracle = value;
            dict.TryGetValue("antipaladin", out value);
            spell.spell_level.antipaladin = value;
            dict.TryGetValue("magus", out value);
            spell.spell_level.magus = value;
            dict.TryGetValue("adept", out value);
            spell.spell_level.adept = value;
            dict.TryGetValue("bloodrager", out value);
            spell.spell_level.bloodrager = value;
            dict.TryGetValue("shaman", out value);
            spell.spell_level.shaman = value;
            dict.TryGetValue("psychic", out value);
            spell.spell_level.psychic = value;
            dict.TryGetValue("medium", out value);
            spell.spell_level.medium = value;
            dict.TryGetValue("mesmerist", out value);
            spell.spell_level.mesmerist = value;
            dict.TryGetValue("occultist", out value);
            spell.spell_level.occultist = value;
            dict.TryGetValue("spiritualist", out value);
            spell.spell_level.spiritualist = value;
            dict.TryGetValue("skald", out value);
            spell.spell_level.skald = value;
            dict.TryGetValue("investigator", out value);
            spell.spell_level.investigator = value;
            dict.TryGetValue("hunter", out value);
            spell.spell_level.hunter = value;
            dict.TryGetValue("summoner_unchained", out value);
            spell.spell_level.summoner_unchained = value;

            spell.components = new spellsSpellComponents();

            dict.TryGetValue("components", out value);
            spell.components.humanreadable = value;

            String components = spell.components.humanreadable;
            Regex re = new Regex(@"\(.*?\)");
            components = re.Replace(components, string.Empty);
            spell.components.verbal = components.Contains("V") ? "1" : "0";
            spell.components.somatic = components.Contains("S") ? "1" : "0";
            spell.components.material = components.Contains("F") ? "1" : "0";
            spell.components.focus = components.Contains("F") ? "1" : "0";
            spell.components.divine_focus = components.Contains("DF") ? "1" : "0";

            /* d20pfsrd sucks at data entry. I'll do it myself.
            dict.TryGetValue("verbal", out value);
            spell.components.verbal = value;
            dict.TryGetValue("somatic", out value);
            spell.components.somatic = value;
            dict.TryGetValue("material", out value);
            spell.components.material = value;
            dict.TryGetValue("focus", out value);
            spell.components.focus = value;
            dict.TryGetValue("divine_focus", out value);
            spell.components.divine_focus = value;*/

            spell.components.costly_components = new spellsSpellComponentsCostly_components();

            dict.TryGetValue("costly_components", out value);
            spell.components.costly_components.Value = value;
            dict.TryGetValue("material_costs", out value);
            spell.components.costly_components.cost = value;

            spell.mythic = new spellsSpellMythic();

            dict.TryGetValue("mythic", out value);
            spell.mythic.Value = value;
            dict.TryGetValue("mythic_text", out value);
            spell.mythic.mythic_text = value;
            dict.TryGetValue("augmented", out value);
            spell.mythic.augmented = value;

            newData.spell[num] = spell;
            return newData;
        }

        private void DomainFilter_SelectedIndexChanged_1(object sender, EventArgs e) {
            repopulate();
        }

        private void BloodlineFilter_SelectedIndexChanged(object sender, EventArgs e) {
            repopulate();
        }

        private void Print_Click(object sender, EventArgs e) {
            SpellInfo.ShowPrintDialog();
        }

        private void PrintPreview_Click(object sender, EventArgs e) {
            SpellInfo.ShowPrintPreviewDialog();
        }

        private void Save_Click(object sender, EventArgs e) {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Filter = "html files (*.html)|*.html";
            dlg.FilterIndex = 0;
            dlg.FileName = SpellInfo.DocumentTitle + ".html";

            if(dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
                System.IO.StreamWriter stream;
                if((stream = new System.IO.StreamWriter(dlg.OpenFile())) != null) {
                    stream.Write(SpellInfo.DocumentText);
                    stream.Close();
                }
            }
        }

        private void NameFilter_Enter(object sender, EventArgs e) {
            
        }

        private void waterMarkTextBox1_TextChanged(object sender, EventArgs e) {
            Console.Out.WriteLine(DescFilter.Text);
            descriptionfilter = DescFilter.Text;
            repopulate();
        }
    }
}
