#coding: utf-8
require 'pp'

module Reckon
  class Money
    include Comparable
    attr_accessor :amount, :currency, :suffixed
    def initialize( amount, options = {} )
      if options[:inverse]
        @amount = -1*amount.to_f
      else
        @amount = amount.to_f
      end
      @currency = options[:currency] || "$"
      @suffixed = options[:suffixed]
    end

    def to_f
      return @amount
    end

    def -@
      Money.new( -@amount, :currency => @currency, :suffixed => @suffixed )
    end

    def <=>( mon )
      other_amount = mon.to_f
      if @amount < other_amount
        -1
      elsif @amount > other_amount
        1
      else
        0
      end
    end

    def pretty( negate = false )
      if @suffixed
        (@amount >= 0 ? " " : "") + sprintf("%0.2f #{@currency}", @amount * (negate ? -1 : 1))
      else
        (@amount >= 0 ? " " : "") + sprintf("%0.2f", @amount * (negate ? -1 : 1)).gsub(/^((\-)|)(?=\d)/, "\\1#{@currency}")
      end
    end

    def Money::from_s( value, options = {} )
      # Empty string is treated as money with value 0
      return Money.new( 0.00, options ) if value.empty?

      # Remove 1000 separaters and replace , with . if comma_separates_cents
      # 1.000,00 -> 1000.00
      value = value.gsub(/\./, '').gsub(/,/, '.') if options[:comma_separates_cents]
      value = value.gsub(/,/, '')

      money_format_regex = /^(.*?)(\d+\.\d\d)/ # Money has two decimal precision
      any_number_regex = /^(.*?)([\d\.]+)/

      # Prefer matching the money_format, match any number otherwise
      m = value.match( money_format_regex ) ||
        value.match( any_number_regex )
      if m
        amount = m[2].to_f
        # Check whether the money had a - or (, which indicates negative amounts
        if (m[1].match( /^[\(-]/ ) || m[1].match( /-$/  ))
          amount *= -1
        end
        return Money.new( amount, options )
      else
        return nil
      end
    end

    def Money::likelihood( entry )
      money_score = 0
      # digits separated by , or . with no more than 2 trailing digits
      money_score += 40 if entry.match(/\d+[,.]\d{2}[^\d]*$/)
      money_score += 10 if entry[/^\$?\-?\$?\d+[\.,\d]*?[\.,]\d\d$/]
      money_score += 10 if entry[/\d+[\.,\d]*?[\.,]\d\d$/]
      money_score += entry.gsub(/[^\d\.\-\+,\(\)]/, '').length if entry.length < 7
      money_score -= entry.length if entry.length > 12
      money_score -= 20 if (entry !~ /^[\$\+\.\-,\d\(\)]+$/) && entry.length > 0
      money_score
    end
  end

  class MoneyColumn < Array
    def initialize( arr = [], options = {} )
      arr.each { |str| self.push( Money.from_s( str, options ) ) }
    end

    def positive?
      self.each do |money|
        return false if money < 0 if money
      end
      true
    end

    def merge!( other_column )
      invert = false
      invert = true if self.positive? && other_column.positive?
      self.each_with_index do |mon, i|
        other = other_column[i]
        return nil if (!mon || !other)
        if mon != 0.00 && other == 0.0
          if invert
            self[i]= -mon
          end
        elsif mon == 0.00 && other != 0.00
          self[i] = other
        else
          return nil
        end
      end
      self
    end
  end
end