package poker.domain

import poker.domain._
import poker.domain.Naipe._
import poker.domain.Valor._
import org.testng.annotations._
import org.junit.Assert._
import org.easymock.EasyMock._

class PilhaDeCartasTest {

  @Test
  def equals() {
    var pilha1 = new PilhaDeCartas(2)
    var pilha2 = new PilhaDeCartas(2)
    assertTrue(pilha1 == pilha2)
    assertTrue(pilha1.hashCode == pilha2.hashCode)
    pilha1.push(new Carta(AS, PAUS))
    assertTrue(pilha1 != pilha2)
    pilha2.push(new Carta(AS, PAUS))
    assertTrue(pilha1 == pilha2)
    assertTrue(pilha1.hashCode == pilha2.hashCode)
  }
  
  @Test
  def quantidadeDeCartas() {
    var pilha = new PilhaDeCartas(3)
    assertEquals(0, pilha.quantidadeDeCartas())
    
    pilha.push(new Carta(AS, ESPADAS))
    assertEquals(1, pilha.quantidadeDeCartas())
    
    pilha.push(new Carta(AS, PAUS))
    assertEquals(2, pilha.quantidadeDeCartas())
    
    pilha.pop()
    assertEquals(1, pilha.quantidadeDeCartas())
  }
  
  @Test
  def aceitaCartasRepetidas() {
    var pilha = new PilhaDeCartas(3)
    assertEquals(0, pilha.quantidadeDeCartas())
    
    pilha.push(new Carta(AS, ESPADAS))
    assertEquals(1, pilha.quantidadeDeCartas())
    
    pilha.push(new Carta(AS, ESPADAS))
    assertEquals(2, pilha.quantidadeDeCartas())
  }
  
  @Test
  def getCarta() {
    var pilha = new PilhaDeCartas(3)
    pilha.push(new Carta(AS, ESPADAS))
    assertEquals(new Carta(AS, ESPADAS), pilha(0))
    // testa que nao faz um pop
    assertEquals(new Carta(AS, ESPADAS), pilha(0))
    
    pilha.push(new Carta(AS, PAUS))
    assertEquals(new Carta(AS, ESPADAS), pilha(0))
    assertEquals(new Carta(AS, PAUS), pilha(1))
  }
  
  @Test
  def pilhaLancaExcecaoSeTentarUltrapassarLimiteDeCartasDefinido() {
    var pilha = new PilhaDeCartas(3)
    pilha.push(new Carta(AS, ESPADAS))
    pilha.push(new Carta(AS, ESPADAS))
    pilha.push(new Carta(AS, ESPADAS))
    try {
      pilha.push(new Carta(AS, ESPADAS))
    } catch {
      case e:RuntimeException => assertTrue(true)
      case _ => fail("Ops")
    }
  }
  
  @Test
  def sortComArrayEmbaralhado() {
    var pilha = new PilhaDeCartas(3)
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(2, PAUS))
    pilha.push(new Carta(4, PAUS))
    pilha.sort()
    (0 to 2).foreach { i:Int => assertEquals(2+i, pilha(i).valor) }
  }
  
  @Test
  def sortComArrayOrdenado() {
    var pilha = new PilhaDeCartas(3)
    pilha.push(new Carta(2, PAUS))
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(4, PAUS))
    pilha.sort()
    (0 to 2).foreach { i:Int => assertEquals(2+i, pilha(i).valor) }
  }
  
  @Test
  def sortComArrayOrdenadoInversamente() {
    var pilha = new PilhaDeCartas(3)
    pilha.push(new Carta(4, PAUS))
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(2, PAUS))
    pilha.sort()
    (0 to 2).foreach { i:Int => assertEquals(2+i, pilha(i).valor) }
  }
  
  @Test
  def embaralhaAleatoriamente() {
    (1 to 100).foreach { i:Int => testaEmbaralharAleatoriamente() }
  }
    
  private def testaEmbaralharAleatoriamente() {
    // Cria um baralho com as cartas ordenadas por sequência e naipe
    var baralho = new Baralho()
    
    // Verifica quantidade de cartas
    assertEquals(52, baralho.quantidadeDeCartas())
    
    // Verifica que tem 5 cartas em sequência
    var result = true
    (1 to 5).foreach { i:Int => result = result && (baralho(i).valor == baralho(i+1).valor-1) }
    assertTrue("Ops, esta embaralhado por valor: " + baralho, result)
    
    // Verifica que tem 5 cartas seguidas com mesmo naipe
    result = true
    (1 to 5).foreach { i:Int => result = result && (baralho(i).naipe == baralho(i+1).naipe) }
    assertTrue("Ops, esta embaralhado por naipe: " + baralho, result)
    
    // Executa
    baralho.embaralha()

    // Verifica que a quantidade de cartas não foi alterada 
    assertEquals(52, baralho.quantidadeDeCartas())
    
    // Verifica não está bem embaralhado: 5 cartas em sequência
    result = true
    (1 to 5).foreach { i:Int => result = result && (baralho(i).valor == baralho(i+1).valor-1) }
    assertFalse("Ops, nao esta embaralhado por valor: " + baralho, result)
    
    // Verifica não está bem embaralhado: 5 cartas seguidas com mesmo naipe
    result = true
    (1 to 5).foreach { i:Int => result = result && (baralho(i).naipe == baralho(i+1).naipe) }
    assertFalse("Ops, nao esta embaralhado por naipe: " + baralho, result)
  }
  
//    var mock = createStrictMock(classOf[])
//    pilha.foreach {
//      expect(mock.)
//    }
  @Test
  def eachCons1() {
    var pilha = new PilhaDeCartas(5)
    pilha.push(new Carta(AS, PAUS))
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(6, PAUS))
    pilha.push(new Carta(8, PAUS))
    pilha.push(new Carta(K, PAUS))
   
    var i = 0
    pilha.eachCons(1, { sublista:Seq[Carta] =>
      assertEquals(1, sublista.length)
      assertEquals(pilha(i), sublista(0))
      i += 1
    })
    assertEquals(5, i)
  }
  
  @Test
  def eachCons3() {
    var pilha = new PilhaDeCartas(5)
    pilha.push(new Carta(AS, PAUS))
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(6, PAUS))
    pilha.push(new Carta(8, PAUS))
    pilha.push(new Carta(K, PAUS))
   
    var i = 0
    pilha.eachCons(3, { sublista:Seq[Carta] =>
      assertEquals(3, sublista.length)
      assertEquals(pilha(i), sublista(0))
      assertEquals(pilha(i+1), sublista(1))
      assertEquals(pilha(i+2), sublista(2))
      i += 1
    })
  }
  
  @Test
  def eachCons5() {
    var pilha = new PilhaDeCartas(5)
    pilha.push(new Carta(AS, PAUS))
    pilha.push(new Carta(3, PAUS))
    pilha.push(new Carta(6, PAUS))
    pilha.push(new Carta(8, PAUS))
    pilha.push(new Carta(K, PAUS))
   
    var i = 0
    pilha.eachCons(5, { sublista:Seq[Carta] =>
      assertEquals(5, sublista.length)
      assertEquals(pilha(i), sublista(0))
      assertEquals(pilha(i+1), sublista(1))
      assertEquals(pilha(i+2), sublista(2))
      assertEquals(pilha(i+3), sublista(3))
      assertEquals(pilha(i+4), sublista(4))
      i += 1
    })
  }
  
  @Test
  def eachConsAceitaRetornoGenerico() {
    var pilha = new PilhaDeCartas(1)
    pilha.eachCons[Boolean](1, { sublista:Seq[Carta] => 
      true
    })
  }
  
  @Test
  def eachConsInvalidThrowsAnException() {
    var pilha = new PilhaDeCartas(2)
    pilha.push(new Carta(AS, PAUS))
    pilha.push(new Carta(3, PAUS))
    try {
	    pilha.eachCons[Boolean](3, { sublista:Seq[Carta] =>
	      true
      })
    } catch {
      case e:RuntimeException => assertTrue(true)
      case _ => fail("Ops")
    }
  }
  
}
