/// <param name="forceServiceAddress">If != null, overwrites the service address, and do not not create the analysis package</param>
    public static int Main2(string[] args, IVerySimpleLineWriterWithEncoding lineWriter, out string[] clousotArgs, string forceServiceAddress = null)
    {
      Contract.Requires(lineWriter != null);

      int result = -1;
      var options = new ClousotServiceOptions(args, forceServiceAddress);

      clousotArgs = options.remainingArgs.ToArray();

      if (options.windowsService)
      {
        ClousotViaWindowsService.EnsureWindowsService();
      }

      var retryOptions = defaultRetryOptions.Clone();
      retryOptions.NumberOfRetries = options.serviceRetries;

      // Here we create the callback object, starting from the lineWriter provided by the caller
      var channelFactory = GetChannelFactoryForType(lineWriter.GetType());
      var channelAddress = String.IsNullOrWhiteSpace(options.serviceAddress)
        ? channelFactory.Endpoint.Address
        : new EndpointAddress(new Uri(options.serviceAddress), channelFactory.Endpoint.Address.Identity); // WCF wants us to provide the address for the factory but also for the instance...

      // We create the callback object
      var callbackInstanceContext = new InstanceContext(lineWriter);

      Func<IClousotService> GetAndOpenClousotService = () =>
        {
          // Let's create a channel with my callback and the address computed above
          var channel = channelFactory.CreateChannel(callbackInstanceContext, channelAddress);

          lineWriter.WriteLine(("Connecting to Cloudot at address " + channelAddress).PrefixWithCurrentTime());

          channel.Open(); // can throw an exception

          // Add an event that will be fired when the connection will enter a fault state.
          // Useful for debugging
          var channelAsDuplex = channel as IDuplexContextChannel;
          if (channelAsDuplex != null)
          {
            channelAsDuplex.Faulted +=
              (object sender, EventArgs e) =>
              {
                // This message is useful for debugging, so that we can see when the error happened
                Console.WriteLine("The connection to Cloudot entered a fault state!".PrefixWithCurrentTime());
              };
          }
          else
          {
            Contract.Assume(false, "We expect the service to be an instance of IDuplexContextChannel");
          }

          return channel;
        };

      IClousotService clousotService = null;
      try
      {
        // Step 0: We connect to the server 
        if (!Retry<ServerTooBusyException>.RetryOnException(GetAndOpenClousotService, retryOptions, out clousotService))
        {
          return -6666; // server too busy, could not connect
        }

        lineWriter.WriteLine("Connection with Cloudot established".PrefixWithCurrentTime());

        var clousotSpecificOptions = options.remainingArgs.ToArray();

        // Step 0.5: Are we simply requested to re-run an existing analysis? If this is the case just invoke cloudot
        if(forceServiceAddress != null)
        {
          result = clousotService.Main(args, lineWriter.Encoding.WebName);
          goto ok;
        }

        // Step 1: We pack all the files on the local machine and send them to the server
        FileTransfer.AnalysisPackageInfo packageInfo;
        var createThePackage = !options.useSharedDirs;
        if (FileTransfer.TryProcessPathsAndCreateAnalysisPackage(GetTmpDirectory, clousotSpecificOptions, createThePackage, out packageInfo))
        {
          clousotArgs = packageInfo.ExpandedClousotOptionsNormalized;

          // Step 2 -- First option: use shared dirs. We do not really want to use it, but it is interesting for debugging
          // We have all the command line options normalized to UNCs, and use them for the server
          if (!createThePackage)
          {
            if (packageInfo.ExpandedClousotOptionsNormalized != null)
            {

              // Step 2: We invoke the analysis on the server
              lineWriter.WriteLine("Invoking the analysis on cloudot using shared dirs".PrefixWithCurrentTime());
              lineWriter.WriteLine("Parameters: {0}".PrefixWithCurrentTime(), String.Join(" ", clousotArgs));

              result = clousotService.Main(clousotArgs, lineWriter.Encoding.WebName); // can throw an exception 

              goto ok;
            }
          }
          // Step 2 -- Second option: use a package
          // We have create a zip file with all the references. We copy it where the server tells us to.
          // Then, we invoke cloudot to perform the analysis
          else
          {
            var serverOut = clousotService.GetOutputDirectory();
            string fileOnTheServer;
            if (serverOut != null && FileTransfer.TrySendTheFile(packageInfo.PackageFileNameLocal, serverOut, out fileOnTheServer))
            {
              if (packageInfo.AssembliesToAnalyze != null && packageInfo.AssembliesToAnalyze.Length > 0)
              {
                packageInfo.PackageFileNameServer = fileOnTheServer;
                var name = Path.GetFileName(packageInfo.AssembliesToAnalyze[0]);
#if false
              // Just for debug
              string[] p;
              FileTransfer.TryRestoreAnalysisPackage(fileOnTheServer, Path.Combine(serverOut, "tmp"), out p);
#endif
                // Step 2: We invoke the analysis on the server
                lineWriter.WriteLine("Invoking the analysis on cloudot using analysis package (on server at {0})".PrefixWithCurrentTime(), fileOnTheServer);
                result = clousotService.AnalyzeUsingAnalysisPackage(name, fileOnTheServer, lineWriter.Encoding.WebName);

                goto ok;
              }
              else
              {
                lineWriter.WriteLine("Cannot figure out the assembly to analyze. We abort.");
              }
            }
          }
        }
        return -1;

      ok:
        // Step * : Close the service
        // We moved the Close action here, as it may be the case that the background thread is still writing something on the callback, while we close it
        // I do not really know how to see if this is the case, but it appears when we output a lot of information via a remote server
        // From what I saw on the internet, for similar cases, it seems the best solution is to use the Try-Catch-Abort pattern
        // Waiting also helps, but how much is a problem.
        clousotService.Close(TimeToWaitBeforeClosing);

        return result;
      }
      // We use the Try-Catch-Abort pattern for WCF
      catch (ActionNotSupportedException actionNotSupported)
      {
        Console.WriteLine("Something is wrong with the client/server contract");
        Console.WriteLine("This is the exception {0}", actionNotSupported);
        return -1;
      }
      catch (CommunicationException)
      {
        if (clousotService != null)
        {
          var channelAsDuplex = clousotService as IDuplexContextChannel;
          if (channelAsDuplex != null)
          {
            if (channelAsDuplex.State == CommunicationState.Faulted)
            {
              Console.WriteLine("The communication is a faulted state. Is Cloudot down?");
            }
          }
          (clousotService as IDuplexContextChannel).Abort();
        }
        else
        {
          Console.WriteLine("Something has gone very wrong. I did not expected clousotService to be null here");
        }
        return -1;

      }
      catch (TimeoutException)
      {
        Console.WriteLine("Hit a timeout in the communication with Cloudot");
        (clousotService as IDuplexContextChannel).Abort();

        return -1;
      }
      catch (Exception e)
      {
        var errMsg = e.Message + (e.InnerException != null ? string.Format("{0} -- {1}", Environment.NewLine, e.InnerException.Message) : "");
        Console.WriteLine(("Analysis terminated because of an error: " + errMsg).PrefixWithCurrentTime());

        return -1;
      }
      finally
      {
        if (clousotService != null)
        {
          clousotService.TryDispose();
        }
      }
    }
        /// <param name="forceServiceAddress">If != null, overwrites the service address, and do not not create the analysis package</param>
        public static int Main2(string[] args, IVerySimpleLineWriterWithEncoding lineWriter, out string[] clousotArgs, string forceServiceAddress = null)
        {
            Contract.Requires(lineWriter != null);

            int result  = -1;
            var options = new ClousotServiceOptions(args, forceServiceAddress);

            clousotArgs = options.remainingArgs.ToArray();

            if (options.windowsService)
            {
                ClousotViaWindowsService.EnsureWindowsService();
            }

            var retryOptions = defaultRetryOptions.Clone();

            retryOptions.NumberOfRetries = options.serviceRetries;

            // Here we create the callback object, starting from the lineWriter provided by the caller
            var channelFactory = GetChannelFactoryForType(lineWriter.GetType());
            var channelAddress = String.IsNullOrWhiteSpace(options.serviceAddress)
        ? channelFactory.Endpoint.Address
        : new EndpointAddress(new Uri(options.serviceAddress), channelFactory.Endpoint.Address.Identity); // WCF wants us to provide the address for the factory but also for the instance...

            // We create the callback object
            var callbackInstanceContext = new InstanceContext(lineWriter);

            Func <IClousotService> GetAndOpenClousotService = () =>
            {
                // Let's create a channel with my callback and the address computed above
                var channel = channelFactory.CreateChannel(callbackInstanceContext, channelAddress);

                lineWriter.WriteLine(("Connecting to Cloudot at address " + channelAddress).PrefixWithCurrentTime());

                channel.Open(); // can throw an exception

                // Add an event that will be fired when the connection will enter a fault state.
                // Useful for debugging
                var channelAsDuplex = channel as IDuplexContextChannel;
                if (channelAsDuplex != null)
                {
                    channelAsDuplex.Faulted +=
                        (object sender, EventArgs e) =>
                    {
                        // This message is useful for debugging, so that we can see when the error happened
                        Console.WriteLine("The connection to Cloudot entered a fault state!".PrefixWithCurrentTime());
                    };
                }
                else
                {
                    Contract.Assume(false, "We expect the service to be an instance of IDuplexContextChannel");
                }

                return(channel);
            };

            IClousotService clousotService = null;

            try
            {
                // Step 0: We connect to the server
                if (!Retry <ServerTooBusyException> .RetryOnException(GetAndOpenClousotService, retryOptions, out clousotService))
                {
                    return(-6666); // server too busy, could not connect
                }

                lineWriter.WriteLine("Connection with Cloudot established".PrefixWithCurrentTime());

                var clousotSpecificOptions = options.remainingArgs.ToArray();

                // Step 0.5: Are we simply requested to re-run an existing analysis? If this is the case just invoke cloudot
                if (forceServiceAddress != null)
                {
                    result = clousotService.Main(args, lineWriter.Encoding.WebName);
                    goto ok;
                }

                // Step 1: We pack all the files on the local machine and send them to the server
                FileTransfer.AnalysisPackageInfo packageInfo;
                var createThePackage = !options.useSharedDirs;
                if (FileTransfer.TryProcessPathsAndCreateAnalysisPackage(GetTmpDirectory, clousotSpecificOptions, createThePackage, out packageInfo))
                {
                    clousotArgs = packageInfo.ExpandedClousotOptionsNormalized;

                    // Step 2 -- First option: use shared dirs. We do not really want to use it, but it is interesting for debugging
                    // We have all the command line options normalized to UNCs, and use them for the server
                    if (!createThePackage)
                    {
                        if (packageInfo.ExpandedClousotOptionsNormalized != null)
                        {
                            // Step 2: We invoke the analysis on the server
                            lineWriter.WriteLine("Invoking the analysis on cloudot using shared dirs".PrefixWithCurrentTime());
                            lineWriter.WriteLine("Parameters: {0}".PrefixWithCurrentTime(), String.Join(" ", clousotArgs));

                            result = clousotService.Main(clousotArgs, lineWriter.Encoding.WebName); // can throw an exception

                            goto ok;
                        }
                    }
                    // Step 2 -- Second option: use a package
                    // We have create a zip file with all the references. We copy it where the server tells us to.
                    // Then, we invoke cloudot to perform the analysis
                    else
                    {
                        var    serverOut = clousotService.GetOutputDirectory();
                        string fileOnTheServer;
                        if (serverOut != null && FileTransfer.TrySendTheFile(packageInfo.PackageFileNameLocal, serverOut, out fileOnTheServer))
                        {
                            if (packageInfo.AssembliesToAnalyze != null && packageInfo.AssembliesToAnalyze.Length > 0)
                            {
                                packageInfo.PackageFileNameServer = fileOnTheServer;
                                var name = Path.GetFileName(packageInfo.AssembliesToAnalyze[0]);
#if false
                                // Just for debug
                                string[] p;
                                FileTransfer.TryRestoreAnalysisPackage(fileOnTheServer, Path.Combine(serverOut, "tmp"), out p);
#endif
                                // Step 2: We invoke the analysis on the server
                                lineWriter.WriteLine("Invoking the analysis on cloudot using analysis package (on server at {0})".PrefixWithCurrentTime(), fileOnTheServer);
                                result = clousotService.AnalyzeUsingAnalysisPackage(name, fileOnTheServer, lineWriter.Encoding.WebName);

                                goto ok;
                            }
                            else
                            {
                                lineWriter.WriteLine("Cannot figure out the assembly to analyze. We abort.");
                            }
                        }
                    }
                }
                return(-1);

ok:
                // Step * : Close the service
                // We moved the Close action here, as it may be the case that the background thread is still writing something on the callback, while we close it
                // I do not really know how to see if this is the case, but it appears when we output a lot of information via a remote server
                // From what I saw on the internet, for similar cases, it seems the best solution is to use the Try-Catch-Abort pattern
                // Waiting also helps, but how much is a problem.
                clousotService.Close(TimeToWaitBeforeClosing);

                return(result);
            }
            // We use the Try-Catch-Abort pattern for WCF
            catch (ActionNotSupportedException actionNotSupported)
            {
                Console.WriteLine("Something is wrong with the client/server contract");
                Console.WriteLine("This is the exception {0}", actionNotSupported);
                return(-1);
            }
            catch (CommunicationException)
            {
                if (clousotService != null)
                {
                    var channelAsDuplex = clousotService as IDuplexContextChannel;
                    if (channelAsDuplex != null)
                    {
                        if (channelAsDuplex.State == CommunicationState.Faulted)
                        {
                            Console.WriteLine("The communication is a faulted state. Is Cloudot down?");
                        }
                    }
                    (clousotService as IDuplexContextChannel).Abort();
                }
                else
                {
                    Console.WriteLine("Something has gone very wrong. I did not expected clousotService to be null here");
                }
                return(-1);
            }
            catch (TimeoutException)
            {
                Console.WriteLine("Hit a timeout in the communication with Cloudot");
                (clousotService as IDuplexContextChannel).Abort();

                return(-1);
            }
            catch (Exception e)
            {
                var errMsg = e.Message + (e.InnerException != null ? string.Format("{0} -- {1}", Environment.NewLine, e.InnerException.Message) : "");
                Console.WriteLine(("Analysis terminated because of an error: " + errMsg).PrefixWithCurrentTime());

                return(-1);
            }
            finally
            {
                if (clousotService != null)
                {
                    clousotService.TryDispose();
                }
            }
        }