Encoding.default_external = "UTF-8" if defined?(Encoding)
require 'json'
require 'bundler/gem_tasks'

# returns the source filename for a given JSON build file
# (e.g., "ui.core.jquery.json" returns "core.js")
def source_file_for_build_file(build_file)
  "#{build_file.sub('ui.', '').sub('.jquery.json', '')}.js"
end

# returns the source filename for a named file in the 'dependencies'
# array of a JSON build file
# (e.g., if the JSON build file contains
#
#    "dependencies": {
#      "jquery": ">=1.6",
#      "ui.core": "1.9.2",
#      "ui.widget": "1.9.2"
#    },
#
# then "ui.widget" returns "widget.js")
#
# The only exception is "jquery", which doesn't follow the
# same naming conventions so it's a special case.
def source_file_for_dependency_entry(dep_entry)
  return "jquery.js" if dep_entry == 'jquery'

  "#{dep_entry.sub 'ui.', ''}.js"
end

# return a Hash of dependency info, whose keys are jquery-ui
# source files and values are Arrays containing the source files
# they depend on
def map_dependencies
  dependencies = {}
  Dir.glob("jquery-ui/*.jquery.json").each do |build_file|
    build_info = JSON.parse(File.read build_file)
    source_file_name = source_file_for_build_file(File.basename(build_file))

    deps = build_info['dependencies'].keys

    # None of jquery.ui files should depend on jquery.js,
    # so we remove 'jquery' from the list of dependencies for all files
    deps.reject! {|d| d == "jquery" }

    deps.map! {|d| source_file_for_dependency_entry d }

    dependencies[source_file_name] = deps
  end
  dependencies
end

def dependency_hash
  @dependency_hash ||= map_dependencies
end

def version
  JSON.load(File.read('jquery-ui/package.json'))['version']
end

task :submodule do
  sh 'git submodule update --init' unless File.exist?('jquery-ui/README.md')
end

def get_js_dependencies(basename)
  dependencies = dependency_hash[basename]
  if dependencies.nil?
    puts "Warning: No dependencies found for #{basename}"
    dependencies = []
  end
  # Make sure we do not package assets with broken dependencies
  dependencies.each do |dep|
    unless File.exist?("jquery-ui/ui/#{dep}")
      fail "#{basename}: missing #{dep}"
    end
  end
  dependencies
end

def remove_js_extension(path)
  path.chomp(".js")
end

def protect_copyright_notice(source_code)
  # YUI does not minify comments starting with "/*!"
  # The i18n files start with non-copyright comments, so we require a newline
  # to avoid protecting those
  source_code.gsub!(/\A\s*\/\*\r?\n/, "/*!\n")
end

def build_image_dependencies(source_code)
  image_dependencies = Set.new source_code.scan(/url\("?images\/([-_.a-zA-Z0-9]+)"?\)/).map(&:first)
  code = image_dependencies.inject("") do |acc, img|
    acc += " *= depend_on_asset \"jquery-ui/#{img}\"\n"
  end
end

desc "Remove the app directory"
task :clean do
  rm_rf 'app'
end

desc "Generate the JavaScript assets"
task :javascripts => :submodule do
  Rake.rake_output_message 'Generating javascripts'

  target_dir = "app/assets/javascripts"
  target_ui_dir = "#{target_dir}/jquery-ui"
  mkdir_p target_ui_dir

  Dir.glob("jquery-ui/ui/*.js").each do |path|
    basename = File.basename(path)
    dep_modules = get_js_dependencies(basename).map(&method(:remove_js_extension))
    File.open("#{target_ui_dir}/#{basename}", "w") do |out|
      dep_modules.each do |mod|
        out.write("//= require jquery-ui/#{mod}\n")
      end
      out.write("\n") unless dep_modules.empty?
      source_code = File.read(path)
      source_code.gsub!('@VERSION', version)
      protect_copyright_notice(source_code)
      out.write(source_code)
    end
  end

  # process the i18n files separately for performance, since they will not have dependencies
  # https://github.com/joliss/jquery-ui-rails/issues/9
  Dir.glob("jquery-ui/ui/i18n/*.js").each do |path|
    basename = File.basename(path)
    File.open("#{target_ui_dir}/#{basename}", "w") do |out|
      source_code = File.read(path)
      source_code.gsub!('@VERSION', version)
      protect_copyright_notice(source_code)
      out.write(source_code)
    end
  end

  File.open("#{target_ui_dir}/effect.all.js", "w") do |out|
    Dir.glob("jquery-ui/ui/effect*.js").sort.each do |path|
      asset_name = remove_js_extension(File.basename(path))
      out.write("//= require jquery-ui/#{asset_name}\n")
    end
  end
  File.open("#{target_dir}/jquery-ui.js", "w") do |out|
    Dir.glob("jquery-ui/ui/*.js").sort.each do |path|
      asset_name = remove_js_extension(File.basename(path))
      out.write("//= require jquery-ui/#{asset_name}\n")
    end
  end
end

desc "Generate the CSS assets"
task :stylesheets => :submodule do
  Rake.rake_output_message 'Generating stylesheets'

  target_dir = "app/assets/stylesheets"
  target_ui_dir = "#{target_dir}/jquery-ui"
  mkdir_p target_ui_dir

  File.open("#{target_dir}/jquery-ui.css", "w") do |out|
    out.write("//= require jquery-ui/all\n")
  end

  css_dir = "jquery-ui/themes/base"
  Dir.glob("#{css_dir}/*.css").each do |path|
    basename = File.basename(path)
    source_code = File.read(path)
    source_code.gsub!('@VERSION', version)
    protect_copyright_notice(source_code)
    extra_dependencies = []
    # Is "theme" listed among the dependencies for the matching JS file?
    unless basename =~ /\.(all|base|core)\./
      if dependencies = dependency_hash[basename.sub(/\.css/, '.js')]
        dependencies.each do |dependency|
          dependency = dependency.sub(/\.js$/, '')
          dependent_stylesheet = "#{dependency}.css"
          extra_dependencies << dependency if File.exists?("#{css_dir}/#{dependent_stylesheet}")
        end
        extra_dependencies << 'theme'
      end
    end
    extra_dependencies.reverse.each do |dep|
      # Add after first comment block
      source_code = source_code.sub(/\A((.*?\*\/\n)?)/m, "\\1/*\n *= require jquery-ui/#{dep}\n */\n")
    end
    # Use "require" instead of @import
    source_code.gsub!(/^@import (.*)$/) { |s|
      m = s.match(/^@import (url\()?"(?<module>[-_.a-zA-Z]+)\.css"\)?;/) \
        or fail "Cannot parse import: #{s}"
      "/*\n *= require jquery-ui/#{m['module']}\n */"
    }
    # Be cute: collapse multiple require comment blocks into one
    source_code.gsub!(/^( \*= require .*)\n \*\/(\n+)\/\*\n(?= \*= require )/, '\1\2')
    source_code.gsub!(/\A(\/\*!.+?\*\/\s)/m, "\\1\n/*\n#{build_image_dependencies(source_code)} */\n\n") unless build_image_dependencies(source_code).empty?
    # Replace hard-coded image URLs with asset path helpers
    image_re = /url\("?images\/([-_.a-zA-Z0-9]+)"?\)/
    extname = source_code =~ image_re ? ".erb" : ""
    source_code.gsub!(image_re, 'url(<%= image_path("jquery-ui/\1") %>)')
    File.open("#{target_ui_dir}/#{basename}#{extname}", "w") do |out|
      out.write(source_code)
    end
  end
end

desc "Generate the image assets"
task :images => :submodule do
  Rake.rake_output_message 'Copying images'

  target_dir = "app/assets/images/jquery-ui"
  mkdir_p target_dir

  FileUtils.cp(Dir.glob("jquery-ui/themes/base/images/*"), target_dir)
end

desc "Update Jquery::Ui::Rails::JQUERY_UI_VERSION"
task :version => :submodule do
  Rake.rake_output_message "Setting Jquery::Ui::Rails::JQUERY_UI_VERSION = \"#{version}\""

  versionRb = 'lib/jquery/ui/rails/version.rb'
  versionRbSource = File.read(versionRb)
  versionDefinition = "JQUERY_UI_VERSION = \"#{version}\""
  versionRbSource.sub! /JQUERY_UI_VERSION = "[^"]*"/, versionDefinition \
    or fail "Could not find JQUERY_UI_VERSION in #{versionRb}"
  File.open(versionRb, 'w') do |out|
    out.write(versionRbSource)
  end
end

desc "Clean and then generate everything (default)"
task :assets => [:clean, :javascripts, :stylesheets, :images, :version]

task :build => :assets

task :default => :assets
