package fmon.stat

import scala.util._

import fmon._
import fmon.battle.msg._
import fmon.util._

import Monster._
import Statistic._

class Monster(val base : StorageMon) {
  def level = base.level
  val stats = Statistic.buildMap(computeStat)
  var boosts = Statistic.buildMap(_ => 0)
  var hp = stats(Hp)
  def maxhp = stats(Hp)
  
  var status : Option[Status] = None
  var volatile = Seq[Status]()
  
  def isAlive = hp > 0
  
  def apply(s : Stat) = {
    val mod = boosts.getOrElse(s, 0)
    val mult = if (mod > 0) Fraction(2 + mod, 2) else Fraction(2, 2 - mod)
    status.foldLeft(1.frac)((m, sts) => m * sts.onModifyStat(this, s)) * mult * stats(s)
  }
  
  def elements = base.form.elements
  
  
  def takeDamage(dmg : Int) : Int = {
    val actualDmg = Math.min(hp, dmg)
    hp -= actualDmg
    actualDmg
  }
  
  def recoverDamage(healing : Int) {
    hp = Math.min(stats(Hp), hp + healing)
  }
  
  def statuses = volatile ++ status
  
  def cureStatus()(implicit reader: SignalConsumer, rng: Random) {
    status.map(_.onEnd(this))
    status = None
  }
  
  def addStatus(s : Status, source: EffectSource)(implicit reader: SignalConsumer, rng: Random) = {
    if (s.effectType == EffectType.Volatile && !volatile.exists(_.name == s.name)) {
      volatile +:= s
      s.onStart(this, source)
    } else if (s.effectType == EffectType.NonVolatile && status == None) {
      status = Some(s)
      s.onStart(this, source)
    } else {
      reader ! Message("But it failed!")
    }
  }
  
  def -=(s : Status)(implicit reader: SignalConsumer, rng: Random) = {
    if (s.effectType == EffectType.Volatile) {
      s.onEnd(this)
      volatile = volatile.filterNot(_.name == s.name)
    } else {
      cureStatus()
    }
  }
  
  def applyBoost(s : Stat, boost : Int, effect: EffectSource)(implicit reader: SignalConsumer, rng: Random) {
    val modified = boosts.getOrElse(s, 0) + boost
    boosts = boosts.updated(s, Math.min(Config.maxBoost, Math.max(-Config.maxBoost, modified)))
    if (boost > 0) {
      reader ! Message(s"$this's $s rose!")
    } else if (boost < 0) {
      reader ! Message(s"$this's $s fell!")
    }
  }
  
  def effectiveness(element : Element) : Double = {
    elements.foldLeft(1.0)((m, e) => m * (element --> e))
  }
  
  private def computeStat(s : Stat) : Int = {
    val num = 2 * base.form.baseStats(s) + base.gene.ivs(s) + base.evs(s) / 4
    val frac = num * level / 100
    val score = if(s == Hp) frac + 10 + level else frac + 5
    (score * base.gene.nature(s)).toInt
  }
  
  def name = if (base.nickname != null) base.nickname else base.form.name
  override def toString = name
}

object Monster {
  def generate(nickname: String, ot: TrainerID, xp: Int, form: Form, moves: IndexedSeq[Move])(implicit rng: Random) = {
    new Monster(StorageMon.generate(nickname, ot, xp, form, moves))
  }
}

class MonsterPtr(var mon : Monster) {
  override def toString : String = mon.toString()
}