module JsLocaleHelper

    def self.load_translations(locale, opts=nil)
        opts ||= {}

        @loaded_translations = nil if opts[:force]

        @loaded_translations ||= HashWithIndifferentAccess.new
        @loaded_translations[locale] ||= begin
            locale_str = locale.to_s

            # load default translations
            translations = YAML::load(File.open("#{Rails.root}/config/locales/client.#{locale_str}.yml"))
            # load plugins translations
            plugin_translations = {}
            Dir["#{Rails.root}/plugins/*/config/locales/client.#{locale_str}.yml"].each do |file|
                plugin_translations.deep_merge! YAML::load(File.open(file))
            end

            # merge translations (plugin translations overwrite default translations)
            translations[locale_str]['js'].deep_merge!(plugin_translations[locale_str]['js']) if translations[locale_str] && plugin_translations[locale_str] && plugin_translations[locale_str]['js']

            # We used to split the admin versus the client side, but it's much simpler to just
            # include both for now due to the small size of the admin section.
            #
            # For now, let's leave it split out in the translation file in case we want to split
            # it again later, so we'll merge the JSON ourselves.
            admin_contents = translations[locale_str].delete('admin_js')
            translations[locale_str]['js'].deep_merge!(admin_contents) if admin_contents.present?
            translations[locale_str]['js'].deep_merge!(plugin_translations[locale_str]['admin_js']) if translations[locale_str] && plugin_translations[locale_str] && plugin_translations[locale_str]['admin_js']

            translations
        end
    end

    # purpose-built recursive algorithm ahoy!
    def self.deep_delete_matches(deleting_from, *checking_hashes)
        checking_hashes.compact!

        new_hash = deleting_from.dup
        deleting_from.each do |key, value|
            if value.is_a? Hash
                # Recurse
                new_at_key = deep_delete_matches(deleting_from[key], *(checking_hashes.map {|h| h[key]}))
                if new_at_key.empty?
                    new_hash.delete key
                else
                    new_hash[key] = new_at_key
                end
            else
                if checking_hashes.any? {|h| h.include? key}
                    new_hash.delete key
                end
            end
        end
        new_hash
    end

    def self.load_translations_merged(*locales)
        @loaded_merges ||= {}
        @loaded_merges[locales.join('-')] ||= begin
            all_translations = {}
            merged_translations = {}
            loaded_locales = []

            locales.map(&:to_s).each do |locale|
                all_translations[locale] = JsLocaleHelper.load_translations locale
                merged_translations[locale] = deep_delete_matches(all_translations[locale][locale], *loaded_locales.map { |l| merged_translations[l] })
                loaded_locales << locale
            end
            merged_translations
        end
    end

    def self.output_locale(locale)
        locale_sym = locale.to_sym
        locale_str = locale.to_s

        current_locale = I18n.locale
        I18n.locale = locale_sym

        site_locale = SiteSetting.default_locale.to_sym

        if Rails.env.development?
            translations = load_translations(locale_sym, force: true)
        else
            if locale_sym == :en
                translations = load_translations(locale_sym)
            elsif locale_sym == site_locale || site_locale == :en
                translations = load_translations_merged(locale_sym, :en)
            else
                translations = load_translations_merged(locale_sym, site_locale, :en)
            end
        end

        message_formats = strip_out_message_formats!(translations[locale_str]['js'])

        result = generate_message_format(message_formats, locale_str)

        result << "I18n.translations = #{translations.to_json};\n"
        result << "I18n.locale = '#{locale_str}';\n"
        # loading moment here cause we must customize it
        result << File.read("#{Rails.root}/lib/javascripts/moment.js")
        result << moment_locale(locale_str)
        result << moment_formats

        I18n.locale = current_locale

        result
    end

    def self.moment_formats
        result = ""
        result << moment_format_function('short_date_no_year')
        result << moment_format_function('short_date')
        result << moment_format_function('long_date')
        result << "moment.fn.relativeAge = function(opts){ return Nilavu.Formatter.relativeAge(this.toDate(), opts)};\n"
    end

    def self.moment_format_function(name)
        format = I18n.t("dates.#{name}")
        "moment.fn.#{name.camelize(:lower)} = function(){ return this.format('#{format}'); };\n"
    end

    def self.moment_locale(locale_str)
        # moment.js uses a different naming scheme for locale files

        locale_str = locale_str.tr('_', '-').downcase

        filename = Rails.root + "lib/javascripts/moment_locale/#{locale_str}.js"

        unless File.exists?(filename)
            # try the language without the territory
            locale_str = locale_str.partition('-').first
            filename = Rails.root + "lib/javascripts/moment_locale/#{locale_str}.js"
        end

        if File.exists?(filename)
            File.read(filename) << "\n"
        end || ""
    end

    def self.generate_message_format(message_formats, locale_str)
        result = "MessageFormat = {locale: {}};\n"

        formats = message_formats.map{|k,v| k.inspect << " : " << compile_message_format(locale_str,v)}.join(", ")
        result << "I18n._compiledMFs = {#{formats}};\n\n"

        filename = Rails.root + "lib/javascripts/locale/#{locale_str}.js"
        filename = Rails.root + "lib/javascripts/locale/en.js" unless File.exists?(filename)
        result << File.read(filename) << "\n\n"

        result << File.read("#{Rails.root}/lib/javascripts/messageformat-lookup.js")

        result
    end

    def self.compile_message_format(locale, format)
        ctx = V8::Context.new
        ctx.load(Rails.root + 'lib/javascripts/messageformat.js')
        path = Rails.root + "lib/javascripts/locale/#{locale}.js"
        ctx.load(path) if File.exists?(path)
        ctx.eval("mf = new MessageFormat('#{locale}');")
        ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")

    rescue V8::Error => e
        message = "Invalid Format: " << e.message
        "function(){ return #{message.inspect};}"
    end

    def self.strip_out_message_formats!(hash, prefix = "", rval = {})
        if hash.is_a?(Hash)
            hash.each do |key, value|
                if value.is_a?(Hash)
                    rval.merge!(strip_out_message_formats!(value, prefix + (prefix.length > 0 ? "." : "") << key, rval))
                elsif key.to_s.end_with?("_MF")
                    rval[prefix + (prefix.length > 0 ? "." : "") << key] = value
                    hash.delete(key)
                end
            end
        end
        rval
    end

end
