using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media;
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.FileIO;

namespace DX1Layout
   {
   public partial class DX1Form : Form
      {
      bool Debug = false;
      const int kMaxButtons = 50; // Maximum number of buttons supported by DX1

      System.Drawing.Color kColorNormal = System.Drawing.Color.Black;
      System.Drawing.Color kColorHeader = System.Drawing.Color.Blue;
      System.Drawing.Color kColorError = System.Drawing.Color.Red;

      const String mPrefsFilename = "DX1LayoutPrefs.txt";
      String mBaseDirectory;
      String mLayoutFilename;
      String mGameFilename;
      String mButtonFilename;
      String mLabelsFilename;

      String mBackgroundFilename;
      Bitmap mBackground;
      bool mOverwrite;
      String mOutputFilename;

      struct LayoutItem
         {
         public int x;
         public int y;
         public int rot;
         }
      LayoutItem[] mLayoutArray = new LayoutItem[kMaxButtons];
      int mButtonCount = 0;

      struct ButtonLabelFieldInfo
         {
         public String Fontname;
         public float FontSize;
         public String FontColor;
         public Font LabelFont;
         public String Horz;
         public String Vert;
         public int X;
         public int Y;
         public int W;
         public int H;
         }
      struct ButtonInfo
         {
         public ButtonInfo(ButtonInfo i)
            {
            ImageFilename = i.ImageFilename;
            ButtonImage = new Bitmap(i.ButtonImage);
            DXNum = i.DXNum;
            Keyname = i.Keyname;
            Action = i.Action;
            }
         public String ImageFilename;
         public Bitmap ButtonImage;
         public ButtonLabelFieldInfo DXNum;
         public ButtonLabelFieldInfo Keyname;
         public ButtonLabelFieldInfo Action;
         }
      ButtonInfo mButtonBlank;
      ButtonInfo mButtonAction;
      ButtonInfo mButtonOther;
      ButtonInfo mButtonIcon;

      struct LabelItem
         {
         public int ButtonNum;
         public String WhichBlank;
         public String KeyName;
         public String Action;
         }
      LabelItem[] mLabels = new LabelItem[kMaxButtons];
      int mLabelsRead = 0;

      public DX1Form()
         {
         InitializeComponent();
         }

      private void DX1Form_Load(object sender, EventArgs e)
         {
         // Load prefs, like previous directories and files run
         Debug = false;
         DirectoryBox.Text = System.Environment.CurrentDirectory;
         mBaseDirectory = DirectoryBox.Text;
         mOverwrite = false;
         try
            {
            System.IO.StreamReader file = new System.IO.StreamReader(mPrefsFilename);
            Log("Loading Prefs", kColorHeader);
            bool Success = true;
            String lDirectory;
            String lLayoutFilename;
            String lGameFilename;
//            String lOverwrite;
            try
               {
               if ((lDirectory = file.ReadLine()) == null)
                  {
                  Log("Unable to read default directory from prefs file", kColorError);
                  Success = false;
                  }
               else
                  {
                  Log("Default Directory: " + lDirectory, kColorNormal);
                  DirectoryBox.Text = lDirectory;
                  mBaseDirectory = lDirectory;
                  }
               if (Success)   // If there was an error then assume the file is corrupt
                  {
                  if ((lLayoutFilename = file.ReadLine()) == null)
                     {
                     Log("Unable to read Layout Filename from prefs file", kColorError);
                     Success = false;
                     }
                  else
                     {
                     Log("Layout Filename: " + lLayoutFilename, kColorNormal);
                     LayoutFileTexbox.Text = lLayoutFilename;
                     mLayoutFilename = lLayoutFilename;
                     }
                  }
               if (Success)
                  {
                  if ((lGameFilename = file.ReadLine()) == null)
                     {
                     Log("Unable to read Game Filename from prefs file", kColorError);
                     Success = false;
                     }
                  else
                     {
                     Log("Game Filename: " + lGameFilename, kColorNormal);
                     GameFileTextbox.Text = lGameFilename;
                     mGameFilename = lGameFilename;
                     }
                  }
               file.Close();
               file.Dispose();
               }
            catch
               {
               Log("Error reading prefs file", kColorError);
               }

            }
         catch
            {
            // Do nothing.  We'll use the defaults we have
            }
         }

      bool HasTrailingSlash(String iPath)
         {
         return (iPath.Substring(iPath.Length - 1) == @"\");
         }

      bool HasSlash(String iPath)
         {
         return (iPath.IndexOf(@"\") != -1);
         }

      private void DirectoryButton_Click(object sender, EventArgs e)
         {
         FolderBrowserDialog dialog = new FolderBrowserDialog();
         dialog.Description = "Set the working directory";
         dialog.SelectedPath = DirectoryBox.Text;
         if (dialog.ShowDialog() == DialogResult.OK)
            {
            DirectoryBox.Text = dialog.SelectedPath;
            mBaseDirectory = DirectoryBox.Text;
            }
         }

      String BrowseFilename(String iFilename)
         {
         String lFilename = iFilename;
         // Select file
         OpenFileDialog dlg = new OpenFileDialog();
         dlg.Title = "Select Layout File";
         dlg.InitialDirectory = DirectoryBox.Text;
         dlg.Filter = "txt files(*.txt)|*.txt|All files (*.*)|*.*";
         dlg.RestoreDirectory = true;
         dlg.CheckFileExists = true;
         if (dlg.ShowDialog() == DialogResult.OK)
            {
            lFilename = dlg.FileName;
            String lDirectory = Path.GetDirectoryName(lFilename);
            // Need to handle if the user typed in a directory with a trailing slash
            if (HasTrailingSlash(mBaseDirectory))
               {
               lDirectory += @"\";
               }
            if (lDirectory != mBaseDirectory)
               {
               // Popup warning
               if (MessageBox.Show("Different Directory",
                     "All of the other files will come from the Base Directory, not the folder you just navigated to.  Is that what you want?",
                     MessageBoxButtons.YesNo) == DialogResult.No)
                  {
                  // Save full path
                  lFilename = iFilename;
                  }
               }
            else
               {
               // Grab off just the filename
               lFilename = Path.GetFileName(lFilename);
               }
            }
         return lFilename;
         }

      private void DirectoryBox_TextChanged(object sender, EventArgs e)
         {
         mBaseDirectory = DirectoryBox.Text;
         }

      private void BrowseLayout_Click(object sender, EventArgs e)
         {
         // Select layout file
         mLayoutFilename = BrowseFilename(mLayoutFilename);
         LayoutFileTexbox.Text = mLayoutFilename;
         }

      private void BrowseGameFile_Click(object sender, EventArgs e)
         {
         // Select Game file
         mGameFilename = BrowseFilename(mGameFilename);
         GameFileTextbox.Text = mGameFilename;
         }

      private void GoButton_Click(object sender, EventArgs e)
         {
         DoIt();
         }

      private void DirectoryBox_KeyDown(object sender, KeyEventArgs e)
         {
         if (e.KeyCode == Keys.Enter)
            {
            e.SuppressKeyPress = true;
            DoIt();
            }
         }

      private void LayoutFileTexbox_KeyDown(object sender, KeyEventArgs e)
         {
         if (e.KeyCode == Keys.Enter)
            {
            e.SuppressKeyPress = true;
            DoIt();
            }
         }

      private void GameFileTextbox_KeyDown(object sender, KeyEventArgs e)
         {
         if (e.KeyCode == Keys.Enter)
            {
            e.SuppressKeyPress = true;
            DoIt();
            }
         }

      void Log(String LogString, System.Drawing.Color i_Color)
         {
         // Or try http://blogs.msdn.com/b/jmstall/archive/2006/01/13/color-print.aspx
         LogTextBox.SelectionColor = i_Color;
         LogTextBox.AppendText(LogString + "\n");
         LogTextBox.SelectionStart = LogTextBox.Text.Length;
         LogTextBox.ScrollToCaret();
         }

      void LogButtonInfo(String FieldLabel, ButtonLabelFieldInfo info)
         {
         Log(FieldLabel + "Fontname: " + info.Fontname + " FontSize=" + info.FontSize +
            " FontColor=" + info.FontColor + " H=" + info.Horz + " V=" + info.Vert +
            " X=" + info.X + " Y=" + info.Y, kColorNormal);
         }

      void DoIt()
         {
         // Grab filenames from controls
         StringBuilder sb = new StringBuilder(); // And save them into the prefs file
         mBaseDirectory = DirectoryBox.Text;
         sb.AppendLine(mBaseDirectory);   // Save off exactly what was in the textboxes
         if (!HasTrailingSlash(mBaseDirectory))
            mBaseDirectory += @"\";
         mLayoutFilename = LayoutFileTexbox.Text;
         sb.AppendLine(mLayoutFilename);
         if (!HasSlash(mLayoutFilename))
            mLayoutFilename = mBaseDirectory + mLayoutFilename;
         mGameFilename = GameFileTextbox.Text;
         sb.AppendLine(mGameFilename);
         if (!HasSlash(mGameFilename))
            mGameFilename = mBaseDirectory + mGameFilename;
         mOverwrite = OverwriteCheckbox.Checked;
         // Write prefs
         using (StreamWriter outfile = new StreamWriter(mPrefsFilename))
            {
            outfile.Write(sb.ToString());
            outfile.Close();
            }

         // Read the files
         bool Success = ReadFiles();
         if (Success)
            {
            LayoutButtons();
            mBackground.Dispose();
            mButtonBlank.ButtonImage.Dispose();
            mButtonAction.ButtonImage.Dispose();
            mButtonOther.ButtonImage.Dispose();
            mButtonIcon.ButtonImage.Dispose();
            }
         if (Success)
            Log("Layout complete", kColorHeader);
         else
            Log("Errors found", kColorError);
         }

      bool ReadFiles()
         {
         bool Success = true;

         Log("Go!", kColorHeader);

         // Load layout
         Log("Load Layout file: " + mLayoutFilename, kColorHeader);
         Success = LoadLayout(mLayoutFilename);

         if (Success)
            {
            // Load Game.txt
            Log("Load Game file: " + mGameFilename, kColorHeader);
            Success = LoadGame(mGameFilename);
            }

         if (Success)
            {
            // Load Button.txt
            Log("Load Button file: " + mButtonFilename, kColorHeader);
            Success = LoadButton(mBaseDirectory + mButtonFilename);
            }

         if (Success)
            {
            // Load Labels.txt
            Log("Load Labels file: " + mLabelsFilename, kColorHeader);
            Success = LoadLabels(mBaseDirectory + mLabelsFilename);
            }

         if (Success)
            {
            // Load the various image files and fonts referenced
            Log("Loading referenced items", kColorHeader);
            Log("Load Background image: " + mBackgroundFilename, kColorNormal);
            Success = LoadImage(mBaseDirectory + mBackgroundFilename, ref mBackground);
            if (Success)
               {
               Log("Load Blank button: " + mButtonBlank.ImageFilename, kColorNormal);
               Success = LoadImage(mBaseDirectory + mButtonBlank.ImageFilename, ref mButtonBlank.ButtonImage);
               }
            if (Success)
               {
               Log("Load Action button: " + mButtonAction.ImageFilename, kColorNormal);
               Success = LoadImage(mBaseDirectory + mButtonAction.ImageFilename, ref mButtonAction.ButtonImage);
               }
            if (Success)
               {
               Log("Load Other button: " + mButtonOther.ImageFilename, kColorNormal);
               Success = LoadImage(mBaseDirectory + mButtonOther.ImageFilename, ref mButtonOther.ButtonImage);
               }
            if (Success)
               {
               Log("Load Icon button: " + mButtonIcon.ImageFilename, kColorNormal);
               Success = LoadImage(mBaseDirectory + mButtonIcon.ImageFilename, ref mButtonIcon.ButtonImage);
               }
            // Load fonts
            if (Success)
               {
               Log("Load fonts", kColorNormal);
               Success = LoadButtonFonts(ref mButtonBlank);
               }
            if (Success)
               {
               Success = LoadButtonFonts(ref mButtonAction);
               }
            if (Success)
               {
               Success = LoadButtonFonts(ref mButtonOther);
               }
            if (Success)
               {
               Success = LoadButtonFonts(ref mButtonIcon);
               }
            }

         return Success;
         }

      bool LoadImage(String iFilename, ref Bitmap oImage)
         {
         bool Success = true;
         try
            {
            oImage = (Bitmap)Image.FromFile(iFilename, true);
            }
         catch (System.IO.FileNotFoundException)
            {
            Log("Error loading: " + iFilename, kColorError);
            Success = false;
            }
         return Success;
         }

      bool LoadButtonFonts(ref ButtonInfo ioButton)
         {
         bool Success = true;

         Success = LoadFont(ioButton.DXNum.Fontname, ioButton.DXNum.FontSize, ref ioButton.DXNum.LabelFont);
         if (Success)
            {
            Success = LoadFont(ioButton.Keyname.Fontname, ioButton.Keyname.FontSize, ref ioButton.Keyname.LabelFont);
            }
         if (Success)
            {
            Success = LoadFont(ioButton.Action.Fontname, ioButton.Action.FontSize, ref ioButton.Action.LabelFont);
            }

         return Success;
         }

      bool LoadFont(String iFontname, float iFontSize, ref Font oFont)
         {
         // http://stackoverflow.com/questions/1669729/how-do-i-get-a-font-from-a-string?rq=1
         // http://www.dotnetperls.com/font
         bool Success = true;

         oFont = new Font(iFontname, iFontSize);
         if (oFont.Name != iFontname)
            {
            Log("Error loading font: " + iFontname, kColorError);
            Success = false;
            }
         // Could fall back to Arial, or Times New Roman, or something like that

         return Success;
         }

      bool LoadLayout(String iFilename)
         {
         bool Success = true;
         int LineCount = 0;
         try
            {
            TextFieldParser parser = new TextFieldParser(iFilename);
            parser.TextFieldType = FieldType.Delimited;
            parser.SetDelimiters(",");

            while (!parser.EndOfData)
               {
               if (LineCount < kMaxButtons)
                  {
                  //Processing row
                  try
                     {
                     string[] fields = parser.ReadFields();
                     // Process fields
                     int x = mLayoutArray[LineCount].x = Convert.ToInt16(fields[0]);
                     int y = mLayoutArray[LineCount].y = Convert.ToInt16(fields[1]);
                     int rot = mLayoutArray[LineCount].rot = Convert.ToInt16(fields[2]);
                     LineCount++;
                     // The local x, y, and rot are simply to make the code look cleaner
                     // Using LineCount after increment for human numbering in text editor
                     if (Debug)
                        Log("Line: " + LineCount + " x=" + x + " y=" + y + " rot=" + rot, kColorNormal);
                     }
                  catch (Exception ex)
                     {
                     Log("Layout Error - Line " + (LineCount + 1) + ": " + ex.Message, kColorError);
                     Success = false;
                     }
                  }
               else
                  {
                  Log("Layout Error: Too many lines", kColorError);
                  Success = false;
                  break;
                  }
               }
            parser.Close();
            }
         catch (Exception ex)
            {
            Log("Error opening file: " + iFilename + ". Original error: " + ex.Message, kColorError);
            }
         mButtonCount = LineCount; // Save off how many buttons we found in the Layout file
         Log("Read " + mButtonCount + " buttons", kColorNormal);
         return Success;
         }

      bool LoadGame(String iFilename)
         {
         bool Success = true;
         try
            {
            System.IO.StreamReader file = new System.IO.StreamReader(iFilename);
            try
               {
               if ((mBackgroundFilename = file.ReadLine()) == null)
                  {
                  Log("Unable to read background file name", kColorError);
                  Success = false;
                  }
               else
                  {
                  Log("Background Filename: " + mBackgroundFilename, kColorNormal);
                  }
               if (Success)
                  {
                  if ((mButtonFilename = file.ReadLine()) == null)
                     {
                     Log("Unable to read Button file name from Game file", kColorError);
                     Success = false;
                     }
                  else
                     {
                     Log("Button Filename: " + mButtonFilename, kColorNormal);
                     }
                  }
               if (Success)
                  {
                  if ((mLabelsFilename = file.ReadLine()) == null)
                     {
                     Log("Unable to read Labels file name from Game file", kColorError);
                     Success = false;
                     }
                  else
                     {
                     Log("Labels Filename: " + mLabelsFilename, kColorNormal);
                     }
                  }
               if (Success)
                  {
                  if ((mOutputFilename = file.ReadLine()) == null)
                     {
                     Log("Unable to read Output file name from Game file", kColorError);
                     Success = false;
                     }
                  else
                     {
                     Log("Output Filename: " + mOutputFilename, kColorNormal);
                     if (!HasSlash(mOutputFilename))
                        mOutputFilename = mBaseDirectory + mOutputFilename;
                     if (!mOverwrite)
                        {
                        // Popup warning
                        if (MessageBox.Show("The file you're writing to exists.  Overwrite?",
                              "Output file exists",
                              MessageBoxButtons.YesNo) == DialogResult.No)
                           {
                           // Don't overwrite the file
                           Success = false;
                           Log("Output file exists, Overwrite disabled", kColorError);
                           }
                        }
                     }
                  }
               file.Close();
               }
            catch (Exception ex)
               {
               Log("Error: Could not read file from disk. Original error: " + ex.Message, kColorError);
               }
            }
         catch (Exception ex)
            {
            Log("Error opening file: " + iFilename + ". Original error: " + ex.Message, kColorError);
            }

         return Success;
         }

      bool LoadButton(String iFilename)
         {
         bool Success = true;
         try
            {
            TextFieldParser parser = new TextFieldParser(iFilename);
            parser.TextFieldType = FieldType.Delimited;
            parser.SetDelimiters(",");

            if (!parser.EndOfData)
               {
               //Processing row for Blank
               try
                  {
                  string[] fields = parser.ReadFields();
                  ParseButtonInfo(fields, ref mButtonBlank);
                  }
               catch (Exception ex)
                  {
                  Log("Layout Error - Line 1 Blank button - " + ex.Message, kColorError);
                  Success = false;
                  }
               //Processing row for Action
               if (Success)
                  {
                  try
                     {
                     string[] fields = parser.ReadFields();
                     ParseButtonInfo(fields, ref mButtonAction);
                     }
                  catch (Exception ex)
                     {
                     Log("Layout Error - Line 2 Action button - " + ex.Message, kColorError);
                     Success = false;
                     }
                  }
               //Processing row for Other
               if (Success)
                  {
                  try
                     {
                     string[] fields = parser.ReadFields();
                     ParseButtonInfo(fields, ref mButtonOther);
                     }
                  catch (Exception ex)
                     {
                     Log("Layout Error - Line 3 Other button - " + ex.Message, kColorError);
                     Success = false;
                     }
                  }
               //Processing row for Icon
               if (Success)
                  {
                  try
                     {
                     string[] fields = parser.ReadFields();
                     ParseButtonInfo(fields, ref mButtonIcon);
                     }
                  catch (Exception ex)
                     {
                     Log("Layout Error - Line 4 Icon button - " + ex.Message, kColorError);
                     Success = false;
                     }
                  }
               }
            else
               {
               Log("File empty", kColorError);
               Success = false;
               }

            parser.Close();
            }
         catch (Exception ex)
            {
            Log("Error opening file: " + iFilename + ". Original error: " + ex.Message, kColorError);
            }
         return Success;
         }

      bool ParseButtonInfo(string[] fields, ref ButtonInfo oButtonInfo)
         {
         bool Success = true;

         // Process fields
         int i = 0; // fields index, so I don't have to renumber over and over
         oButtonInfo.ImageFilename = fields[i++];
         Log("Image filename: " + oButtonInfo.ImageFilename, kColorNormal);
         ParseButtonFieldInfo(fields, ref oButtonInfo.DXNum, ref i);
         ParseButtonFieldInfo(fields, ref oButtonInfo.Keyname, ref i);
         ParseButtonFieldInfo(fields, ref oButtonInfo.Action, ref i);

         return Success;
         }

      bool ParseButtonFieldInfo(string[] fields, ref ButtonLabelFieldInfo oInfo, ref int i)
         {
         bool Success = true;

         oInfo.Fontname = fields[i++];
         oInfo.FontSize = Convert.ToSingle(fields[i++]);
         oInfo.FontColor = fields[i++];
         oInfo.Horz = fields[i++].ToUpper();
         oInfo.Vert = fields[i++].ToUpper();
         // Check for valid characters?  Currently doing it when actually writing on buttons
         oInfo.X = Convert.ToInt16(fields[i++]);
         oInfo.Y = Convert.ToInt16(fields[i++]);
         oInfo.W = Convert.ToInt16(fields[i++]);
         oInfo.H = Convert.ToInt16(fields[i++]);

         return Success;
         }

      bool LoadLabels(String iFilename)
         {
         bool Success = true;
         int LineCount = 0;
         try
            {
            TextFieldParser parser = new TextFieldParser(iFilename);
            parser.TextFieldType = FieldType.Delimited;
            parser.SetDelimiters(",");

            while (!parser.EndOfData)
               {
               // Do we want to keep track of buttons which have labels, and flag duplicates?
               if (LineCount < mButtonCount)
                  {
                  //Processing row
                  try
                     {
                     string[] fields = parser.ReadFields();
                     // Process fields
                     int ButtonNum = mLabels[LineCount].ButtonNum = Convert.ToInt16(fields[0]);
                     // This is where we could search for dupes
                     String KeyName = mLabels[LineCount].KeyName = fields[1];
                     String ButtonBlank = mLabels[LineCount].WhichBlank = fields[2].ToUpper();
                     // We could check for valid character here.  Currently doing it when actually doing positioning
                     String Action = mLabels[LineCount].Action = fields[3].Replace(@"\n", "\r\n");
                     LineCount++;
                     // The local copies are simply to make the code look cleaner
                     // Using LineCount after increment for human numbering in text editor
                     if (Debug)
                        Log("Line: " + LineCount + " ButtonNum=" + ButtonNum + " ButtonBlank=" + ButtonBlank +
                           " KeyName=" + KeyName + " Action=" + Action, kColorNormal);
                     if (ButtonNum > mButtonCount)
                        Log("Invalid button number", kColorError);
                     }
                  catch (Exception ex)
                     {
                     Log("Labels Error - Line " + (LineCount + 1) + ": " + ex.Message, kColorError);
                     Success = false;
                     }
                  }
               else
                  {
                  Log("Labels Error: Too many lines", kColorError);
                  Success = false;
                  break;
                  }
               }
            parser.Close();
            }
         catch (Exception ex)
            {
            Log("Error opening file: " + iFilename + ". Original error: " + ex.Message, kColorError);
            }
         mLabelsRead = LineCount;
         Log("Read " + mLabelsRead + " labels", kColorNormal);

         return Success;
         }

      bool LayoutButtons()
         {
         bool Success = true;
         Graphics lBackground = Graphics.FromImage(mBackground);
         lBackground.CompositingMode = CompositingMode.SourceOver;

         // For each button we have in the Layout array
         for (int i = 0; i < mButtonCount; i++)
            {
            // Search for it in the Labels array
            LabelItem lLabel;
            lLabel.ButtonNum = i + 1;
            lLabel.WhichBlank = "B";
            lLabel.KeyName = "";
            lLabel.Action = "";
            int LabelFound = -1;
            int j = 0;
            while ((LabelFound < 0) && (j < mLabelsRead))
               {
               if (mLabels[j].ButtonNum == (i + 1))
                  {
                  lLabel = mLabels[j];
                  LabelFound = j;
                  }
               j++;
               }
            // Make a copy of the blank indicated
            ButtonInfo lBlankToUse;
            switch (lLabel.WhichBlank)
               {
               case "B":
                  lBlankToUse = new ButtonInfo(mButtonBlank);
                  break;
               case "A":
                  lBlankToUse = new ButtonInfo(mButtonAction);
                  break;
               case "O":
                  lBlankToUse = new ButtonInfo(mButtonOther);
                  break;
               case "I":
                  lBlankToUse = new ButtonInfo(mButtonIcon);
                  break;
               default:
                  Log("Line " + j + "  Bad button blank field.  " + lLabel.WhichBlank + "  Using Blank", kColorError);
                  // Go ahead and copy the blank, anyway
                  lBlankToUse = new ButtonInfo(mButtonBlank);
                  break;
               }
            Graphics g = Graphics.FromImage(lBlankToUse.ButtonImage);
            DrawALabel(ref g, lBlankToUse.DXNum, Convert.ToString(lLabel.ButtonNum));
            DrawALabel(ref g, lBlankToUse.Keyname, lLabel.KeyName);
            if (lLabel.WhichBlank == "I")
               {
               // Do Icon stuff
               Bitmap Icon = null;
               Success = LoadImage(mBaseDirectory + lLabel.Action, ref Icon);
               if (Success)
                  {
                  g.DrawImage(Icon, new Point(lBlankToUse.Action.X, lBlankToUse.Action.Y));
                  }
               Icon.Dispose();
               }
            else
               {
               DrawALabel(ref g, lBlankToUse.Action, lLabel.Action);
               }
            if (Success)
               {
               if (Debug)
                  // Hmm.  I could spit out each button here
                  lBlankToUse.ButtonImage.Save("debug.png", ImageFormat.Png);

               // Rotate
               Bitmap Rotated = RotateImage(lBlankToUse.ButtonImage, mLayoutArray[i].rot, true, false,
                  System.Drawing.Color.Transparent);
               if (Debug)
                  Rotated.Save("debug.png", ImageFormat.Png);

               // Place onto background
               lBackground.DrawImage(Rotated, new Point(mLayoutArray[i].x, mLayoutArray[i].y));
               Rotated.Dispose();
               }
            g.Dispose();

            }
         if (Success)
            {
            // Write image file
            try
               {
               mBackground.Save(mOutputFilename, GetImageFormat(mOutputFilename));
               }
            catch (Exception ex)
               {
               Log("Error writing output file: " + mOutputFilename + ". Error: " + ex.Message, kColorError);
               }
            }
         lBackground.Dispose();

         return Success;
         }

      bool DrawALabel(ref Graphics g, ButtonLabelFieldInfo iFieldInfo, String iStr)
         {
         bool Success = true;
         g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
         if (!((iFieldInfo.Horz == "X") || (iFieldInfo.Vert == "X")))
            {
            StringFormat strFormat = new StringFormat();
            switch (iFieldInfo.Horz)
               {
               case "C":
                  strFormat.Alignment = StringAlignment.Center;
                  break;
               case "L":
                  strFormat.Alignment = StringAlignment.Near;
                  break;
               case "R":
                  strFormat.Alignment = StringAlignment.Far;
                  break;
               default:
                  Log("Bad Horizontal Alignment: " + iFieldInfo.Horz, kColorError);
                  break;
               }
            switch (iFieldInfo.Horz)
               {
               case "C":
                  strFormat.LineAlignment = StringAlignment.Center;
                  break;
               case "T":
                  strFormat.LineAlignment = StringAlignment.Near;
                  break;
               case "B":
                  strFormat.Alignment = StringAlignment.Far;
                  break;
               default:
                  Log("Bad Vertical Alignment: " + iFieldInfo.Horz, kColorError);
                  break;
               }
            g.DrawString(
               iStr,
               new Font(iFieldInfo.Fontname, iFieldInfo.FontSize, FontStyle.Regular),
               new System.Drawing.SolidBrush(System.Drawing.ColorTranslator.FromHtml(iFieldInfo.FontColor)),
               new Rectangle(iFieldInfo.X, iFieldInfo.Y,
                  iFieldInfo.W, iFieldInfo.H),
                  strFormat);
            }

         return Success;
         }

      /// From: http://stackoverflow.com/questions/2163829/how-do-i-rotate-a-picture-in-c-sharp
      /// <summary>
      /// Method to rotate an Image object. The result can be one of three cases:
      /// - upsizeOk = true: output image will be larger than the input, and no clipping occurs 
      /// - upsizeOk = false & clipOk = true: output same size as input, clipping occurs
      /// - upsizeOk = false & clipOk = false: output same size as input, image reduced, no clipping
      /// 
      /// A background color must be specified, and this color will fill the edges that are not 
      /// occupied by the rotated image. If color = transparent the output image will be 32-bit, 
      /// otherwise the output image will be 24-bit.
      /// 
      /// Note that this method always returns a new Bitmap object, even if rotation is zero - in 
      /// which case the returned object is a clone of the input object. 
      /// </summary>
      /// <param name="inputImage">input Image object, is not modified</param>
      /// <param name="angleDegrees">angle of rotation, in degrees</param>
      /// <param name="upsizeOk">see comments above</param>
      /// <param name="clipOk">see comments above, not used if upsizeOk = true</param>
      /// <param name="backgroundColor">color to fill exposed parts of the background</param>
      /// <returns>new Bitmap object, may be larger than input image</returns>
      public static Bitmap RotateImage(Image inputImage, float angleDegrees, bool upsizeOk,
                                    bool clipOk, System.Drawing.Color backgroundColor)
         {
         // Test for zero rotation and return a clone of the input image
         if (angleDegrees == 0f)
            return (Bitmap)inputImage.Clone();

         // Set up old and new image dimensions, assuming upsizing not wanted and clipping OK
         int oldWidth = inputImage.Width;
         int oldHeight = inputImage.Height;
         int newWidth = oldWidth;
         int newHeight = oldHeight;
         float scaleFactor = 1f;

         // If upsizing wanted or clipping not OK calculate the size of the resulting bitmap
         if (upsizeOk || !clipOk)
            {
            double angleRadians = angleDegrees * Math.PI / 180d;

            double cos = Math.Abs(Math.Cos(angleRadians));
            double sin = Math.Abs(Math.Sin(angleRadians));
            newWidth = (int)Math.Round(oldWidth * cos + oldHeight * sin);
            newHeight = (int)Math.Round(oldWidth * sin + oldHeight * cos);
            }

         // If upsizing not wanted and clipping not OK need a scaling factor
         if (!upsizeOk && !clipOk)
            {
            scaleFactor = Math.Min((float)oldWidth / newWidth, (float)oldHeight / newHeight);
            newWidth = oldWidth;
            newHeight = oldHeight;
            }

         // Create the new bitmap object. If background color is transparent it must be 32-bit, 
         //  otherwise 24-bit is good enough.
         Bitmap newBitmap = new Bitmap(newWidth, newHeight, backgroundColor == System.Drawing.Color.Transparent ?
               System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
         newBitmap.SetResolution(inputImage.HorizontalResolution, inputImage.VerticalResolution);

         // Create the Graphics object that does the work
         using (Graphics graphicsObject = Graphics.FromImage(newBitmap))
            {
            graphicsObject.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphicsObject.PixelOffsetMode = PixelOffsetMode.HighQuality;
            graphicsObject.SmoothingMode = SmoothingMode.HighQuality;

            // Fill in the specified background color if necessary
            if (backgroundColor != System.Drawing.Color.Transparent)
               graphicsObject.Clear(backgroundColor);

            // Set up the built-in transformation matrix to do the rotation and maybe scaling
            graphicsObject.TranslateTransform(newWidth / 2f, newHeight / 2f);

            if (scaleFactor != 1f)
               graphicsObject.ScaleTransform(scaleFactor, scaleFactor);

            graphicsObject.RotateTransform(angleDegrees);
            graphicsObject.TranslateTransform(-oldWidth / 2f, -oldHeight / 2f);

            // Draw the result 
            graphicsObject.DrawImage(inputImage, 0, 0);
            }

         return newBitmap;
         }

      // From: http://stackoverflow.com/questions/1337750/get-imageformat-from-file-extension
      private static ImageFormat GetImageFormat(string fileName)
         {
         string extension = Path.GetExtension(fileName);
         if (string.IsNullOrEmpty(extension))
            throw new ArgumentException(
                string.Format("Unable to determine file extension for fileName: {0}", fileName));

         switch (extension.ToLower())
            {
            case @".bmp":
               return ImageFormat.Bmp;

            case @".gif":
               return ImageFormat.Gif;

            case @".jpg":
            case @".jpeg":
               return ImageFormat.Jpeg;

            case @".png":
               return ImageFormat.Png;

            case @".tif":
            case @".tiff":
               return ImageFormat.Tiff;

            default:
               throw new NotImplementedException();
            }
         }

      }
   }