context: using System;
  using System.Collections.Generic;
  
  namespace FreelancerModStudio.Data.IO
  {
      public class FileManager
      {
          public string File { get; set; }
          public bool IsBini { get; set; }
  
          public FileManager(string file, bool isBINI)
          {
              IsBini = isBINI;
              File = file;
          }
  
          public FileManager(string file)
          {
              File = file;
          }
  
          public EditorINIData Read(FileEncoding encoding, int templateFileIndex)
          {
  #if DEBUG
              System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
              st.Start();
  #endif
              INIBlocks iniData = null;
              try
              {
                  //read basic file structure
                  if (encoding == FileEncoding.Automatic || encoding == FileEncoding.BINI)
                  {
                      IsBini = true;
  
                      BINIManager biniManager = new BINIManager(File);
                      biniManager.Read();
                      if (biniManager.IsBini)
                          iniData = biniManager.Data;
                      else
                      {
                          if (encoding == FileEncoding.Automatic)
                              iniData = ReadINI();
                          else
                              return null;
                      }
                  }
                  else
                      iniData = ReadINI();
              }
              catch (Exception ex)
              {
                  throw ex;
              }
  #if DEBUG
              st.Stop();
              System.Diagnostics.Debug.WriteLine("load data: " + st.ElapsedMilliseconds + "ms");
  #endif
  
              return GetEditorData(iniData, templateFileIndex); ;
          }
  
          EditorINIData GetEditorData(INIBlocks iniData, int templateFileIndex)
          {
  #if DEBUG
              System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
              st.Start();
  #endif
  
              EditorINIData editorData = new EditorINIData(templateFileIndex);
  
              //loop each template block
              for (int i = 0; i < Helper.Template.Data.Files[templateFileIndex].Blocks.Count; i++)
              {
                  Template.Block templateBlock = Helper.Template.Data.Files[templateFileIndex].Blocks[i];
  
                  List<INIOptions> iniBlocks;
                  if (iniData.TryGetValue(templateBlock.Name, out iniBlocks))
                  {
                      //loop each ini block
                      foreach (INIOptions iniBlock in iniBlocks)
                      {
                          EditorINIBlock editorBlock = new EditorINIBlock(templateBlock.Name, i);
  
                          //loop each template option
                          for (int j = 0; j < templateBlock.Options.Count; j++)
                          {
                              Template.Option childTemplateOption = null;
                              if (j < templateBlock.Options.Count - 1 && templateBlock.Options[j + 1].Parent != null)
                              {
                                  //next option is child of current option
                                  childTemplateOption = templateBlock.Options[j + 1];
                              }
  
                              Template.Option templateOption = templateBlock.Options[j];
                              EditorINIOption editorOption = new EditorINIOption(templateOption.Name, j);
  
                              List<INIOption> iniOptions;
                              if (iniBlock.TryGetValue(templateOption.Name, out iniOptions))
                              {
                                  //h is used to start again at last child option in order to provide better performance
                                  int h = 0;
  
                                  if (templateOption.Multiple)
                                  {
                                      //loop each ini option
                                      for (int k = 0; k < iniOptions.Count; k++)
                                      {
                                          List<object> editorChildOptions = null;
                                          List<INIOption> iniChildOptions;
                                          if (childTemplateOption != null && iniBlock.TryGetValue(childTemplateOption.Name, out iniChildOptions))
                                          {
                                              editorOption.ChildTemplateIndex = j + 1;
                                              editorOption.ChildName = childTemplateOption.Name;
                                              editorChildOptions = new List<object>();
  
                                              //loop each ini option of child
                                              for (; h < iniChildOptions.Count; h++)
                                              {
                                                  INIOption childOption = iniChildOptions[h];
                                                  if (k < iniOptions.Count - 1 && childOption.Index > iniOptions[k + 1].Index)
                                                      break;
  
                                                  if (childOption.Index > iniOptions[k].Index)
                                                      editorChildOptions.Add(ConvertToTemplate(childTemplateOption.Type, childOption.Value));
                                              }
                                          }
  
                                          //add entry
                                          editorOption.Values.Add(new EditorINIEntry(ConvertToTemplate(templateOption.Type, iniOptions[k].Value), editorChildOptions));
                                      }
                                  }
                                  else
                                  {
                                      //just add the first option if aviable to prevent multiple options which should be single
                                      if (iniBlock[templateOption.Name].Count > 0)
                                          editorOption.Values.Add(new EditorINIEntry(ConvertToTemplate(templateOption.Type, iniOptions[0].Value)));
                                  }
  
                                  //add option
                                  editorBlock.Options.Add(editorOption);
                              }
                              else
                                  //add empty option
                                  editorBlock.Options.Add(new EditorINIOption(templateOption.Name, j));
  
                              //set index of main option (value displayed in table view)
                              if (templateBlock.Identifier != null && templateBlock.Identifier.ToLower() == editorBlock.Options[editorBlock.Options.Count - 1].Name.ToLower())
                                  editorBlock.MainOptionIndex = editorBlock.Options.Count - 1;
  
                              //ignore next option because we already added it as children to the current option
                              if (childTemplateOption != null)
                                  j++;
                          }
  
                          //add block
                          editorData.Blocks.Add(editorBlock);
                      }
                  }
              }
  #if DEBUG
              st.Stop();
              System.Diagnostics.Debug.WriteLine("typecast data: " + st.ElapsedMilliseconds + "ms");
  #endif
  
              return editorData;
          }
  
          INIBlocks ReadINI()
          {
              IsBini = false;
              INIManager iniManager = new INIManager(File);
              return iniManager.Read();
          }
  
          public void Write(EditorINIData data)
          {
              //sort blocks first
              for (int i = 0; i < Helper.Template.Data.Files[data.TemplateIndex].Blocks.Count; i++)
              {
                  Template.Block templateBlock = Helper.Template.Data.Files[data.TemplateIndex].Blocks[i];
  
                  for (int j = i; j < data.Blocks.Count; j++)
                  {
                      if (data.Blocks[j].Name.ToLower() == templateBlock.Name.ToLower())
                      {
                          //swap blocks
                          EditorINIBlock temporaryBlock = data.Blocks[i];
                          data.Blocks[i] = data.Blocks[j];
                          data.Blocks[j] = temporaryBlock;
                          break;
                      }
                  }
              }
  
              //save data
              INIBlocks newData = new INIBlocks();
              foreach (EditorINIBlock block in data.Blocks)
              {
                  INIOptions newBlock = new INIOptions();
  
gt:                 for (int i = 0; i < block.Options.Count; i++)