package poker.domain

import poker.domain._
import scala.RandomAccessSeq.MutableProjection
import scala.collection.jcl.ArrayList
import scala.Iterable
import scala.util.Sorting
import scala.collection.Sorted

// Abstract Factory
object CombinacaoFactory {
  
  def get(cartas:List[Carta]): Combinacao = {
    var combinacao:Combinacao = null
    combinacao = new CartaMaisAlta(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new UmPar(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new DoisPares(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new Trinca(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new Sequencia(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new FullHouse(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new Flush(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new Quadra(cartas)
    if(combinacao.valida()) return combinacao
    combinacao = new SequenciaFlush(cartas)
    if(combinacao.valida()) return combinacao
    throw new RuntimeException("ops, combinação inválida.")
  }
  
  def ordena(combinacoes:List[Combinacao]): List[Combinacao] = {
    combinacoes.sort((e1, e2) => (e1 compareTo e2) <= 0)
  }
}
  
sealed abstract class Combinacao(cartas:List[Carta]) extends PilhaDeCartas(5) with Ordered[Combinacao] {
  require(cartas.length == 5)
  this ++ cartas
  this.sort()

  override def toString() = getClass().getSimpleName() + ": " + super.toString()
  
  override def equals(that: Any) = that match {
    case other:Combinacao => 
      other.getClass == getClass && (this.compare(other) == 0)
    case _ => false
  }

  def valida(): Boolean
 
  // Padrao: Pela carta mais alta
  def desempate(that:Combinacao): Int = {
    (0 to 4).foreach { i:Int =>
      var r = this.reverse(i) compare that.reverse(i)
      if(r != 0) return r
    }
    0
  }
  
  def maiorDeQuantidade(that:Combinacao, n:Int, indice:Int): Int = {
    var qtde = cartasIguais(n)(indice)
    var thatQtde = that.cartasIguais(n)(indice)
    var r = qtde.toList(0) compare thatQtde.toList(0)
    r
  }

  def todosComMesmoNaipe(cartas:Seq[Carta]): Boolean = {
    return cartas.forall { carta:Carta => carta.naipe == cartas(0).naipe }
	}
  
  def conjuntosDe(): List[Iterable[Carta]] = {
    var conjuntos = List[Iterable[Carta]]()
    var array:Iterable[Carta] = this
    
    while(!array.isEmpty) {
	    var tupla = array.partition { carta:Carta => array.toList(0).valor == carta.valor }
	    conjuntos += tupla._1
      array = tupla._2
    }
    conjuntos
  }

//  bugado: doisPares: find, nao find all, so acha 0 ou 1
//  protected def cartasIguais(qtde:Int):Int = {
//    var conjuntos = conjuntosDe()
//    conjuntos.find { cartas:Iterable[Carta] => cartas.toList.length == qtde }.toList.length
//  }

  def cartasIguais(qtde:Int):List[Iterable[Carta]] = {
    var conjuntos = conjuntosDe()
    conjuntos.filter { cartas:Iterable[Carta] =>
      cartas.toList.length == qtde 
    }.toList
  }
                                       
  def pares():Int = {
    cartasIguais(2).length
  }
  
  def trincas():Int = {
    cartasIguais(3).length
  }
  
  def quadras():Int = {
    cartasIguais(4).length
  }

  def sequencia(): Boolean = {
    var result = true
    eachCons(2, { sublista:Seq[Carta] =>
      if((sublista(0).valor+1) != sublista(1).valor) 
        result = false
    })
	  // Cuidado com 2-3-4-5-AS
    if(!result) {
      result = true
      (0 to (3+0)).foreach { i:Int => 
        if(this(i).valor != (i+2))
          result = false
      }
      if(result) result = (this(4).valor == 14)
    }
    result
  }
  
}

case class CartaMaisAlta(cartas:List[Carta]) extends Combinacao(cartas) {

  def valida(): Boolean = {
    pares() == 0 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => this.desempate(that)
      case _ => -1
    }
  }
}
 
case class UmPar(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 1 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior par
    var r = maiorDeQuantidade(that, 2, 0)
    if(r != 0) return r
    // Maior carta
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => this.desempate(that)
      case _ => -1
    }
  }
 
}

case class DoisPares(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 2 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior par
    var r = maiorDeQuantidade(that, 2, 1)
    if(r != 0) return r
    // Segundo maior par
    r = maiorDeQuantidade(that, 2, 0)
    if(r != 0) return r
    // Maior carta
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => this.desempate(that)
      case _ => -1
    }
  }
}

case class Trinca(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 0 && trincas() == 1 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior trinca
    var r = maiorDeQuantidade(that, 3, 0)

    // Precaucao para ordenar combinacoes da rodada
    // Maior carta
    if(r != 0) return r
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => this.desempate(that)
      case _ => -1
    }
  }
}

case class Sequencia(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 0 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == true
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior carta
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => 1
      case other:Sequencia => this.desempate(that)
      case _ => -1
    }
  }
}

case class Flush(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 0 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == true && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior carta
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => 1
      case other:Sequencia => 1
      case other:Flush => this.desempate(that)
      case _ => -1
    }
  }
}

case class FullHouse(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 1 && trincas() == 1 && quadras() == 0 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior trinca
    var r = maiorDeQuantidade(that, 3, 0)
    
    // Precaucao para ordenar combinacoes da rodada
    // Maior carta
    if(r != 0) return r
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => 1
      case other:Sequencia => 1
      case other:Flush => 1
      case other:FullHouse => this.desempate(that)
      case _ => -1
    }
  }
}

case class Quadra(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 0 && trincas() == 0 && quadras() == 1 && 
    todosComMesmoNaipe(this) == false && sequencia() == false
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior quadra
    var r = maiorDeQuantidade(that, 4, 0)

    // Precaucao para ordenar combinacoes da rodada
    // Maior carta
    if(r != 0) return r
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => 1
      case other:Sequencia => 1
      case other:Flush => 1
      case other:FullHouse => 1
      case other:Quadra => this.desempate(that)
      case _ => -1
    }
  }
}

case class SequenciaFlush(cartas:List[Carta]) extends Combinacao(cartas) {
  
  def valida(): Boolean = {
    pares() == 0 && trincas() == 0 && quadras() == 0 && 
    todosComMesmoNaipe(this) == true && sequencia() == true
  }
  
  override def desempate(that:Combinacao): Int = {
    // Maior carta
    super.desempate(that)
  }
  
  override def compare(that:Combinacao):Int = {
    that match {
      case other:CartaMaisAlta => 1
	    case other:UmPar => 1
      case other:DoisPares => 1
      case other:Trinca => 1
      case other:Sequencia => 1
      case other:Flush => 1
      case other:FullHouse => 1
      case other:Quadra => 1
      case other:SequenciaFlush => this.desempate(that)
      case _ => -1
    }
  }
}