module Sinatra
  module AssetPack
    module Compressor
      extend self

      # Compresses a given string.
      #
      #     compress File.read('x.js'), :js, :jsmin
      #
      def compress(str, type, engine=nil, options={})
        engine ||= 'jsmin'   if type == :js
        engine ||= 'simple'  if type == :css

        key  = :"#{type}/#{engine}"
        meth = compressors[key]
        return str  unless meth

        meth[str, options]
      end

      def compressors
        @compressors ||= {
          :'js/jsmin'    => method(:jsmin),
          :'js/yui'      => method(:yui_js),
          :'js/closure'  => method(:closure_js),
          :'css/sass'    => method(:sass),
          :'css/yui'     => method(:yui_css),
          :'css/simple'  => method(:simple_css),
          :'css/sqwish'  => method(:sqwish_css)
        }
      end

      # =====================================================================
      # Compressors

      def jsmin(str, options={})
        require 'jsmin'
        JSMin.minify str
      end

      def sass(str, options={})
        Tilt.new("scss", {:style => :compressed}) { str }.render
      rescue LoadError
        simple_css str
      end

      def yui_css(str, options={})
        require 'yui/compressor'
        YUI::CssCompressor.new.compress(str)
      rescue Errno::ENOENT
        sass str
      end

      def yui_js(str, options={})
        require 'yui/compressor'
        YUI::JavaScriptCompressor.new(options).compress(str)
      rescue LoadError
        jsmin str
      end

      def simple_css(str, options={})
        str.gsub! /[ \r\n\t]+/m, ' '
        str.gsub! %r{ *([;\{\},:]) *}, '\1'
      end

      def sqwish_css(str, options={})
        cmd = "#{sqwish_bin} %f "
        cmd += "--strict"  if options[:strict]

        _, input = sys :css, str, cmd
        output   = input.gsub(/\.css/, '.min.css')

        File.read(output)
      rescue => e
        simple_css str
      end

      def sqwish_bin
        ENV['SQWISH_PATH'] || "sqwish"
      end

      def closure_js(str, options={})
        require 'net/http'
        require 'uri'

        response = Net::HTTP.post_form(URI.parse('http://closure-compiler.appspot.com/compile'), {
          'js_code' => str,
          'compilation_level' => options[:level] || "ADVANCED_OPTIMIZATIONS",
          'output_format' => 'text',
          'output_info' => 'compiled_code'
        })

        response.body
      end

      # For others
      def sys(type, str, cmd)
        t = Tempfile.new ['', ".#{type}"]
        t.write(str)
        t.close

        output = `#{cmd.gsub('%f', t.path)}`
        FileUtils.rm t

        [output, t.path]
      end
    end
  end
end