require 'radix/base'

module Radix

  # Redix separator used in string and array representations.
  DOT = '.'

  #
  DIV = '/'

  #
  DIVIDER = " "

  # Radix Numeric base class is subclassed by Radix::Integer and Radix::Float,
  # and is a subclass of Ruby's built-in Numeric class.
  class Numeric < ::Numeric

    # TODO: Make immutable, but best way to do it?

    #class << self
    #  alias_method :_new, :new
    #  private :_new
    #end
    #
    #def self.new(value, base=10)
    #  @cache ||= {}
    #  @cache[[value, base]] ||= _new(value, base)
    #end

    # Addition
    def +(other)
      operation(:+, other)
    end

    # Subtraction
    def -(other)
      operation(:-, other)
    end

    # Multiplication
    def *(other)
      operation(:*, other)
    end

    # Division
    def /(other)
      operation(:/, other)
    end

    private

    #
    def parse_base(base)
      case base
      when Array
        code = base
        base = base.size
      else
        code = nil
        base = base
      end
      return base, code
    end

    #
    def parse_numeric(value, base)
      value
    end

    # If a float style string is passed in for +value+, e.g. "9.5", the
    # decimal will simply be truncated. So "9.x" would become "9".
    def parse_string(value, base)
      digits = value.split(//)
      parse_array(digits, base)
    end

    # Take an Array in the form of [d1, d2, ..., DOT, d-1, d-2, ...]
    # and convert it to base ten, and store in @value.
    def parse_array(value, base)
      value = value.dup

      if value.first == '-'
        neg = true
        value.shift
      else
        neg = false
      end

      value = base_decode(value)

      ## raise an error if any digit is not less than base
      raise ArgumentError if value.any?{ |e| ::Numeric === e && base < e }

      v = decimal(value, base)

      neg ? -v : v
    end

    # Convert array of values of a different base to decimal.
    # This handles integer values. The method for Radix::Float
    # is slighly different.
    def decimal(digits, base)
      e = digits.size - 1
      v = 0
      digits.each do |n|
        v += n.to_i * base**e
        e -= 1
      end
      v
    end

    # Map array of values to base encoding. If no encoding is defined
    # this simply returns the +digits+ unchanged.
    def base_encode(digits)
      return digits unless @code
      digits.map do |i|
        case i
        when '-', DOT, DIV
          i
        else
          code[i]
        end
      end
    end

    # Decode an encoded array.
    def base_decode(digits)
      #return digits unless code
      code = self.code || BASE::B62
      digits.map do |c|
        case c
        when '-', DOT, DIV
          c
        when ::Numeric
          c
        else
          code.index(c)  # TODO: Could speed up with an reverse index.
        end
      end
    end

  end

end