module Unitwise
  # Functional is an alterative function-based scale for atoms with a
  # non-linear (or non-zero y-intercept) scale. This is most commonly used for
  # temperatures. Known functions for converting to and from special atoms
  # are setup as class methods here.
  class Functional < Scale
    extend Math

    def self.to_cel(x)
      x - 273.15
    end

    def self.from_cel(x)
      x + 273.15
    end

    def self.to_degf(x)
      9.0 * x / 5.0 - 459.67
    end

    def self.from_degf(x)
      5.0 / 9.0 * (x + 459.67)
    end

    def self.to_degre(x)
      4.0 * x / 5.0 - 218.52
    end

    def self.from_degre(x)
      5.0 / 4.0 * (x + 218.52)
    end

    def self.to_hpX(x)
      -log10(x)
    end

    def self.from_hpX(x)
      10.0 ** -x
    end

    def self.to_hpC(x)
      -log(x) / log(100.0)
    end

    def self.from_hpC(x)
      100.0 ** -x
    end

    def self.to_tan100(x)
      100.0 * tan(x)
    end

    def self.from_tan100(x)
      atan(x / 100.0)
    end

    def self.to_ph(x)
      to_hpX(x)
    end

    def self.from_ph(x)
      from_hpX(x)
    end

    def self.to_ld(x)
      Math.log(x) / Math.log(2.0)
    end

    def self.from_ld(x)
      2.0 ** x
    end

    def self.to_ln(x)
      log(x)
    end

    def self.from_ln(x)
      Math::E ** x
    end

    def self.to_lg(x)
      log10(x)
    end

    def self.from_lg(x)
      10.0 ** x
    end

    def self.to_2lg(x)
      2.0 * log10(x)
    end

    def self.from_2lg(x)
      10.0 ** (x / 2.0)
    end

    attr_reader :function_name

    # Setup a new functional.
    # @param value [Numeric] The magnitude of the scale
    # @param unit [Unitwise::Unit, String] The unit of the scale
    # @param function_name [String, Symbol] One of the class methods above to be
    # used for conversion
    def initialize(value, unit, function_name)
      @function_name = function_name
      super(value, unit)
    end

    # Get the equivalent scalar value of a magnitude on this scale
    # @param magnitude [Numeric] The magnitude to find the scalar value for
    # @return [Numeric] Equivalent linear scalar value
    # @api public
    def scalar(magnitude = value)
      self.class.send(:"from_#{function_name}", magnitude)
    end

    # Get the equivalent magnitude on this scale for a scalar value
    # @param scalar [Numeric] A linear scalar value
    # @return [Numeric] The equivalent magnitude on this scale
    # @api public
    def magnitude(scalar = scalar())
      self.class.send(:"to_#{function_name}", scalar)
    end
  end
end
