require 'json'
require 'open3'
require 'stringio'
require 'logger'
require 'canzea/core/audit'


def time_diff_milli(start, finish)
   (finish - start) * 1000.0
end

def handleIO(stillOpen, ioArray, io, log)
  if ioArray.include?(io)
    begin
      log.write(io.readpartial(4096))
    rescue EOFError
      stillOpen.delete_if{|s| s == io}
    end
  end
end

class ManagedError < StandardError
end

class Worker
    @test = false

    def initialize ()
        @log = Logger.new(Canzea::config[:logging_root] + '/plans.log')
    end

    def test (t)
        @test = t
    end

    def find(command, list = [])
        audit = Audit.new false

        @log.info("     FIND: " + command)

        File.foreach(command) { |l|
            if ( l.start_with?('## ') )
                @log.info "#{lines + 1} Label: " + l
            elsif ( l.start_with?('#') )
            elsif ( /\S/ !~ l )
            elsif ( l.chomp.end_with?(".sh") && !l.chomp.end_with?(".atomic.sh") && !l.include?("cp ") && !l.include?("chmod") )
                @log.info("     [#{ref}] RECURSE: " + l)

                workingDir = "#{Pathname.new(Canzea::config[:catalog_location]).realpath}"

                Dir.chdir(workingDir) {

                    newCommand = "#{Pathname.new(l.chomp).realpath}"

                    lines = find newCommand, list
                }
            else
                list.push l
            end
        }
        return list
    end

    def run(command, start, lines, statusIndicator = false, ref = "" )
        audit = Audit.new false

        @log.info("     [#{ref}] HANDLING: " + command)

        File.foreach(command) { |l|
            if ( l.start_with?('## ') )
                @log.info "#{lines + 1} Label: " + l
            elsif ( l.start_with?('#') )
            elsif ( /\S/ !~ l )
            elsif ( l.chomp.end_with?(".sh") && !l.chomp.end_with?(".atomic.sh") && !l.include?("cp ") && !l.include?("chmod") )
                @log.info("     [#{ref}] RECURSE: " + l)

                workingDir = "#{Pathname.new(Canzea::config[:catalog_location]).realpath}"

                Dir.chdir(workingDir){

                    newCommand = "#{Pathname.new(l.chomp).realpath}"

                    lines = run newCommand, start, lines
                }
            else

                if ( (lines + 1) < start )
                    @log.info("     [#{ref}] Skipping : #{lines + 1} #{l}")
                    lines += 1
                    next
                end


                audit.start( "#{lines + 1 }", l.chomp)

                @log.info("     [#{ref}] #{(lines + 1).to_s.rjust(2, "0")} : " + l)

                if @test == false
                    t1 = Time.now

                    begin
                        workingDir = "#{Pathname.new(Canzea::config[:catalog_location]).realpath}"

                        puts "-- Executing [#{workingDir}] #{l}"
                        Dir.chdir(workingDir){
                            Open3.popen3(l) {|stdin, stdout, stderr, wait_thr|
                              pid = wait_thr.pid # pid of the started process.

                              log_w = StringIO.new

                              stillOpen = [stdout, stderr]
                              while !stillOpen.empty?
                                fhs = select(stillOpen, nil, nil, nil)
                                handleIO(stillOpen, fhs[0], stdout, log_w)
                                handleIO(stillOpen, fhs[0], stderr, log_w)
                              end

                              exit_status = wait_thr.value # wait for it to finish

                              log_w.close_write

                              output = log_w.string

                              begin
                                  output.split(/\n/).each do | line |
                                      puts "-- #{line}"
                                  end
                              rescue => exception
                                  puts "-- WARNING : Unable to parse output, exception: #{exception.to_s}"
                              end

                              puts "-- Exit Status = #{exit_status}"

                              @log.info("STDOUT #{output}")

                              @log.info("Exit Status = #{exit_status}")

                              # if exit status is failure then exit right away

                              t2 = Time.now

                              msecs = time_diff_milli t1, t2

                              audit.complete("#{lines + 1}", l.chomp, exit_status.exitstatus, msecs, output)

                              if (statusIndicator)
                                  audit.status("#{lines + 1}", l.chomp, exit_status.exitstatus, msecs, output)
                              end

                              if exit_status.exitstatus != 0
                                abort()
                              end
                            }
                        }
                    rescue => exception
                        msecs = time_diff_milli t1, Time.now
                        audit.complete("#{lines + 1}", l.chomp, "-1", msecs, exception.to_s)
                        raise
                    end
                else
                    puts "-- TEST [#{lines + 1}] FOUND: " + l
                end

            end

            lines += 1
        }
        return lines

    end
end