require 'timeout'
require 'oats/ossh'
# Returns all the matching IPs from the listed logs

class ApplicationLogs

  @@app_logs_error_getter = {} # webHostName => ApplicationLogs objects
  @@ip = nil
  @@plink_cmd_params = {} # webHostName => Extra parameters to connect or ''
  PLINK_TIMEOUT = 5

  # Call returns only errors occurring after the previous call.
  def ApplicationLogs.new_errors(initial = false)
    logs = $oats['env']['web']['logs']
    logs = nil if logs.nil? or logs.empty?
    return [] unless logs
    host = Oats.data 'env.web.host'
    if @@app_logs_error_getter[host]
      return [] if initial
    else
      @@app_logs_error_getter[host] = ApplicationLogs.new(host,logs)
    end
    return @@app_logs_error_getter[host].new_errors
  end

  def initialize(host,logs)
    ApplicationLogs.set_ip unless @@ip
    @logs = logs
    @log_error_count = []
    @command = "plink #{host} -l loguser sudo grep -i #{@@ip} " + @logs.join(' ')
    ApplicationLogs.set_plink_cmd_params
    @command_issue = @command
  end

  def new_errors
    $log.debug "Interrogating the application logs: #{@command_issue}"
    errors = IO.popen(@command).readlines
    total_errors = 0
    @log_error_count.each {|i| total_errors += i}
    new_error = []
    return new_error if total_errors == errors.length
    i = 0
    new_error_count = []
    new_error_count[0] = 0
    errors.each do |line|
      prefix = Regexp.new('^'+@logs[i]+':')
      unless prefix =~ line
        @log_error_count[i] = new_error_count[i]
        i += 1
        break if i == @logs.length
        new_error_count[i] = 0
        redo
      end
      new_error_count[i] += 1
      new_error << line if new_error_count[i] > (@log_error_count[i] ? @log_error_count[i] : 0)
    end
    @log_error_count[i] = new_error_count[i]
    return new_error
  end

  # Tails logs continuously.
  def ApplicationLogs.tail_errors
    tail_ip = $oats['execution']['tail_logs_ip']
    logs = $oats['env']['web']['logs']
    logs = nil if logs.nil? or logs.empty?
    host = $oats['env']['web']['host']
    if tail_ip
      raise(OatsBadInput, "The execution:tail_logs_ip is set to [#{tail_ip}] but env:web:logs is empty.") unless logs
    else
      return
    end
    if tail_ip and tail_ip.instance_of?(String)
      if /^\d\d\.\d\d*\.\d\d*\.\d\d*$/ =~ tail_ip
        @@ip = tail_ip
      else
        raise(OatsBadInput, "Input for execution:tail_logs_ip [#{tail_ip}] is not in proper IP format.")
      end
    else
      ApplicationLogs.set_ip unless @@ip
    end
    ApplicationLogs.set_plink_cmd_params(true)
    command = "plink #{host} -l loguser sudo tail -f " + logs.join(' ')
    command_issue = command
    puts "Filtering for [#{@@ip}] after executing command: #{command}"
    IO.popen(command_issue) do |io|
      while io.gets do
        puts $_ if Regexp.new(@@ip) =~ $_ or /^==>/ =~ $_
      end
    end
    exit 0 # Should never get here. User has to kill the process to quit
  end

  def ApplicationLogs.set_plink_cmd_params(from_tail = nil)
    host = $oats['env']['web']['host']
    return if @@plink_cmd_params[host]
    # Ossh does not support this option anymore. Must go in via Paegant
    # Clean-up the code below later.
    @@plink_cmd_params = ''
    error = 0
    error_msg = nil
    error_msg2 = nil
    correct_response = false
    cmd_issue = "plink #{host} -l loguser whoami 2>&1"
    cmd_display = cmd_issue
    $log.debug "Issuing: " + cmd_issue
    $log.debug "Please start Paegant with loguser key if there is no response in {PLINK_TIMEOUT} seconds."
    begin
      timeout(PLINK_TIMEOUT) do
        IO.popen(cmd_issue) do |ios|
          while ios.gets(':') do
            if /s password:/ =~ $_
              error = 1
              error_msg = "Received Plink authentication challenge from "
              error_msg2 = "If you have not started Paegant already, please do so."
            elsif /s password:/ =~ $_
              error = 2
              error_msg = "Received Plink authentication challenge from "
              error_msg2 = "If you have not defined an entry for [#{host} on Putty already, please do so."
            elsif /^Password:/ =~ $_
              error = 3
              error_msg = "Received 'sudo root' authentication challenge from "
              error_msg2 = "Sudo password cash has timed out. Please type the following on a shell commandline: #{cmd_display}"
            elsif /^Unable to open connection:/ =~ $_
              error = 4
              error_msg = "Could not connect to "
              error_msg2 = "You need to unset error log interrogation to run this test in this environment."
#            elsif /^root/ =~ $_
            elsif /^loguser/ =~ $_
              correct_response = true
            end
            $log.error $_ unless $_.nil? or correct_response
            ios.gets
            $log.error $_ unless $_.nil? or correct_response
          end
        end
      end
    rescue Timeout::Error
      $log.error "plink command timed out after [#{PLINK_TIMEOUT}] seconds."
    end
    if error == 0 and correct_response
      $log.info "Received proper response from Plink."
      return
    else
      if error_msg
        if error_msg2
          $log.error error_msg + "[#{host}]"
        else
          raise(OatsSetupError,error_msg)
        end
        raise(OatsSetupError,error_msg2)
      end
    end
  end

  def ApplicationLogs.set_ip
    IO.popen('ipconfig') do |io|
      while io.gets do
        @@ip = $_.chomp.sub(/.*IP Address.*: (\d.*\d).*/,'\1') if /IP Address.*: / =~ $_
      end
    end
  end

end
