module UnitConversion
  class ParsedUnit

    attr_reader :original_unit, :unit

    def initialize(str)
      @original_unit = str
      @unit = normalize_unit_names(str)
    end

    def to_s
      unit.gsub(/[a-z_\[\]]+/) do |match|
        if aliases = UNIT_ALIASES[match.to_sym]
          aliases.first
        else
          match
        end
      end
    end

    def density?
      compatible? 'g/ml'
    end

    def volume?
      compatible? 'ml'
    end

    def mass?
      compatible? 'g'
    end

    def metric?
      METRIC_UNIT_ALIASES.keys.include? unit.to_sym
    end

    def unitwise(value = 1)
      begin
        Unitwise(value, unit)
      rescue Unitwise::ExpressionError => err
        raise UnknownUnitError, err.message
      end
    end

    def compatible?(unit_str)
      begin
        unitwise.compatible_with? Unitwise(1, unit_str)
      rescue UnknownUnitError, TypeError, Unitwise::ConversionError, Unitwise::ExpressionError
        false
      end
    end

    def normalize_unit_names(unit_description)
      return unit_description.to_s if UNIT_ALIASES.include?(unit_description.to_sym)

      # fluid ounce is the only known unit with a space; fix it up before the following replacement
      unit_description = unit_description.gsub(/fl oz/i, 'floz')

      # if a cubic unit is found, map it to unit3 (ex 'cubic inches' is converted to 'inches3')
      unit_description = unit_description.gsub(/cubic\s+(\w+)/i, '\13')

      unit_description.downcase.gsub(/[a-z_\[\]]+/) do |match|
        UNIT_ALIAS_MAP[match] || match
      end
    end
  end
end