require File.expand_path(File.dirname(__FILE__) + '/test_helper')

class DinheiroTest < Test::Unit::TestCase

  CONTABIL = { "(2,00)" =>    -2,
                 "2,00" =>     2,    
                 "0,00" =>     0,
                 "0,32" =>  0.32,
               "(0,01)" => -0.01 }
  
  REAL_CONTABIL = { "R$ (1,00)" => -1,
                    "R$ (0,12)" => -0.12,
                      "R$ 1,00" => 1,
                      "R$ 1,00" => 1,
                      "R$ 1,00" => 1,
                      "R$ 0,00" => 0 }

  SOMA = {      0.real =>    0.real + 0.real,
                1.real =>    0.real + 1.real,
                1.real =>    1.real + 0.real,
               2.reais =>    1.real + 1.real,
               2.reais =>   0.real + 2.reais,
               2.reais =>   2.reais + 0.real,
                0.real =>   2.real + -2.real,
                0.real =>   0.real + BigDecimal.new("0"),
              0.3.real =>  0.real + 0.3.real,
             -0.3.real => 0.real + -0.3.real,
            -0.03.real =>     0 + -0.03.real,
            -0.03.real =>     0.real + -0.03,   
            -1.03.real =>    -1.real + -0.03,
            -1.03.real =>    -1.real + BigDecimal.new("-0.03") }

  SUBTRACAO = {     0.real =>       0.real - 0.real,
                   -1.real =>       0.real - 1.real,
                    1.real =>       1.real - 0.real,
                    0.real =>       1.real - 1.real,
                  -2.reais =>      0.real - 2.reais,
                   2.reais =>      2.reais - 0.real,
                  -4.reais =>    -2.reais - 2.reais,
                  0.3.real =>     0.3.real - 0.real,
                 0.03.real =>    0.03.real - 0.real,
                 0.03.real => 0.06.real - 0.03.real,
                -0.03.real =>         0 - 0.03.real,
                -0.03.real =>         0.real - 0.03,    
                -1.03.real =>        -1.real - 0.03, 
                -1.03.real =>        -1.real - BigDecimal.new("0.03") }
                
  MULTIPLICACAO = {    0.real =>        0.real * 0,
                       0.real =>        0.real * 1,
                       0.real =>       0.real * -1,
                       1.real =>        1.real * 1,
                       1.real =>      -1.real * -1,
                      -1.real =>       1.real * -1,
                      -1.real =>       -1.real * 1,
                     0.1.real =>      1.real * 0.1,
                    0.01.real =>     1.real * 0.01,
                    0.01.real =>    1.real * 0.009,
                    0.01.real =>    1.real * 0.005,
                    0.00.real =>   1.real * 0.0049,
                     0.1.real =>      0.1.real * 1,
                    0.01.real =>     0.01.real * 1,
                    0.01.real =>    0.009.real * 1,
                    0.00.real =>  0.00049.real * 1,
                       0.real =>   0.real * 0.real,
                       0.real =>   0.real * BigDecimal("0"),
                       1.real =>   1.real * 1.real,
                       1.real =>   1.real * BigDecimal("1"),
                       1.real => 0.5.real * 2.real,
                       1.real => 0.5.real * BigDecimal("2"),
                       1.real =>        1 * 1.real,
                      -1.real =>       -1 * 1.real,
                       1.real =>      -1 * -1.real,
                    0.01.real =>     0.01 * 1.real,
                    0.01.real =>   1.real * BigDecimal("0.01"),
                    0.01.real =>   BigDecimal("0.01") * 1.real }
                    
  DIVISAO = { 
            [Dinheiro.new(0.33), Dinheiro.new(0.33), Dinheiro.new(0.34)] =>    Dinheiro.new(1) / 3,
         [Dinheiro.new(33.33), Dinheiro.new(33.33), Dinheiro.new(33.34)] =>  Dinheiro.new(100) / 3,
                                 [Dinheiro.new(50.00), Dinheiro.new(50)] =>  Dinheiro.new(100) / 2,
                                [Dinheiro.new(0.25), Dinheiro.new(0.25)] =>  Dinheiro.new(0.5) / 2,
             [Dinheiro.new(0.16), Dinheiro.new(0.16),Dinheiro.new(0.18)] =>  Dinheiro.new(0.5) / 3,
                                                    [Dinheiro.new(0.33)] => Dinheiro.new(0.33) / 1 ,
                                                    [Dinheiro.new(0.33)] => Dinheiro.new(0.33) / 1 ,
                                                    }                    
                    
                    
  QUANTIA_COM_FORMATO_VALIDO =  [                   "1211",
                                                   "1211.",
                                                  "1211.0",
                                                 "1211.23",
                                                 "1211,23",
                                                   "1.211",
                                                "1.211,00",
                                                "1.211,01",
                                                 "1.211,1",
                                                  "1.211,",
                                                      "1,",
                                                     "12,",
                                         "32349898989912,",
                                     "32.349.898.989.912,",
                                    "32.349.898.989.912,1",
                                   "32.349.898.989.912,12",
                                                       "1",
                                                    "1.00",
                                                    "1.01",
                                                     "1.1",
                                                      "1.",
                                                      ".1",
                                                     ".12",
                                                    "0.12",
                                                    "1.12",
                                                   "12.12",
                                                   "12.12",
                                                  "123.12",
                                                "1,234.12",
                                               "12,234.12",
                                              "123,234.12",
                                            "2,123,234.12",
                                                      ",1",
                                                     ",11",
                                                     ",01",
                                                    "0,01" ]
  QUANTIA_COM_FORMATO_INVALIDO = [    'teste',
                                   '12,123,99',
                                   '12.123.99',
                                    '1,123,99',
                                   '1212,39.90' ]
                                                    
  COMPARACAO = [  1.real < 2.reais,
                  1.real <= 2.reais,
                  2.real  >  1.real,
                  2.real >=  1.real,
                  1.real ==  1.real,
                  1.real >=  1.real,
                  1.real <=  1.real ]

  COMPARACAO_COM_ESCALAR = [  1.real  < 2.00,
                              1.real <= 2.00,
                              2.real  > 1.00,
                              2.real >= 1.00,
                              1.real == 1.00,
                              1.real >= 1.00,
                              1.real <= 1.00 ]
                                                      
  ARREDONDAMENTO =  { 23049 =>         230.49, 
                      23049 => 230.4949999999,
                      23050 =>        230.495 }
  
  PONTO_NO_MILHAR = {  "234.175.211"  =>   "234175211",
                                 ""   =>            "",
                                "1"   =>           "1",
                               "12"   =>          "12",
                              "123"   =>         "123",
                            "1.234"   =>        "1234",
                           "12.345"   =>       "12345",
                          "123.456"   =>      "123456",
                      "123.112.211"   =>   "123112211",
                        "1.234.567"   =>     "1234567" }

  QUANTIA_VALIDA = {       "0,00"  => 	           0  ,
                           "0,00"  =>	           0.0  ,
                           "0,00"  =>	            "0" ,
                           "0,00"  =>	         "0,00" ,
                           "1,00"  =>	             1  ,
                           "1,03"  =>	          1.03  ,
                           "1,03"  =>	         "1,03" ,
                           "0,03"  =>	          ",03" ,
                           "0,30"  =>	           ",3" ,
                           "0,03"  =>	          ".03" ,
                           "0,30"  =>	           ".3" ,
                          "-0,30"  =>	          -0.3  ,
                          "-0,03"  =>	         -0.03  ,
                           "1,00"  =>	         "1,00" ,
                          "-1,00"  =>	            -1  ,
                          "-1,00"  =>	          -1.0  ,
                          "-1,00"  =>	           "-1" ,
                          "-1,00"  =>	        "-1,00" ,
                          "-2,30"  =>	        "-2,30" ,
                           "2,30"  =>	         "2,30" ,
                           "2,30"  =>	          2.30  ,
                           "2,30"  =>	           2.3  ,
                       "1.211,00"  =>      	"1211,00" , 
                       "1.211,01"  =>       "1211,01" , 
                       "1.211,50"  =>        "1211,5" , 
                       "1.211,00"  =>	        "1211," , 
                       "1.211,00"  =>	         "1211" ,
                       "1.211,00"  =>	      "1211.00" ,      
                       "1.211,01"  =>	      "1211.01" ,      
                       "1.211,20"  =>	       "1211.2" ,      
                       "1.211,00"  =>	        "1211." ,
                       "1.211,00"  =>	         "1211" ,
                       "1.211,00"  =>	        "1.211" ,
                 "123.112.211,35"  =>	 "123112211,35" ,      
                "-123.112.211,35"  =>	"-123112211,35" ,          
                 "123.112.211,35"  =>	"+123112211,35" }
  
  PARTE_INTEIRA = [ -1, -123112211, 0, 1, 12344545 ]
  
  
  def setup
    tornar_metodos_publicos Dinheiro
    @dinheiro = 1.real
  end

  def testa_se_cria_dinheiro_a_partir_de_quantias_validos
    QUANTIA_VALIDA.each do |esperado, quantia| 
      assert_equal esperado, Dinheiro.new(quantia).to_s, "Deveria ter vindo o quantia: #{esperado} quando recebeu #{quantia}"
    end
  end
  
  # coloca_ponto_no_milhar
  def testa_se_coloca_ponto_no_milhar
    PONTO_NO_MILHAR.each do |esperado, quantia| 
      { esperado       =>     quantia,
        "-#{esperado}" => "-#{quantia}" }.each do |esperado, quantia| 
          assert_equal esperado, @dinheiro.inteiro_com_milhar(quantia) 
      end
    end
  end
  
  # real
  def testa_real
    assert_equal "R$ 1,00", Dinheiro.new(1).real
  end
  
  def testa_real_contabil
    REAL_CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).real_contabil }
  end  
  
  def testa_reais_contabeis
    REAL_CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).reais_contabeis }
  end  
  
  # reais
  def testa_reais
    assert_equal "R$ 2,00", Dinheiro.new(2).reais
  end
  
  def testa_contabil
    CONTABIL.each { |esperado, quantia| assert_equal esperado, Dinheiro.new(quantia).contabil }
  end  
  
  # ==
  def testa_igualdade
    assert_equal Dinheiro.new(1), Dinheiro.new(1)
  end
  
  def testa_igualdade_quando_passa_possivel_dinheiro
    assert_equal Dinheiro.new(1), 1.0
  end
  
  # / (divisao/parcelamento)
  def testa_divisao
    DIVISAO.each { |parcelas, divisao| assert_equal parcelas, divisao }
  end
  
  def testa_divisao_por_zero
    assert_raise(ZeroDivisionError) { 1.real / 0 }
  end
  
  def testa_divisao_por_algo_que_nao_seja_um_escalar
    assert_raise(DivisaPorNaoEscalarError) { 10.reais / 2.reais }
  end
  
  # initialize
  def testa_se_cria_dinheiro_a_partir_de_float
    verifica_se_cria_dinheiro_para 1.0
  end 
  
  def testa_se_cria_dinheiro_a_partir_de_fixnum
    verifica_se_cria_dinheiro_para 1
  end 
  
  def testa_se_cria_dinheiro_a_partir_de_bigdecimal
    verifica_se_cria_dinheiro_para BigDecimal.new("1")
  end 

  def testa_se_cria_dinheiro_a_partir_de_string
    verifica_se_cria_dinheiro_para "1"
  end 
  
  def testa_se_rejeita_criacao_de_dinheiro_a_partir_de_string_invalida
    QUANTIA_COM_FORMATO_INVALIDO.each do |quantia| 
      assert_raise DinheiroInvalidoError, "Deveria ter rejeitado [#{quantia}]" do
        Dinheiro.new quantia
      end
    end
  end
  
  # + (soma)
  def testa_soma
    SOMA.each{ |esperado, soma| assert_equal esperado, soma }
  end  
  
  # - (subtracao)
  def testa_subtracao
    SUBTRACAO.each { |esperado, subtracao| assert_equal esperado, subtracao }
  end
  
  # * (multiplicacao)
  def testa_multiplicacao
    MULTIPLICACAO.each { |esperado, multiplicacao| assert_equal esperado, multiplicacao }  
  end
  
  # quantia_de
  def testa_quantia_de
    assert_equal 0, @dinheiro.quantia_de(0.real)
    assert_equal 0, @dinheiro.quantia_de(0)
  end

  #por_extenso
  def testa_por_extenso
    assert_equal 'um real', 1.real.por_extenso
    assert_equal 'um centavo', (0.01).real.por_extenso
    assert_equal 'cem reais', 100.real.por_extenso
    assert_equal 'cem reais e um centavo', (100.01).real.por_extenso
  end

  #por_extenso_em_reais
  def testa_por_extenso
    assert_equal 'um real', 1.real.por_extenso_em_reais
    assert_equal 'um centavo', (0.01).real.por_extenso_em_reais
    assert_equal 'cem reais', 100.real.por_extenso_em_reais
    assert_equal 'cem reais e um centavo', (100.01).real.por_extenso_em_reais
  end

  # to_f
  def testa_to_f
    assert_equal 2.30, 2.30.real.to_f
  end
  
  # quantia_respeita_formato?
  def testa_se_quantia_respeita_formato
    QUANTIA_COM_FORMATO_VALIDO.each { |quantia| verifica_se_quantia_respeita_formato quantia }
  end
  
  # >, <, == (ordenacao)
  def testa_comparacao_entre_dinheiro
    COMPARACAO.each { |comparacao| assert comparacao }
  end
  
  def testa_comparacao_entre_possivel_dinheiro
    COMPARACAO_COM_ESCALAR.each { |comparacao_com_escalar| assert comparacao_com_escalar }
  end
  
  # decimal
  def testa_decimal_quando_todas_as_casas_estao_preenchidas
    verifica_decimal("12")
  end
  
  def testa_decimal_quando_apenas_uma_das_casas_esta_preenchida
    verifica_decimal("10", "1")
  end
  
  def testa_decimal_quando_nenhuma_das_casas_esta_preenchida
    verifica_decimal("00", "")
  end
  
  def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_tres_digitos
    verifica_se_transforma_em_string_corretamente "1.23", 123
  end

  def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_dois_digitos    
    verifica_se_transforma_em_string_corretamente "0.12", 12
  end

  def testa_se_transforma_em_string_que_represente_a_quantia_quando_tem_um_digito
    verifica_se_transforma_em_string_corretamente "0.03", 3
  end

  def testa_se_transforma_em_string_para_valores_especiais
    verifica_se_transforma_em_string_corretamente "-123112211.00", -12311221100          
  end

  # parte_inteira
  def testa_parte_inteira
    PARTE_INTEIRA.each { |quantia| assert_equal "#{quantia}", Dinheiro.new(quantia).parte_inteira }
  end
  
  def testa_se_arredonda_valores_corretamente
    ARREDONDAMENTO.each do |esperado, quantia| 
      assert_equal esperado, Dinheiro.new(quantia).quantia, "Deveria retornar #{esperado} para #{quantia}"
    end
  end
  
  def testa_se_valor_decimal_cria_o_big_decimal_corretamente
    assert_equal BigDecimal.new("1234.56"), Dinheiro.new("1234,56").valor_decimal
  end

  def testa_soma_de_dinheiro_com_big_decimal
    assert_equal Dinheiro.new(200), BigDecimal.new("100").reais + "100".reais
  end
  
  private
  
  def verifica_se_transforma_em_string_corretamente(quantia_esperada, quantia)
    assert_equal quantia_esperada, @dinheiro.transforma_em_string_que_represente_a_quantia(quantia)
  end
  
  def verifica_decimal(esperado, quantia = esperado)
    assert_equal esperado, @dinheiro.decimal("." + quantia)
    assert_equal esperado, @dinheiro.decimal("," + quantia)
    assert_equal esperado, @dinheiro.decimal(quantia) if quantia.blank?
  end
  
  def verifica_se_quantia_respeita_formato(quantia)
    formatos_validos(quantia).each do |quantia_str| 
      assert 1.real.quantia_respeita_formato?(quantia_str), "O sistema deveria considerar o quantia '#{quantia_str}' dentro do formato valido."
    end
  end
  
  def formatos_validos(quantia)
    formatos_validos = []
    quantias_validas(quantia).each do |quantia|
      formatos_validos << quantia
      [ "R$", "r$" ].each do |simbolo|
        [ "", " ", "     " ].each do |espacos|
          formatos_validos << "#{simbolo}#{espacos}#{quantia}"
        end
      end
    end
    formatos_validos
  end
  
  def quantias_validas(quantia)
    return [quantia] if [ ".", "," ].include?(quantia[0..0])
    [ quantia, "-#{quantia}" ]
  end
  
  def verifica_se_cria_dinheiro_para(quantia)
    assert quantia.para_dinheiro.kind_of?(Dinheiro)
  end
  
end
