// Copyright (c) 2017 PSForever
package net.psforever.objects

import akka.actor.Actor
import net.psforever.objects.ce.{Deployable, DeployableBehavior, InteractWithTurrets}
import net.psforever.objects.definition.DeployableDefinition
import net.psforever.objects.definition.converter.SmallTurretConverter
import net.psforever.objects.equipment.JammableUnit
import net.psforever.objects.guid.{GUIDTask, TaskWorkflow}
import net.psforever.objects.serverobject.affinity.FactionAffinityBehavior
import net.psforever.objects.serverobject.damage.Damageable
import net.psforever.objects.serverobject.hackable.Hackable
import net.psforever.objects.serverobject.mount.InteractWithRadiationCloudsSeatedInEntity
import net.psforever.objects.serverobject.turret.auto.{AffectedByAutomaticTurretFire, AutomatedTurret}
import net.psforever.objects.serverobject.turret.{TurretControl, TurretDefinition, WeaponTurret}
import net.psforever.objects.sourcing.{PlayerSource, SourceEntry}
import net.psforever.objects.vital.damage.DamageCalculations
import net.psforever.objects.vital.interaction.DamageResult
import net.psforever.objects.vital.resistance.StandardResistanceProfile
import net.psforever.objects.vital.{SimpleResolutions, StandardVehicleResistance}
import net.psforever.objects.zones.InteractsWithZone
import net.psforever.packet.game.TriggeredSound
import net.psforever.services.vehicle.{VehicleAction, VehicleServiceMessage}

import scala.concurrent.duration.FiniteDuration

class TurretDeployable(tdef: TurretDeployableDefinition)
  extends Deployable(tdef)
    with AutomatedTurret
    with WeaponTurret
    with JammableUnit
    with InteractsWithZone
    with StandardResistanceProfile
    with Hackable {
  HackSound = TriggeredSound.HackVehicle
  HackDuration = Array(0, 20, 10, 5)

  if (tdef.Seats.nonEmpty) {
    interaction(new InteractWithTurrets())
    interaction(new InteractWithRadiationCloudsSeatedInEntity(obj = this, range = 100f))
  }
  WeaponTurret.LoadDefinition(turret = this)

  def TurretOwner: SourceEntry = {
    Seats
      .values
      .headOption
      .flatMap(_.occupant)
      .map(p => PlayerSource.inSeat(PlayerSource(p), SourceEntry(this), seatNumber=0))
      .orElse(Owners.map(PlayerSource(_, Position)))
      .getOrElse(SourceEntry(this))
  }

  override def MaxShields: Int = Definition.MaxShields

  override def Definition: TurretDeployableDefinition = tdef
}

abstract class TurretDeployableDefinition(private val objectId: Int)
    extends DeployableDefinition(objectId)
    with TurretDefinition {
  Name = "turret_deployable"
  Packet = new SmallTurretConverter
  DamageUsing = DamageCalculations.AgainstVehicle
  ResistUsing = StandardVehicleResistance
  Model = SimpleResolutions.calculate

  //override to clarify inheritance conflict
  override def MaxHealth: Int = super[DeployableDefinition].MaxHealth
  //override to clarify inheritance conflict
  override def MaxHealth_=(max: Int): Int = super[DeployableDefinition].MaxHealth_=(max)
}

/** control actors */

abstract class TurretDeployableControl
    extends Actor
    with DeployableBehavior
    with FactionAffinityBehavior.Check
    with TurretControl
    with AffectedByAutomaticTurretFire {

  override def postStop(): Unit = {
    super.postStop()
    deployableBehaviorPostStop()
  }

  override def commonBehavior: Receive =
    super.commonBehavior
      .orElse(deployableBehavior)
      .orElse(checkBehavior)
      .orElse(takeAutomatedDamage)

  override protected def DestructionAwareness(target: Damageable.Target, cause: DamageResult): Unit = {
    super.DestructionAwareness(target, cause)
    Deployables.AnnounceDestroyDeployable(DeployableObject, None)
  }

  override def deconstructDeployable(time: Option[FiniteDuration]) : Unit = {
    val zone = TurretObject.Zone
    val seats = TurretObject.Seats.values
    //either we have no seats or no one gets to sit
    val retime = if (seats.count(_.isOccupied) > 0) {
      //it's possible to request deconstruction of one's own field turret while seated in it
      val wasKickedByDriver = false
      seats.foreach { seat =>
        seat.occupant.collect {
          case player: Player =>
            seat.unmount(player)
            player.VehicleSeated = None
            zone.VehicleEvents ! VehicleServiceMessage(
              zone.id,
              VehicleAction.KickPassenger(player.GUID, 4, wasKickedByDriver, TurretObject.GUID)
            )
        }
      }
      Some(time.getOrElse(Deployable.cleanup) + Deployable.cleanup)
    } else {
      time
    }
    super.deconstructDeployable(retime)
  }

  override def unregisterDeployable(obj: Deployable): Unit = {
    val zone = obj.Zone
    TaskWorkflow.execute(GUIDTask.unregisterDeployableTurret(zone.GUID, TurretObject))
  }
}
