# Generate a download URL for a particular version of the engine.
#
# See `_data/download_configs.yml` for a reference table of slugs.

HOST_TUXFAMILY = "https://downloads.tuxfamily.org/godotengine"
HOST_HETZNER = "https://downloads.godotengine.org/"

FLAVOR_MATRIX = {
  "dev"    => 1,
  "alpha"  => 2,
  "beta"   => 3,
  "rc"     => 4,
  "stable" => 10
}

module MakeDownloadFilter
  # Input should be a value from versions.yml or the output of make_release_version().
  def get_download_platforms(input, mono = false)
    slugs_defaults = get_download_slugs(input, mono)
    if slugs_defaults.nil?
      return []
    end

    platforms = slugs_defaults["editor"].keys

    if slugs_defaults.key?("templates")
      platforms.push("templates")
    end

    if slugs_defaults.key?("extras")
      extras = slugs_defaults["extras"].keys
      for extra in extras do
        platforms.push(extra)
      end
    end

    return platforms
  end

  # Input should be a value from versions.yml or the output of make_release_version().
  def get_download_primary(input, mono = false)
    slugs_defaults = get_download_slugs(input, mono)
    if slugs_defaults.nil?
      return {}
    end

    primary = {}

    platforms = slugs_defaults["editor"].keys
    for platform in platforms do
      platform_key = platform.split(".")[0]

      unless primary.key?(platform_key)
        primary[platform_key] = platform
      end
    end

    if slugs_defaults.key?("templates")
      primary["templates"] = "templates"
    end

    return primary
  end

  # Input should be a value from versions.yml or the output of make_release_version().
  def make_download(input, platform, mono = false, host = "github")
    version_name = get_version_name(input)
    version_flavor = input["flavor"]

    version_bits = version_name.split(".")
    version_majmin = "#{version_bits[0]}.#{version_bits[1]}"

    slugs_defaults = get_download_slugs(input, mono)
    if slugs_defaults.nil?
      return "#"
    end

    mono_slug = ""
    if mono
      mono_slug = "/mono"
    end

    download_slug = ""
    if platform == "templates" and slugs_defaults.key?(platform)
      download_slug = slugs_defaults[platform]
    elsif slugs_defaults["editor"].key?(platform)
      download_slug = slugs_defaults["editor"][platform]
    elsif slugs_defaults.key?("extras") and slugs_defaults["extras"].key?(platform)
      download_slug = slugs_defaults["extras"][platform]
    else
      # Unknown platform key, abort.
      return "#"
    end

    download_file = ""
    if platform == "aar_library"
      download_file = "godot-lib.#{version_name}.#{version_flavor}.#{download_slug}"
    else
      # Format was slightly different up until 2.1.
      if version_bits[0] == "1" or (version_bits[0] == "2" and version_bits[1] == "0")
        download_file = "Godot_v#{version_name}_#{version_flavor}_#{download_slug}"
      else
        download_file = "Godot_v#{version_name}-#{version_flavor}_#{download_slug}"
      end
    end

    if host == "github" or host == "github_builds"
      # Use our custom download API that supports all github builds but will prefer S3 downloads
      return "#{HOST_HETZNER}?version=#{version_name}&flavor=#{version_flavor}&slug=#{download_slug}&platform=#{platform}"
    elsif host == "tuxfamily"
      if version_flavor == "stable"
        return "#{HOST_TUXFAMILY}/#{version_name}#{mono_slug}/#{download_file}"
      else
        return "#{HOST_TUXFAMILY}/#{version_name}/#{version_flavor}#{mono_slug}/#{download_file}"
      end
    end
  end

  # Input and release should be values from versions.yml or strings.
  def make_release_version(input, release)
    site_data = @context.registers[:site].data

    version_data = nil
    # Input can be a version string, e.g. 4.1. Try to match it against version data.
    if input.is_a? String
      version_data = site_data["versions"].find { |item| item["name"] == input }
    else
      version_data = input
    end
    if version_data.nil?
      return nil
    end

    release_data = nil
    # Release name can be a string as well. Try to match it with the current version flavor
    # or with one of previous releases.
    if release.is_a? String
      if version_data["flavor"] == release
        release_data = nil
      elsif version_data.key?("releases")
        release_data = version_data["releases"].find { |item| item["name"] == release }
      end
    else
      release_data = release
    end
    if release_data.nil?
      return version_data
    end

    new_version = version_data.dup
    new_version["flavor"] = release_data["name"]
    new_version["release_version"] = release_data.key?("release_version") ? release_data["release_version"] : version_data["name"]
    new_version["release_date"] = release_data["release_date"]
    new_version["release_notes"] = release_data["release_notes"]

    return new_version
  end

  private

  # Input should be a value from versions.yml or the output of make_release_version().
  def get_version_name(input)
      return input.key?("release_version") ? input["release_version"] : input["name"]
  end

  def parse_version_string(version_string)
    string_bits = version_string.split("-")

    version_bits = parse_version_name(string_bits[0])
    flavor_bits = parse_version_flavor(string_bits[1])

    return [ version_bits, flavor_bits ]
  end

  def parse_version_name(version_name)
    parsed_version = [ 0, 0, 0, 0 ]
    version_bits = version_name.split(".")

    parsed_version.each_with_index do |value, index|
      if version_bits[index] != nil
        parsed_version[index] = version_bits[index].to_i
      end
    end

    return parsed_version
  end

  def parse_version_flavor(version_flavor)
    flavor_name = version_flavor
    flavor_number = 0

    match = /([0-9]+)$/.match(version_flavor)
    if match != nil
      match_offset = match.offset(0)

      flavor_name = version_flavor[0, match_offset[0]]
      flavor_number = version_flavor[match_offset[0], match_offset[1] - match_offset[0]].to_i
    end

    return [ flavor_name, flavor_number ]
  end

  def get_version_bits_hash(version_bits)
    hash = 0

    # We assume no part of the Godot version string can be above 99,
    # but we also assume that values above 9 are possible.
    version_bits.each_with_index do |bit, index|
      pow_index = version_bits.size - index
      pow_value = 100 ** pow_index

      hash += pow_value * bit
    end

    return hash
  end

  def get_flavor_bits_hash(flavor_bits)
    hash = 0

    if FLAVOR_MATRIX.key?(flavor_bits[0])
      hash += FLAVOR_MATRIX[flavor_bits[0]] * 10000
    end

    hash += flavor_bits[1]

    return hash
  end

  def is_in_version_range(version_bits_hash, flavor_bits_hash, version_range)
    range_from = parse_version_string(version_range[0])
    range_to = parse_version_string(version_range[1])

    from_version_hash = get_version_bits_hash(range_from[0])
    to_version_hash = get_version_bits_hash(range_to[0])

    if version_bits_hash < from_version_hash or version_bits_hash > to_version_hash
      return false
    end

    if version_bits_hash == from_version_hash and flavor_bits_hash < get_flavor_bits_hash(range_from[1])
      return false
    end

    if version_bits_hash == to_version_hash and flavor_bits_hash > get_flavor_bits_hash(range_to[1])
      return false
    end

    return true
  end

  # Input should be a value from versions.yml or the output of make_release_version().
  def get_download_slugs(input, mono = false)
    version_name = get_version_name(input)
    version_major = version_name.split(".")[0].to_i

    # Access the site data for the slug reference table.
    site_data = @context.registers[:site].data
    download_configs = site_data["download_configs"]

    # If there is no config for this major version, abort.
    if not download_configs["defaults"].key?(version_major)
      return nil
    end
    slugs_data = download_configs["defaults"][version_major]

    # Consider potential overrides.
    if download_configs.key?("overrides")
      version_bits_hash = get_version_bits_hash(parse_version_name(version_name))
      flavor_bits_hash = get_flavor_bits_hash(parse_version_flavor(input["flavor"]))

      for override_data in download_configs["overrides"] do
        if override_data["version"] != version_major
          next
        end

        if is_in_version_range(version_bits_hash, flavor_bits_hash, override_data["range"])
          slugs_data = override_data["config"]
          break
        end
      end
    end

    if mono
      # Requesting mono, but there is no config for mono, abort.
      if not slugs_data.key?("mono")
        return nil
      end
      slugs_data = slugs_data["mono"]
    end

    return slugs_data
  end

end

Liquid::Template.register_filter(MakeDownloadFilter)
