#!/usr/bin/ruby.ruby3.4 

require 'rubygems'
require 'pathname'

require 'envjs/options'

require 'johnson/tracemonkey'
require 'johnson/cli'
require 'envjs/runtime'

RUNTIME = js = Johnson::Runtime.new
RUNTIME.extend Envjs::Runtime

# RUNTIME.load Envjs::ENVJS

EXIT_VERBS = [nil] + %w(exit quit)
  
prepped = false
ruby_readline = []
js_readline = []
local_binding = binding
mode = nil

(class<<self; self; end).send :define_method, :prep do

  return if prepped
  prepped = true

  require "readline"
  require "johnson/cli"

  RUNTIME.evaluate(Johnson::CLI::JS)

  def copy_history
    new_history = []
    until Readline::HISTORY.empty?
      new_history.push(Readline::HISTORY.pop)
    end
    new_history
  end

  def paste_history(old_history)
    until old_history.empty?
      Readline::HISTORY << old_history.pop
    end
  end

  def handle_exit(input)
    return EXIT_VERBS.include?(input)
  end

  def rescued(&block)
    yield
  rescue Exception => e
    exit if SystemExit === e

    puts e.message
    puts e.backtrace.reject { |l| l =~ /bin\/johnson/ }
  end

  def eval_in_js(expression,stream)
    rescued do
      v = RUNTIME.evaluate(expression, "(console)")
      stream.result { "=> " + v.inspect }
  end
  end

  def eval_in_ruby(expression, bind_to, stream)
    rescued do
      v = eval(expression, bind_to)
      stream.result { "=> " + v.inspect }
    end
  end
  
  (class<<self; self; end).send :define_method, :do_loop do |stream|
  # def foo

    catch "Breakout" do
      loop do
        mode = :js
        input = stream.next("js> ")
        input and input.chomp!
        begin stream.exit(input); throw "Breakout" end if handle_exit(input)
        
        if input =~ /^rb\s+(.+)$/
          eval_in_ruby($1, local_binding, stream)
          next
        end
        
        if input == "rb"
          js_readline = copy_history
          paste_history(ruby_readline)
          
          loop do
            mode = :rb
            # input = Readline.readline("rb> ", true)
            input = stream.next("rb> ")
            begin stream.exit(input); throw "Breakout" end if handle_exit(input)

            break if input == "js"

            if input =~ /^js\s+(.+)$/
              eval_in_js($1)
              next
            end

            eval_in_ruby(input, local_binding, stream)
          end
          
          ruby_readline = copy_history
          paste_history(js_readline)
          next
        end
        
        eval_in_js(input, stream)
      end
    end
  end
end

if ARGV.length > 0

  begin
    about_blank = true
    ARGV.each do |file|

      uri = URI.parse file

      if uri.scheme == nil
        uri.scheme = "file"
        begin
          uri.path = Pathname.new(uri.path).realpath.to_s
        rescue Errno::ENOENT; end
        uri = URI.parse uri.to_s
      end

      uri_s = uri.to_s.sub %r(^file:/([^/])), 'file:///\1'

      if uri.scheme == "about" and uri.opaque == "blank"
        # $stderr.puts "wait before set about"
        RUNTIME.wait
        RUNTIME.evaluate("window.location = 'about:blank'", "(comand line)", 1)
        about_blank = true
      elsif uri.path =~ /\.x?html?$/ || uri.scheme =~ %r(^https?)
        # $stderr.puts "wait before set real" if RUNTIME.evaluate("window.location.href") != "about:blank"
        RUNTIME.wait if RUNTIME.evaluate("window.location.href") != "about:blank"
        # RUNTIME.evaluate("window.location = 'file://#{Pathname.new(file).realpath}'", file, 1)
        RUNTIME.evaluate("window.location = '#{uri_s}'", file, 1)
        about_blank = false
      else
        RUNTIME.top_level_load uri_s
        RUNTIME.load uri_s
        RUNTIME.top_level_load nil
      end
    end
  rescue Johnson::Error => je
    if je.message.match /<SystemExit: exit>/
      exit
    end
    raise je
  end

  RUNTIME.wait

  if !$wake_info.empty?
    puts "<wake><![CDATA["
    puts $wake_info.join("\n")
    puts "]]></wake>"
    $stdout.flush
  end


else

  prep

  if !(files = Dir[".envjsrbrc",File.join(ENV["HOME"],".envjsrbrc")]).empty?
    file = Class.new do
      def initialize f
        @file = open f
      end
      def next prompt
        @file.gets
      end
      def result &block
      end
      def exit v; end
    end.new files[0]

    do_loop file
  end

  options = Johnson::CLI::Options.parse!(ARGV)

  options.load_paths.each { |d| $LOAD_PATH << d }
  options.paths_to_require.each { |p| RUNTIME.evaluate("Johnson.require('#{p}')") }
  options.files_to_preload.each { |f| RUNTIME.load(f) }

  unless options.expressions.empty?
    options.expressions.each { |e| RUNTIME.evaluate(e, '-e') }
    exit if options.files_to_evaluate.empty?
  end

  unless options.files_to_evaluate.empty?
    RUNTIME[:arguments] = options.arguments
    
    options.files_to_evaluate.each do |file|
      RUNTIME.load(file)
    end
    
    exit
  end

  reader = Class.new do
    def next prompt
      Readline.readline(prompt, true)
    end
    def result
      puts yield
    end
    def exit v; puts if v.nil?; end
  end.new

  begin
  open(File.join(ENV["HOME"],".envjsrb.js")) do |f|
    js_readline = f.read.split("\n")
  end
  rescue; end

  begin
  open(File.join(ENV["HOME"],".envjsrb.ruby")) do |f|
    ruby_readline = f.read.split("\n")
  end
  rescue; end

  case mode
  when :js; paste_history js_readline
  when :rb; paste_history ruby_readline
  end

  do_loop reader

  case mode
  when :js; js_readline = copy_history
  when :rb; ruby_readline = copy_history
  end

  open(File.join(ENV["HOME"],".envjsrb.js"),"w") do |f|
    f.write js_readline[0..100].join("\n")+"\n"
  end

  open(File.join(ENV["HOME"],".envjsrb.ruby"),"w") do |f|
    f.write ruby_readline[0..100].join("\n")+"\n"
  end

end
