#!/usr/bin/env ruby

require "optparse"
require "pathname"

require File.dirname(__FILE__) + "/../lib/r2p"

module R2P

  # Set default values of the compiler options.
  options = {
    :execute      => nil,
    :ast          => false,
    :color        => false,
    :debug_parser => false,
    :verbose      => false,
    :runtime_path => nil,
  }

  # Define compiler options...
  option_parser = OptionParser.new do |option_parser|
    option_parser.banner = "Usage: r2p [options] [ruby_file] [php_file]"

    option_parser.separator ""
    option_parser.separator "General options:"

    option_parser.on("-R", "--runtime-path path", "set path to the PHP runtime") do |runtime_path|
      options[:runtime_path] = runtime_path
    end

    option_parser.on("-e", "--execute ruby_program",
        "compile specified ruby_program to standard",
        "output (ruby_file and php_file ignored)") do |execute|
      options[:execute] ||= []
      options[:execute] << execute
    end

    option_parser.on("-w", "enable verbose mode") do
      options[:verbose] = true
    end

    option_parser.on("-h", "--help", "write this help") do
      puts option_parser
      exit
    end

    option_parser.on("-v", "--version", "write version information") do
      puts "0.3"
      exit
    end

    option_parser.separator ""
    option_parser.separator "Debug options:"

    option_parser.on("-a", "--ast", "print only AST") do
      options[:ast] = true
    end

    option_parser.on("-c", "--color", "colorize printed AST") do
      options[:color] = true
    end

    option_parser.on("-l", "--show-lines", "show source lines in printed AST") do
      options[:show_lines] = true
    end

    option_parser.on("-y", "--yydebug", "enable parser debug dumps") do
      options[:debug_parser] = true
    end
  end

  # ...and parse the options.
  begin
    option_parser.parse!(ARGV)
  rescue OptionParser::InvalidOption => e
    abort e
  rescue OptionParser::MissingArgument => e
    abort e
  end

  # Check for invalid option combinations.

  if options[:execute] && ARGV.length > 0
    abort "No files should be specified with -e or --execute option."
  end

  if options[:color] && !options[:ast]
    abort "The -c or --color option can be specified only when -a or --ast " +
      "option is\nspecified too."
  end

  if options[:show_lines] && !options[:ast]
    abort "The -l or --show-lines option can be specified only when -a or " +
      "--ast option is\nspecified too."
  end

  if options[:runtime_path] && options[:ast]
    abort "The -R or --runtime-path option can be specified only when -a or --ast " +
      "option is\nnot specified."
  end

  # Now determine, what the user wants us to do, and do it.

  def self.build_runtime_path(file, options)
    if options[:runtime_path].nil?
      runtime_path = File.expand_path("../../runtime/lib", File.dirname(__FILE__))
      file_path = File.expand_path(File.dirname(file))
      Pathname.new(runtime_path).relative_path_from(Pathname.new(file_path)).to_s
    else
      options[:runtime_path]
    end
  end

  def self.compile_or_build_ast(code, file, options)
    compiler_options = {
      :verbose      => options[:verbose],
      :debug        => options[:debug_parser],
      :file         => file,
      :runtime_path => build_runtime_path(file, options)
    }
    if options[:ast]
      highlighter = if options[:color]
        AST::TerminalHighlighter
      else
        AST::NullHighlighter
      end.instance
      RubyAST::Node::show_position = true if options[:show_lines]
      result, messages = R2P.parse(code, compiler_options)
      result = result.to_s(highlighter) if result
    else
      result, messages = R2P.compile(code, compiler_options)
    end

    messages.each { |message| $stderr.puts message.to_s }

    if result == nil
      abort
    end
    result
  end

  def self.extract_file_names(argv)
    ruby_file = ARGV[0]
    if ARGV.length == 1
      if ruby_file =~ /\.rb$/
        php_file = ruby_file.sub(/\.rb$/, ".php")
      else
        php_file = ruby_file + ".php"
      end
    else
      php_file = ARGV[1]
    end
    [ruby_file, php_file]
  end

  if !options[:execute]
    if ARGV.length == 0
      options[:runtime_path] = build_runtime_path("dummy", options)
      input_stream = $stdin
      output_stream = $stdout
      file = "-"
    elsif ARGV.length == 1 || ARGV.length == 2
      ruby_file, php_file = extract_file_names(ARGV)
      abort "File does not exist: #{ruby_file}." unless File.exists?(ruby_file)
      options[:runtime_path] = build_runtime_path(php_file, options)
      input_stream = File.open(ruby_file)
      output_stream = File.open(php_file, "w")
      file = File.basename(ruby_file)
    else
      abort option_parser
    end

    output_stream.puts(
     compile_or_build_ast(input_stream.read, file, options)
    )
    input_stream.close
    output_stream.close
  else
    options[:runtime_path] = build_runtime_path("dummy", options)
    options[:execute].each do |code|
      puts compile_or_build_ast(code, "-e", options)
    end
  end

end
