#!/usr/bin/env ruby
require 'ruby2d/version'
require 'fileutils'

# Extending `String` to include some fancy colors
class String
  def colorize(c); "\e[#{c}m#{self}\e[0m" end
  def bold;  colorize('1')    end
  def error; colorize('4;31') end
end

# The installed gem directory
@gem_dir = "#{Gem::Specification.find_by_name('ruby2d').gem_dir}"

# The Ruby 2D library files
@lib_files = [
  'exceptions',
  'color',
  'window',
  'application',
  'dsl',
  'quad',
  'rectangle',
  'square',
  'triangle',
  'image',
  'sprite',
  'text',
  'sound',
  'music'
]


# Check if source file provided is good
def check_build_src_file(rb_file)
  if !rb_file
    puts "Please provide a Ruby file to build."
    exit
  elsif !File.exists? rb_file
    puts "Can't find file: #{rb_file}"
    exit
  end
end


# Assemble the Ruby 2D library in one `.rb` file
def make_lib
  FileUtils.mkdir_p 'build'
  
  lib_dir = "#{@gem_dir}/lib/ruby2d/"
  
  lib = ""
  @lib_files.each do |f|
    lib << File.read("#{lib_dir + f}.rb") + "\n\n"
  end
  
  lib << "
include Ruby2D::DSL
include Ruby2D\n"
  
  File.write('build/lib.rb', lib)
end


# Remove `require 'ruby2d'` from source file
def strip_require(file)
  output = ''
  File.foreach(file) do |line|
    output << line unless line =~ /require ('|")ruby2d('|")/
  end
  return output
end


# Build a native version of the provided Ruby application
def build_native(rb_file)
  
  # Check if MRuby exists; if not, quit
  if `which mruby`.empty?
    puts "#{'Error:'.error} Can't find MRuby, which is needed to build native Ruby 2D applications.\n"
    exit
  end
  
  # Assemble the Ruby 2D library in one `.rb` file and compile to bytecode
  make_lib
  `mrbc -Bruby2d_lib -obuild/lib.c build/lib.rb`
  
  # Read the provided Ruby source file, copy to build dir and compile to bytecode
  
  File.open('build/src.rb', 'w') do |file|
    file << strip_require(rb_file)
  end
  
  `mrbc -Bruby2d_app -obuild/src.c build/src.rb`
  
  # Combine contents of C source files and bytecode into one file
  open('build/app.c', 'w') do |f|
    f << "#define MRUBY 1" << "\n\n"
    f << File.read("build/lib.c") << "\n\n"
    f << File.read("build/src.c") << "\n\n"
    f << File.read("#{@gem_dir}/ext/ruby2d/ruby2d.c")
  end
  
  # Compile to native executable
  `cc build/app.c -lmruby \`simple2d --libs\` -o build/app`
  
  # Success!
  puts "Native app created at `build/app`"
end


# Build a web-based version of the provided Ruby application
def build_web(rb_file)
  
  # Assemble the Ruby 2D library in one `.rb` file and compile to JS
  make_lib
  `opal --compile --no-opal build/lib.rb > build/lib.js`
  
  # Read the provided Ruby source file, copy to build dir, and compile to JS
  
  File.open('build/src.rb', 'w') do |file|
    file << strip_require(rb_file)
  end
  
  `opal --compile --no-opal build/src.rb > build/src.js`
  
  FileUtils.cp "#{@gem_dir}/ext/ruby2d/ruby2d-opal.rb", "build/"
  `opal --compile --no-opal build/ruby2d-opal.rb > build/ruby2d-opal.js`
  
  # Combine contents of JS source files and compiled JS into one file
  open('build/app.js', 'w') do |f|
    f << File.read("#{@gem_dir}/assets/simple2d.js") << "\n\n"
    f << File.read("#{@gem_dir}/assets/opal.js") << "\n\n"
    f << File.read("build/lib.js") << "\n\n"
    f << File.read("build/ruby2d-opal.js") << "\n\n"
    f << File.read("build/src.js") << "\n\n"
  end
  
  # Copy over HTML template
  FileUtils.cp "#{@gem_dir}/assets/template.html", "build/app.html"
  
  # Success!
  puts "Web app created at `build/app.js`",
       "  Run by opening `build/app.html`"
end


# Build an application package for the current platform
def build_package
  require 'fileutils'
  
  icon_path = "#{@gem_dir}/assets/app.icns"
  
  FileUtils.mkpath "build/App.app/Contents/MacOS"
  FileUtils.mkpath "build/App.app/Contents/Resources"
  FileUtils.cp icon_path, "build/App.app/Contents/Resources"
  
  info_plist = %(
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleExecutable</key>
  <string>app</string>
  <key>CFBundleIconFile</key>
  <string>app.icns</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>1.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>CFBundleSignature</key>
  <string>????</string>
  <key>CFBundleVersion</key>
  <string>1.0</string>
</dict>
</plist>
)
  
  File.open("build/App.app/Contents/Info.plist", 'w') { |f| f.write(info_plist) }
  FileUtils.cp "build/app", "build/App.app/Contents/MacOS/"
  puts "App written to `build/App.app`."
end


# Clean up unneeded build files
def clean_up
  FileUtils.rm(
    Dir.glob('build/{src,lib}.{rb,c,js}') +
    Dir.glob('build/ruby2d-opal.{rb,js}') +
    Dir.glob('build/app.c')
  )
end


# Check Command-line Arguments #################################################

usage = "Ruby 2D: Make cross-platform 2D applications in Ruby".bold + "\n
Usage: ruby2d <command> <options>
              [-v|--version]

Summary of commands and options:
  build         Build the application both natively and for the web
    --native      Build only native
    --web         Build only web
    --debug       Build native and web, keeping all intermediate files
  package       Create application package for distribution (experimental)
  -v|--version  Prints the installed version\n\n"

case ARGV[0]
when 'build'
  puts "Building..."
  case ARGV[1]
  when '--native'
    build_native(ARGV[2])
  when '--web'
    build_web(ARGV[2])
  else
    if ARGV[2]; puts usage; exit end
    check_build_src_file(ARGV[1])
    build_native(ARGV[1])
    build_web(ARGV[1])
  end
  unless ARGV.include? '--debug'; clean_up end
when 'package'
  puts "Running package..."
  build_package
when '-v', '--version'
  puts Ruby2D::VERSION
else
  puts usage
end
