//
    // You can use the following additional attributes as you write your tests:
    //
    // Use ClassInitialize to run code before running the first test in the class
    // [ClassInitialize()]
    // public static void MyClassInitialize(TestContext testContext) { }
    //
    // Use ClassCleanup to run code after all tests in a class have run
    // [ClassCleanup()]
    // public static void MyClassCleanup() { }
    //
    // Use TestInitialize to run code before running each test 
    // [TestInitialize()]
    // public void MyTestInitialize() { }
    //
    // Use TestCleanup to run code after each test has run
    // [TestCleanup()]
    // public void MyTestCleanup() { }
    //
    #endregion

    private string ExecuteTest(string assemblyName, HeapFactory heapFactory) {
      var options = new Options();
      options.monotonicHeap = true;
      options.dereference = Options.Dereference.Assume;
      BCT.TranslateAssemblyAndWriteOutput(new List<string> { assemblyName }, heapFactory, options, null, false);
      var fileName = Path.ChangeExtension(assemblyName, "bpl");
      var s = File.ReadAllText(fileName);
      return s;
    }
    public static Bpl.Program/*?*/ TranslateAssembly(List<string> assemblyNames, HeapFactory heapFactory, Options options, List<Regex> exemptionList, bool whiteList) {
      Contract.Requires(assemblyNames != null);
      Contract.Requires(heapFactory != null);

      var libPaths = options.libpaths;
      var wholeProgram = options.wholeProgram;
      var/*?*/ stubAssemblies = options.stub;
      var phoneControlsConfigFile = options.phoneControls;
      var doPhoneNav = options.phoneNavigationCode;
      var doPhoneFeedback = options.phoneFeedbackCode;

      var host = new CodeContractAwareHostEnvironment(libPaths != null ? libPaths : Enumerable<string>.Empty, true, true);
      Host = host;

      Bpl.CommandLineOptions.Install(new Bpl.CommandLineOptions());

      #region Assemlies to translate (via cmd line)
      modules = new List<IModule>();
      var contractExtractors = new Dictionary<IUnit, IContractProvider>();
      var pdbReaders = new Dictionary<IUnit, PdbReader>();
      #region Load *all* of the assemblies before doing anything else so that they can all vote on unification matters
      foreach (var a in assemblyNames) {
        var module = host.LoadUnitFrom(a) as IModule;
        if (module == null || module == Dummy.Module || module == Dummy.Assembly) {
          Console.WriteLine(a + " is not a PE file containing a CLR module or assembly, or an error occurred when loading it.");
          Console.WriteLine("Skipping it, continuing with other input assemblies");
          continue;
        }
        modules.Add(module);
      }
      #endregion
      #region Decompile all of the assemblies
      var decompiledModules = new List<IModule>();
      foreach (var m in modules) {
        PdbReader/*?*/ pdbReader = null;
        string pdbFile = Path.ChangeExtension(m.Location, "pdb");
        if (File.Exists(pdbFile)) {
          Stream pdbStream = File.OpenRead(pdbFile);
          pdbReader = new PdbReader(pdbStream, host);
        }
        var m2 = Decompiler.GetCodeModelFromMetadataModel(host, m, pdbReader, DecompilerOptions.Unstack) as IModule;
        // The decompiler does not turn calls to Assert/Assume into Code Model nodes
        m2 = new Microsoft.Cci.MutableContracts.ContractExtractor.AssertAssumeExtractor(host, pdbReader).Rewrite(m2);
        decompiledModules.Add(m2);
        host.RegisterAsLatest(m2);
        contractExtractors.Add(m2, host.GetContractExtractor(m2.UnitIdentity));
        pdbReaders.Add(m2, pdbReader);
      }
      modules = decompiledModules;
      #endregion
      #endregion

      #region Assemblies to translate (stubs)
      if (stubAssemblies != null) {
        foreach (var s in stubAssemblies) {
          var module = host.LoadUnitFrom(s) as IModule;
          if (module == null || module == Dummy.Module || module == Dummy.Assembly) {
            Console.WriteLine(s + " is not a PE file containing a CLR module or assembly, or an error occurred when loading it.");
            Console.WriteLine("Skipping it, continuing with other input assemblies");
          }
          PdbReader/*?*/ pdbReader = null;
          string pdbFile = Path.ChangeExtension(module.Location, "pdb");
          if (File.Exists(pdbFile)) {
            Stream pdbStream = File.OpenRead(pdbFile);
            pdbReader = new PdbReader(pdbStream, host);
          }
          module = Decompiler.GetCodeModelFromMetadataModel(host, module, pdbReader, DecompilerOptions.Unstack) as IModule;

          var copier = new CodeDeepCopier(host);
          var mutableModule = copier.Copy(module);

          var mscorlib = TypeHelper.GetDefiningUnit(host.PlatformType.SystemObject.ResolvedType);

          //var mutator = new ReparentModule(host, mscorlib, mutableModule);
          //module = mutator.Rewrite(mutableModule);
          //modules.Add(Tuple.Create(module, pdbReader));

          RewriteUnitReferences renamer = new RewriteUnitReferences(host, mutableModule);
          var mscorlibAssembly = (IAssembly)mscorlib;
          renamer.targetAssembly = mscorlibAssembly;
          renamer.originalAssemblyIdentity = mscorlibAssembly.AssemblyIdentity;
          renamer.RewriteChildren(mutableModule);
          modules.Add((IModule)mutableModule);
          contractExtractors.Add(module, host.GetContractExtractor(module.UnitIdentity));
          pdbReaders.Add(module, pdbReader);
        }
      }
      #endregion

      if (modules.Count == 0) {
        throw new TranslationException("No input assemblies to translate.");
      }

      var primaryModule = modules[0];
      Sink sink= new Sink(host, heapFactory, options, exemptionList, whiteList);
      TranslationHelper.tmpVarCounter = 0;

      // TODO move away, get all plugin and translators from a config file or alike
      #region Plugged translators
      List<Translator> translatorsPlugged = new List<Translator>();
      ITranslationPlugin bctPlugin= new BytecodeTranslatorPlugin(wholeProgram);
      Translator bcTranslator = bctPlugin.getTranslator(sink, contractExtractors, pdbReaders);
      translatorsPlugged.Add(bcTranslator);

      if (phoneControlsConfigFile != null && phoneControlsConfigFile != "") {
        // TODO this should be part of the translator initialziation
        PhoneCodeHelper.initialize(host);
        PhoneCodeHelper.instance().PhonePlugin = new PhoneControlsPlugin(phoneControlsConfigFile);

        if (doPhoneNav) {
          // TODO this should be part of the translator initialziation
          PhoneCodeHelper.instance().PhoneNavigationToggled = true;

          ITranslationPlugin phoneInitPlugin = new PhoneInitializationPlugin();
          ITranslationPlugin phoneNavPlugin = new PhoneNavigationPlugin();
          Translator phInitTranslator = phoneInitPlugin.getTranslator(sink, contractExtractors, pdbReaders);
          Translator phNavTranslator = phoneNavPlugin.getTranslator(sink, contractExtractors, pdbReaders);
          translatorsPlugged.Add(phInitTranslator);
          translatorsPlugged.Add(phNavTranslator);
        }

        if (doPhoneFeedback) {
          // TODO this should be part of the translator initialziation
          PhoneCodeHelper.instance().PhoneFeedbackToggled = true;

          ITranslationPlugin phoneFeedbackPlugin = new PhoneFeedbackPlugin();
          Translator phFeedbackTranslator = phoneFeedbackPlugin.getTranslator(sink, contractExtractors, pdbReaders);
          translatorsPlugged.Add(phFeedbackTranslator);
        }
      }
      #endregion
      sink.TranslationPlugins = translatorsPlugged;

      /*
      if (phoneControlsConfigFile != null && phoneControlsConfigFile != "") {
        // TODO send this all way to initialization of phone plugin translator
        PhoneCodeHelper.initialize(host);
        PhoneCodeHelper.instance().PhonePlugin = new PhoneControlsPlugin(phoneControlsConfigFile);
        
        // TODO these parameters will eventually form part of plugin configuration
        if (doPhoneNav) {
          PhoneCodeHelper.instance().PhoneNavigationToggled = true;
          PhoneInitializationMetadataTraverser initTr = new PhoneInitializationMetadataTraverser(host);
          initTr.InjectPhoneCodeAssemblies(modules);
          PhoneNavigationMetadataTraverser navTr = new PhoneNavigationMetadataTraverser(host);
          navTr.InjectPhoneCodeAssemblies(modules);
        }

        if (doPhoneFeedback) {
          PhoneCodeHelper.instance().PhoneFeedbackToggled = true;
          PhoneControlFeedbackMetadataTraverser fbMetaDataTraverser= new PhoneControlFeedbackMetadataTraverser(host);
          fbMetaDataTraverser.Visit(modules);
        }
      }
      */

      // TODO replace the whole translation by a translator initialization and an orchestrator calling back for each element
      // TODO for the current BC translator it will possibly just implement onMetadataElement(IModule)
      // TODO refactor this away, handle priorities between plugged translators
      IOrderedEnumerable<Translator> prioritizedTranslators = translatorsPlugged.OrderBy(t => t.getPriority());
      foreach (Translator t in prioritizedTranslators) {
        t.initialize();
        if (t.isOneShot())
          t.TranslateAssemblies(modules);
      }

      foreach (var pair in sink.delegateTypeToDelegates.Values) {
        CreateDispatchMethod(sink, pair.Item1, pair.Item2);
        CreateDelegateCreateMethod(sink, pair.Item1, pair.Item2);
        CreateDelegateAddMethod(sink, pair.Item1, pair.Item2);
        CreateDelegateRemoveMethod(sink, pair.Item1, pair.Item2);
      }

      // Subtyping for extern types
      if(sink.Options.typeInfo > 0) sink.DeclareExternTypeSubtyping();

      string outputFileName = primaryModule.Name + ".bpl";
      callPostTranslationTraversers(modules, sink, phoneControlsConfigFile, outputFileName);
      if (PhoneCodeHelper.instance().PhoneNavigationToggled) {
        finalizeNavigationAnalysisAndBoogieCode(phoneControlsConfigFile, sink, outputFileName);
      }

      //sink.CreateIdentifierCorrespondenceTable(primaryModule.Name.Value);

      //var rc = new Bpl.ResolutionContext((Bpl.IErrorSink)null);
      //foreach (var decl in sink.TranslatedProgram.TopLevelDeclarations) {
      //  decl.Register(rc);
      //}
      //sink.TranslatedProgram.Resolve(rc);
      //var goodDecls = new List<Bpl.Declaration>();
      //var tc = new Bpl.TypecheckingContext(null);
      //foreach (var decl in sink.TranslatedProgram.TopLevelDeclarations) {
      //  var impl = decl as Bpl.Implementation;
      //  if (impl == null) {
      //    goodDecls.Add(decl);
      //    continue;
      //  }
      //  try {
      //    //var tc = new Bpl.TypecheckingContext(null);
      //    impl.Typecheck(tc);
      //    goodDecls.Add(impl);
      //  } catch {
      //    Console.WriteLine("Deleting implementation for: " + impl.Name);
      //    // nothing to do, just continue
      //  }
      //}
      //sink.TranslatedProgram.TopLevelDeclarations = goodDecls;
      return sink.TranslatedProgram;
    }
    static int Main(string[] args)
    {
      int errorReturnValue = -1;

      #region Parse options and check for errors
      var options = new Options();
      options.Parse(args);
      if (options.HelpRequested) {
        options.PrintOptions("");
        return errorReturnValue;
      }
      if (options.HasErrors) {
        options.PrintErrorsAndExit(Console.Out);
      }
      if (!String.IsNullOrWhiteSpace(options.exemptionFile)) {
        string fileName = options.exemptionFile;
        var c = fileName[fileName.Length - 1];
        if (c == '+' || c == '-') fileName = options.exemptionFile.Remove(fileName.Length - 1);
        if (!File.Exists(fileName)) {
          Console.WriteLine("Specified exemption file '{0}' not found.", fileName);
        }
      }
      if (options.stub != null) {
        Console.WriteLine("/s is no longer used to specify stub assemblies");
        return errorReturnValue;
      }

      if (options.modelExceptions == 1 && !options.wholeProgram) {
        Console.WriteLine("can specify a precise modeling of exceptions only when doing whole program analysis");
        return errorReturnValue;
      }

      if (options.breakIntoDebugger) {
        System.Diagnostics.Debugger.Launch();
      }

      #endregion

      var assemblyNames = options.assemblies;
      if (assemblyNames == null || assemblyNames.Count == 0) {
        assemblyNames = new List<string>();
        foreach (var g in options.GeneralArguments) {
          assemblyNames.Add(g);
        }
      }

      #region If an exclusion file has been specified, read in each line as a regular expression
      List<Regex> exemptionList = null;
      bool whiteList = false;
      if (!String.IsNullOrWhiteSpace(options.exemptionFile)) {
        int i = 0;
        exemptionList = new List<Regex>();
        string fileName = options.exemptionFile;
        var c = fileName[fileName.Length - 1];
        whiteList = true;
        if (c == '+' || c == '-') {
          fileName = options.exemptionFile.Remove(fileName.Length - 1);
          if (c == '-') whiteList = false;
        }
        try {
          // Create an instance of StreamReader to read from a file.
          // The using statement also closes the StreamReader.
          using (StreamReader sr = new StreamReader(fileName)) {
            String line;
            // Read and display lines from the file until the end of 
            // the file is reached.
            while ((line = sr.ReadLine()) != null) {
              exemptionList.Add(new Regex(line));
              i++;
            }
            //Console.WriteLine("Read {0} lines from the exclusion file '{1}'.",
            //  i, options.exemptionFile);
          }
        } catch (Exception e) {
          Console.WriteLine("Something went wrong reading the exclusion file '{0}'; read in {1} lines, continuing processing.",
            fileName, i);
          Console.WriteLine(e.Message);
        }
      }
      #endregion

      try {
        HeapFactory heap;
        switch (options.heapRepresentation) {
          case Options.HeapRepresentation.splitFields:
            heap = new SplitFieldsHeap();
            break;
          case Options.HeapRepresentation.general:
            heap = new GeneralHeap();
            break;
          default:
            Console.WriteLine("Unknown setting for /heap");
            return 1;
        }

        if ((options.phoneFeedbackCode || options.phoneNavigationCode) && (options.phoneControls == null || options.phoneControls == "")) {
          Console.WriteLine("Options /phoneNavigationCode and /phoneFeedbackCode need /phoneControls option set.");
          return 1;
        }

        var pgm = TranslateAssembly(assemblyNames, heap, options, exemptionList, whiteList);
        var fileName = assemblyNames[0];
        fileName = Path.GetFileNameWithoutExtension(fileName);
        string outputFileName = fileName + ".bpl";
        // Microsoft.Boogie.TokenTextWriter writer = new Microsoft.Boogie.TokenTextWriter("_" + outputFileName);
        Microsoft.Boogie.TokenTextWriter writer = new Microsoft.Boogie.TokenTextWriter(outputFileName);
        Prelude.Emit(writer);
        pgm.Emit(writer);
        writer.Close();
        return 0;
        // return Inline(outputFileName);
      } catch (Exception e) { // swallow everything and just return an error code
        Console.WriteLine("The byte-code translator failed: {0}", e.Message);
        // Console.WriteLine("Stack trace: {0}", e.StackTrace);
        return -1;
      }
    }
 public static int TranslateAssemblyAndWriteOutput(List<string> assemblyNames, HeapFactory heapFactory, Options options, List<Regex> exemptionList, bool whiteList) {
   Contract.Requires(assemblyNames != null);
   Contract.Requires(heapFactory != null);
   try {
     var pgm = TranslateAssembly(assemblyNames, heapFactory, options, exemptionList, whiteList);
     var fileName = assemblyNames[0];
     fileName = Path.GetFileNameWithoutExtension(fileName);
     string outputFileName = fileName + ".bpl";
     using (var writer = new Microsoft.Boogie.TokenTextWriter(outputFileName)) {
       Prelude.Emit(writer);
       pgm.Emit(writer);
       writer.Close();
     }
     return 0; // success
   } catch (Exception e) { // swallow everything and just return an error code
     Console.WriteLine("The byte-code translator failed: {0}", e.Message);
     // Console.WriteLine("Stack trace: {0}", e.StackTrace);
     return -1;
   }
 }