using System;
using System.Collections.Generic;
using System.Diagnostics;



namespace Instrumental
{

  class InstrumentServerProcess
  {

    private EventLog destination;
    private Process  process;

    public InstrumentServerProcess(EventLog log){
      destination = log;
    }

    public void Run(string executablePath, string configPath, string hostname, bool scriptsEnabled, string scriptsDirectory){
      SetupProcess(executablePath, configPath, hostname, scriptsEnabled, scriptsDirectory);
    }

    public bool IsRunning(){
      if(process == null){
        return false;
      }
      try {
        return !Convert.ToBoolean(process?.HasExited);
      } catch(InvalidOperationException){
        return false;
      }
    }

    public void CleanupProcess(){
      if(IsRunning()){
        process.Kill();
        process.WaitForExit();
      }
      process?.Close();
      process = null;
    }

    public TimeSpan Age(){
      DateTime lastStarted = process?.StartTime ?? DateTime.Now;
      return DateTime.Now - lastStarted;
    }

    public string RubyDir(string basePath){
      return basePath + "\\lib\\ruby";
    }

    public string RubyLibDir(string basePath){
      return RubyDir(basePath) + "\\lib\\ruby";
    }

    public string AppDir(string basePath){
      return basePath + "\\lib\\app";
    }

    public string VendorDir(string basePath){
      return basePath + "\\lib\\vendor";
    }

    public string Gemfile(string basePath){
      return VendorDir(basePath) + "\\Gemfile";
    }

    public string RubyExecutable(string basePath){
      return RubyDir(basePath) + "\\bin.real\\ruby.exe";
    }

    public string InstrumentServerScript(string basePath){
      return AppDir(basePath) + "\\bin\\instrument_server";
    }

    public string RubyFlags(){
      return "-rbundler/setup";
    }

    public string SslCertFile(string basePath){
      return RubyDir(basePath) + "\\lib\\ca-bundle.crt";
    }

    public void SetupProcess(string executablePath, string configPath, string hostname, bool scriptsEnabled, string scriptsDirectory){
      CleanupProcess();
      string args                              = $"{RubyFlags()} \"{InstrumentServerScript(executablePath)}\" -f \"{configPath}\" -H \"{hostname}\"";
      if(scriptsEnabled){
        args += $" -e -s \"{scriptsDirectory}\"";
      }
      args += " foreground";

      string rubyVersion = "2.1.0";
      string rubyArch = "i386-mingw32";
      string rubyDir = RubyLibDir(executablePath);
      string[] libDirs = new string[]{
        $"{rubyDir}\\site_ruby\\{rubyVersion}",
        $"{rubyDir}\\site_ruby\\{rubyVersion}\\{rubyArch}",
        $"{rubyDir}\\site_ruby",
        $"{rubyDir}\\vendor_ruby\\{rubyVersion}",
        $"{rubyDir}\\vendor_ruby\\{rubyVersion}\\{rubyArch}",
        $"{rubyDir}\\vendor_ruby",
        $"{rubyDir}\\{rubyVersion}",
        $"{rubyDir}\\{rubyVersion}\\{rubyArch}"
      };

      Dictionary<string, string> env = new Dictionary<string, string>(){
        { "BUNDLE_GEMFILE",       Gemfile(executablePath) },
        { "RUBYLIB",              String.Join(";", libDirs) },
        { "SSL_CERT_FILE",        SslCertFile(executablePath) }
      };

      destination.WriteEntry($"Trying to start {RubyExecutable(executablePath)} {args} with env {String.Join(";", env)}", EventLogEntryType.Information);

      process                                  = new Process();
      process.StartInfo.FileName               = RubyExecutable(executablePath);
      process.StartInfo.Arguments              = args;

      foreach(KeyValuePair<string, string> entry in env){
        process.StartInfo.EnvironmentVariables[entry.Key] = entry.Value;
      }

      process.StartInfo.UseShellExecute        = false;
      process.StartInfo.RedirectStandardOutput = true;
      process.StartInfo.RedirectStandardError  = true;
      process.StartInfo.CreateNoWindow         = true;
      process.OutputDataReceived              += new DataReceivedEventHandler((sender, e) => {
          if(!String.IsNullOrEmpty(e.Data)){
            destination.WriteEntry(e.Data, EventLogEntryType.Information);
          }
        });
      process.ErrorDataReceived               += new DataReceivedEventHandler((sender, e) => {
          if(!String.IsNullOrEmpty(e.Data)){
            destination.WriteEntry(e.Data, EventLogEntryType.Error);
          }
        });
      if(!process.Start()){
        destination.WriteEntry("Failed to start process", EventLogEntryType.Error);
      } else {
        process.BeginOutputReadLine();
        process.BeginErrorReadLine();
      }
    }


  }
}