require 'pathname'

module Airake 
  # Project settings for AIR app
  class Project
  
    attr_reader :project_name
    attr_reader :base_dir, :bin_dir, :src_dir, :lib_dir, :test_dir
    attr_reader :mxml_path, :appxml_path, :air_path, :swf_path
    attr_reader :debug
    
    attr_reader :mxmlc_path, :adt_path, :adl_path
    attr_reader :mxmlc_command, :adt_command, :adl_command, :mxmlc_extra_opts
    
    Options = [ :mxmlc_path, :adt_path, :bin_dir, :src_dir, :lib_dir, :test_dir, 
      :appxml_path, :air_path, :swf_path, :debug, :mxmlc_command, :adt_command, 
      :adl_command, :mxmlc_extra_opts, :adl_extra_opts, :adt_extra_opts ]
  
    # Settings
    # Specify base directory, the path to Project.mxml (relative) and options.
    # Options override default settings, see Options class var.
    def initialize(base_dir, mxml_path, options = {})
      @base_dir = base_dir
      @mxml_path = mxml_path
      
      @mxmlc_path = options[:mxmlc_path] || "mxmlc"
      @adt_path = options[:adt_path] || "adt"
      @adl_path = options[:adl] || "adl"
      
      @bin_dir = options[:bin_dir] || File.join(base_dir, "bin")
      @src_dir = options[:src_dir] || File.join(base_dir, "src")
      @lib_dir = options[:lib_dir] || File.join(base_dir, "lib")
      @test_dir = options[:test_dir] || File.join(base_dir, "test")
    
      @project_name = File.basename(@mxml_path, ".mxml")      
      mxml_dir = File.expand_path(File.dirname(@mxml_path))    
      @appxml_path = options[:appxml_path] || File.join(mxml_dir, "#{project_name}-app.xml")
    
      # Dest package files
      @air_path = options[:air_path] || File.join(@bin_dir, "#{project_name}.air")
      @swf_path = options[:swf_path] || File.join(@bin_dir, "#{project_name}.swf")
    
      @debug = options[:debug].is_a?(TrueClass) || options[:debug] == "true"
      
      @mxmlc_extra_opts = options[:mxmlc_extra_opts]
      @adl_extra_opts = options[:adl_extra_opts]
      @adt_extra_opts = options[:adt_extra_opts]
      
      @mxmlc_command = options[:mxmlc_command] || load_mxmlc_command
      @adt_command = options[:adt_command] || load_adt_command
      @adl_command = options[:adl_command] || load_adl_command
    end
    
    # Run the ADT
    def run_adt
      run(adt_command)
    end
  
    # Run the Flex compiler
    def run_mxmlc    
      run(mxmlc_command)
    end
  
    # Run the Adobe Debug Launcher
    def run_adl
      run(adl_command)
    end
    
    # Create project using parameters from rake ENV
    # You can set any of the options via ENV; just upcase it. <tt>:foo_bar => ENV["FOO_BAR"]</tt>
    # Booleans must be "true" for true and false for anything else.
    def self.new_from_rake(env, is_test = false)
      base_dir = env["BASE_DIR"]
      mxml = is_test ? env["MXML_TEST"] : env["MXML"]
      
      # For any option check to see if its in the ENV      
      options = {}
      Options.each do |option_key|
        env_key = option_key.to_s.upcase
        options[option_key] = env[env_key] if env.has_key?(env_key)
      end
      puts "Using options: #{options.inspect}" unless options.empty?
      self.new(base_dir, mxml, options)
    end
    
  protected     
  
    # Get the mxmlc command
    def load_mxmlc_command
      command = []
      command << @mxmlc_path
      command << "+configname=air"
      command << source_path_option
      command << "-library-path+=#{@lib_dir}"
      command << "-output #{@swf_path}"
      command << debug_option
      command << @mxmlc_extra_opts
      command << "-disable-incremental-optimizations=true"
      command << "-- #{@mxml_path}"
      command.compact.join(" ")
    end

    # Get the ADT command
    def load_adt_command
      command = []
      command << @adt_path
      command << @adt_extra_opts
      command << "-package #{relative_path(@air_path)}"
      command << relative_path(@appxml_path)
      command << relative_path(@swf_path)
      command.compact.join(" ")
    end

    # Get the ADL command
    def load_adl_command
      command = []
      command << @adl_path
      command << @adl_extra_opts
      command << @appxml_path
      command << @base_dir
      command.compact.join(" ")
    end
    
    # List of directories in lib/ for source_path +=
    def lib_source_paths
      Dir["#{@lib_dir}/*"].collect { |f| f if File.directory?(f) }.compact + [ @test_dir, @src_dir ]
    end
  
    def source_path_option
      @source_paths ||= lib_source_paths
      @source_paths.empty? ? "" : "-source-path #{@source_paths.join(" ")}"
    end
  
    def debug_option
      "-debug=#{@debug}" if @debug
    end    
  
    def relative_path(path)
      Pathname.new(path).relative_path_from(Pathname.new(@base_dir))
    end
  
    def fail(cmd, error_status)
      fail_message = <<-EOS
        
        The '#{cmd.split.first}' command failed to run. This could be because this executable isn't in the path.
        
        To include mxmlc, adl and adt (from Flex Builder):
          export PATH="/Applications/Adobe Flex Builder 3/sdks/moxie/bin:$PATH"
          
        To include adl and adt (from Apollo SDK):
          export PATH="/Applications/ApolloSDK/bin:$PATH"
        
      EOS
      puts fail_message if error_status != 256 && error_status != 4096
      raise "[#{error_status}] Failed to run command: #{cmd}"
    end

    def run(cmd)
      puts "Running: #{cmd}"
      system cmd or fail(cmd, $?)
    end
  
  end
end