class TETU::Helpers::Curve
  include YAML::Serializable
  property function : String
  property coefs : Hash(String, Float64 | Int32)

  def coef(name : String, default : Float64 = 0.0) : Float64
    @coefs.fetch(name, default).to_f64
  end

  # Proc(Float64, Tick, Float64).new { |x| x }
  # (x) : the tier of the upgrade
  FUNCTIONS = {
    # = x (any constant name is accepted, only one)
    "constant" => ->(f : Curve, x : Float64, t : TETU::Tick) {
      f.coefs.values.first.to_f64
    },
    # a(x + b) + c (constants are a, b)
    "linear" => ->(f : Curve, x : Float64, t : TETU::Tick) {
      x * f.coef("a", 1.0) + f.coef("b", 0.0)
    },
    # ax⁰ + bx¹ + cx² + dx³ + ... (constants to define are alpha order)
    "polynome" => ->(f : Curve, x : Float64, t : TETU::Tick) {
      coef_ordered = f.coefs.keys.sort
      return 0.0 if coef_ordered.empty?
      index = 0
      coef_ordered[1..-1].reduce(f.coef(coef_ordered.first)) do |base, coef|
        index += 1
        base + coef.to_f64 * (x ** index)
      end
    },
    # log[base](x + b)*c + d (constants are a, base, b, c)
    "log" => ->(f : Curve, x : Float64, t : TETU::Tick) {
      Math.log(x + f.coef("b", 2.0), f.coef("base", 2.0)) * f.coef("a", 1.0) + f.coef("c", 0.0)
    },
    # (a^x)b + c (constants are a, base, b, c)
    "squared" => ->(f : Curve, x : Float64, t : TETU::Tick) {
      (x + f.coef("b", 2.0)) ** f.coef("base", 2.0) * f.coef("a", 1.0) + f.coef("c", 0.0)
    },
  }

  def execute(x : Float64, t : TETU::Tick = 0) : Float64
    f = FUNCTIONS.fetch(@function) { raise "Invalid \"#{@function}\" name for function type. Valids are #{FUNCTIONS.keys.join(", ")}" }
    f.call(self, x, t)
  end
end
