public static void Generate(string inputDirectory, IOutputForm outputForm)
        {
            try {
                outputForm.WriteOutputMessage("Starting flashcard generation...");
                outputForm.WriteOutputMessage("");

                InputDirectory = inputDirectory;
                OutputDeckFile = new OutputDeckFile();

                ValidateInput();
                var itemsFile = new ItemsFile(inputDirectory);
                var deckFile = new DeckFile(inputDirectory);

                CreateOutputDirectory(deckFile);

                foreach (var card in deckFile.Cards) {
                    GenerateCardImages(card, itemsFile, deckFile, outputForm);
                }

                foreach (var template in deckFile.Templates) {
                    GenerateCardImages(template, itemsFile, deckFile, outputForm);
                }

                outputForm.WriteOutputMessage("");
                outputForm.WriteOutputMessage("Writing output deck file...");
                OutputDeckFile.WriteFile(OutputDirectory, deckFile.DeckName);

                outputForm.WriteOutputMessage("");
                outputForm.WriteOutputMessage("Done!", Color.Green);
            }
            catch (Exception exception) {
                outputForm.WriteOutputErrorMessage("");
                outputForm.WriteOutputErrorMessage(new string('=', 40));
                outputForm.WriteOutputErrorMessage("");
                outputForm.WriteOutputErrorMessage("ERROR: " + exception.Message);
                outputForm.WriteOutputErrorMessage("");
            }
        }
        private static string GetBaseImagePathForCard(XmlNode sideNode, XmlNode cardNode, DeckFile deckFile)
        {
            string output = deckFile.GetPathFromAttribute(sideNode, "background");

            if (output == "") {
                output = deckFile.GetPathFromAttribute(cardNode, "background");
            }

            if (output == "") {
                output = deckFile.DefaultBackground;
            }

            if (output == "") {
                throw new XmlException("No background image specified for card");
            }
            else if (File.Exists(output) == false) {
                throw new FileNotFoundException(string.Format("Background image \"{0}\" could not be found.", output), output);
            }

            return output;
        }
        private static void GenerateCardImages(XmlNode cardNode, ItemsFile itemsFile, DeckFile deckFile, IOutputForm outputForm)
        {
            string cardID = GeneratorXmlFile.GetAttributeValue(cardNode, "id");
            int sideCounter = 0;

            if (cardNode.Name == "card") {
                foreach (XmlNode sideNode in cardNode.SelectNodes("side")) {
                    sideCounter++;

                    outputForm.WriteOutputMessage(string.Format("Generating side {0} of card \"{1}\"", sideCounter, cardID));
                    GenerateCardSideImage(sideNode, sideCounter, cardNode, cardID, itemsFile, deckFile, outputForm);
                }
            }
            else {
                outputForm.WriteOutputMessage(string.Format("Generating template \"{1}\"", sideCounter, cardID));
                GenerateCardSideImage(cardNode, sideCounter, cardNode, cardID, itemsFile, deckFile, outputForm);
            }
        }
        private static void GenerateCardSideImage(XmlNode sideNode, int sideCounter, XmlNode cardNode, string cardID, ItemsFile itemsFile, DeckFile deckFile, IOutputForm outputForm)
        {
            var cardSideTypeID = DetermineCardSideType(sideNode);
            string baseImagePath = GetBaseImagePathForCard(sideNode, cardNode, deckFile);
            var baseImage = Image.FromFile(baseImagePath);

            foreach (var item in itemsFile.Items) {
                string keyValue = itemsFile.GetKeyValueFromItemNode(item);
                string outputFileName = BuildOutputFileName(cardID, keyValue, sideCounter);
                string fullOutputFileName = Path.Combine(OutputDirectory, deckFile.MediaDirectoryName, outputFileName);

                switch (cardSideTypeID) {
                    case CardSideType.Image:
                        outputForm.WriteOutputMessage(string.Format("   - Generating image card side file \"{0}\"", outputFileName));
                        GenerateCardImageSideImage(sideNode, item, itemsFile, (Image) baseImage.Clone(), fullOutputFileName);
                        break;

                    case CardSideType.Text:
                        outputForm.WriteOutputMessage(string.Format("   - Generating text card side file \"{0}\"", outputFileName));
                        GenerateCardTextSideImage(sideNode, item, (Image) baseImage.Clone(), fullOutputFileName);
                        break;

                    case CardSideType.Templated:
                        string templateID = GeneratorXmlFile.GetAttributeValue(sideNode.FirstChild, "id");
                        outputFileName = BuildOutputFileName(templateID, keyValue, 0);
                        break;
                }

                if (cardNode.Name == "card") {
                    OutputDeckFile.Add(cardID, keyValue, outputFileName);
                }
            }

            baseImage.Dispose();
        }
        private static void CreateOutputDirectory(DeckFile deckFile)
        {
            if (Directory.Exists(OutputDirectory) == true) {
                Directory.Delete(OutputDirectory, true);
            }

            Directory.CreateDirectory(OutputDirectory);
            Directory.CreateDirectory(Path.Combine(OutputDirectory, deckFile.MediaDirectoryName));
        }