<?php

namespace Aowow;

if (!defined('AOWOW_REVISION'))
    die('illegal access');


// TrinityCore - SmartAI
class SmartEvent
{
    use SmartHelper;

    public const EVENT_UPDATE_IC               = 0;         // In combat.
    public const EVENT_UPDATE_OOC              = 1;         // Out of combat.
    public const EVENT_HEALTH_PCT              = 2;         // Health Percentage
    public const EVENT_MANA_PCT                = 3;         // Mana Percentage
    public const EVENT_AGGRO                   = 4;         // On Creature Aggro
    public const EVENT_KILL                    = 5;         // On Creature Kill
    public const EVENT_DEATH                   = 6;         // On Creature Death
    public const EVENT_EVADE                   = 7;         // On Creature Evade Attack
    public const EVENT_SPELLHIT                = 8;         // On Creature/Gameobject Spell Hit
    public const EVENT_RANGE                   = 9;         // On Target In Range
    public const EVENT_OOC_LOS                 = 10;        // On Target In Distance Out of Combat
    public const EVENT_RESPAWN                 = 11;        // On Creature/Gameobject Respawn
    public const EVENT_TARGET_HEALTH_PCT       = 12;        // [DEPRECATED] On Target Health Percentage
    public const EVENT_VICTIM_CASTING          = 13;        // On Target Casting Spell
    public const EVENT_FRIENDLY_HEALTH         = 14;        // [DEPRECATED] On Friendly Health Deficit
    public const EVENT_FRIENDLY_IS_CC          = 15;        //
    public const EVENT_FRIENDLY_MISSING_BUFF   = 16;        // On Friendly Lost Buff
    public const EVENT_SUMMONED_UNIT           = 17;        // On Creature/Gameobject Summoned Unit
    public const EVENT_TARGET_MANA_PCT         = 18;        // [DEPRECATED] On Target Mana Percentage
    public const EVENT_ACCEPTED_QUEST          = 19;        // On Target Accepted Quest
    public const EVENT_REWARD_QUEST            = 20;        // On Target Rewarded Quest
    public const EVENT_REACHED_HOME            = 21;        // On Creature Reached Home
    public const EVENT_RECEIVE_EMOTE           = 22;        // On Receive Emote.
    public const EVENT_HAS_AURA                = 23;        // On Creature Has Aura
    public const EVENT_TARGET_BUFFED           = 24;        // On Target Buffed With Spell
    public const EVENT_RESET                   = 25;        // After Combat, On Respawn or Spawn
    public const EVENT_IC_LOS                  = 26;        // On Target In Distance In Combat
    public const EVENT_PASSENGER_BOARDED       = 27;        //
    public const EVENT_PASSENGER_REMOVED       = 28;        //
    public const EVENT_CHARMED                 = 29;        // On Creature Charmed
    public const EVENT_CHARMED_TARGET          = 30;        // [DEPRECATED] On Target Charmed
    public const EVENT_SPELLHIT_TARGET         = 31;        // On Target Spell Hit
    public const EVENT_DAMAGED                 = 32;        // On Creature Damaged
    public const EVENT_DAMAGED_TARGET          = 33;        // On Target Damaged
    public const EVENT_MOVEMENTINFORM          = 34;        // WAYPOINT_MOTION_TYPE = 2,  POINT_MOTION_TYPE = 8
    public const EVENT_SUMMON_DESPAWNED        = 35;        // On Summoned Unit Despawned
    public const EVENT_CORPSE_REMOVED          = 36;        // On Creature Corpse Removed
    public const EVENT_AI_INIT                 = 37;        //
    public const EVENT_DATA_SET                = 38;        // On Creature/Gameobject Data Set, Can be used with SMART_ACTION_SET_DATA
    public const EVENT_WAYPOINT_START          = 39;        // [DEPRECATED] On Creature Waypoint ID Started
    public const EVENT_WAYPOINT_REACHED        = 40;        // On Creature Waypoint ID Reached
    public const EVENT_TRANSPORT_ADDPLAYER     = 41;        // [RESERVED]
    public const EVENT_TRANSPORT_ADDCREATURE   = 42;        // [RESERVED]
    public const EVENT_TRANSPORT_REMOVE_PLAYER = 43;        // [RESERVED]
    public const EVENT_TRANSPORT_RELOCATE      = 44;        // [RESERVED]
    public const EVENT_INSTANCE_PLAYER_ENTER   = 45;        // [RESERVED]
    public const EVENT_AREATRIGGER_ONTRIGGER   = 46;        //
    public const EVENT_QUEST_ACCEPTED          = 47;        // [RESERVED] On Target Quest Accepted
    public const EVENT_QUEST_OBJ_COMPLETION    = 48;        // [RESERVED] On Target Quest Objective Completed
    public const EVENT_QUEST_COMPLETION        = 49;        // [RESERVED] On Target Quest Completed
    public const EVENT_QUEST_REWARDED          = 50;        // [RESERVED] On Target Quest Rewarded
    public const EVENT_QUEST_FAIL              = 51;        // [RESERVED] On Target Quest Field
    public const EVENT_TEXT_OVER               = 52;        // On TEXT_OVER Event Triggered After SMART_ACTION_TALK
    public const EVENT_RECEIVE_HEAL            = 53;        // On Creature Received Healing
    public const EVENT_JUST_SUMMONED           = 54;        // On Creature Just spawned
    public const EVENT_WAYPOINT_PAUSED         = 55;        // On Creature Paused at Waypoint ID
    public const EVENT_WAYPOINT_RESUMED        = 56;        // On Creature Resumed after Waypoint ID
    public const EVENT_WAYPOINT_STOPPED        = 57;        // On Creature Stopped On Waypoint ID
    public const EVENT_WAYPOINT_ENDED          = 58;        // On Creature Waypoint Path Ended
    public const EVENT_TIMED_EVENT_TRIGGERED   = 59;        //
    public const EVENT_UPDATE                  = 60;        //
    public const EVENT_LINK                    = 61;        // Used to link together multiple events as a chain of events.
    public const EVENT_GOSSIP_SELECT           = 62;        // On gossip clicked (gossip_menu_option335).
    public const EVENT_JUST_CREATED            = 63;        //
    public const EVENT_GOSSIP_HELLO            = 64;        // On Right-Click Creature/Gameobject that have gossip enabled.
    public const EVENT_FOLLOW_COMPLETED        = 65;        //
    public const EVENT_EVENT_PHASE_CHANGE      = 66;        // [DEPRECATED] On event phase mask set
    public const EVENT_IS_BEHIND_TARGET        = 67;        // [DEPRECATED] On Creature is behind target.
    public const EVENT_GAME_EVENT_START        = 68;        // On game_event started.
    public const EVENT_GAME_EVENT_END          = 69;        // On game_event ended.
    public const EVENT_GO_LOOT_STATE_CHANGED   = 70;        //
    public const EVENT_GO_EVENT_INFORM         = 71;        //
    public const EVENT_ACTION_DONE             = 72;        //
    public const EVENT_ON_SPELLCLICK           = 73;        //
    public const EVENT_FRIENDLY_HEALTH_PCT     = 74;        //
    public const EVENT_DISTANCE_CREATURE       = 75;        // On creature guid OR any instance of creature entry is within distance.
    public const EVENT_DISTANCE_GAMEOBJECT     = 76;        // On gameobject guid OR any instance of gameobject entry is within distance.
    public const EVENT_COUNTER_SET             = 77;        // If the value of specified counterID is equal to a specified value
    public const EVENT_SCENE_START             = 78;        // [RESERVED] don't use on 3.3.5a
    public const EVENT_SCENE_TRIGGER           = 79;        // [RESERVED] don't use on 3.3.5a
    public const EVENT_SCENE_CANCEL            = 80;        // [RESERVED] don't use on 3.3.5a
    public const EVENT_SCENE_COMPLETE          = 81;        // [RESERVED] don't use on 3.3.5a
    public const EVENT_SUMMONED_UNIT_DIES      = 82;        //
    public const EVENT_ON_SPELL_CAST           = 83;        // On Spell::cast
    public const EVENT_ON_SPELL_FAILED         = 84;        // On Unit::InterruptSpell
    public const EVENT_ON_SPELL_START          = 85;        // On Spell::prapare
    public const EVENT_ON_DESPAWN              = 86;        // On before creature removed
    public const EVENT_SEND_EVENT_TRIGGER      = 87;        // [RESERVED] UNUSED NEEDS CHERRYPICK
    public const EVENT_AREATRIGGER_EXIT        = 88;        // [RESERVED] don't use on 3.3.5a
    public const EVENT_ON_AURA_APPLIED         = 89;        //
    public const EVENT_ON_AURA_REMOVED         = 90;        //

    public const FLAG_NO_REPEAT        = 0x0001;
    public const FLAG_DIFFICULTY_0     = 0x0002;
    public const FLAG_DIFFICULTY_1     = 0x0004;
    public const FLAG_DIFFICULTY_2     = 0x0008;
    public const FLAG_DIFFICULTY_3     = 0x0010;
    public const FLAG_DEBUG_ONLY       = 0x0080;
    public const FLAG_NO_RESET         = 0x0100;
    public const FLAG_WHILE_CHARMED    = 0x0200;
    public const FLAG_ALL_DIFFICULTIES = self::FLAG_DIFFICULTY_0 | self::FLAG_DIFFICULTY_1 | self::FLAG_DIFFICULTY_2 | self::FLAG_DIFFICULTY_3;
    public const FLAG_VALIDATE         = self::FLAG_NO_REPEAT | self::FLAG_DEBUG_ONLY | self::FLAG_NO_RESET | self::FLAG_WHILE_CHARMED | self::FLAG_ALL_DIFFICULTIES;

    private const EVENT_CELL_TPL = '[tooltip name=e-#rowIdx#]%1$s[/tooltip][span tooltip=e-#rowIdx#]%2$s[/span]';

    private array $data = array(                          // param 1-5 - int > 0: type, array: [fn, newIdx, extraParam]; error class: int
        self::EVENT_UPDATE_IC               => [['numRange', 10, true],       null,                       ['numRange', -1, true], null,                   null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
        self::EVENT_UPDATE_OOC              => [['numRange', 10, true],       null,                       ['numRange', -1, true], null,                   null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
        self::EVENT_HEALTH_PCT              => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // HPMin%, HPMax%,  RepeatMin, RepeatMax
        self::EVENT_MANA_PCT                => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // ManaMin%, ManaMax%, RepeatMin, RepeatMax
        self::EVENT_AGGRO                   => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_KILL                    => [['numRange', -1, true],       null,                       null,                   Type::NPC,              null, 0], // CooldownMin0, CooldownMax1, playerOnly2, else creature entry3
        self::EVENT_DEATH                   => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_EVADE                   => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_SPELLHIT                => [Type::SPELL,                  ['magicSchool', 10, false], ['numRange', -1, true], null,                   null, 0], // SpellID, School, CooldownMin, CooldownMax
        self::EVENT_RANGE                   => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // MinDist, MaxDist, RepeatMin, RepeatMax
        self::EVENT_OOC_LOS                 => [['hostilityMode', 10, false], null,                       ['numRange', -1, true], null,                   null, 0], // hostilityModes, MaxRange, CooldownMin, CooldownMax
        self::EVENT_RESPAWN                 => [null,                         null,                       Type::ZONE,             null,                   null, 0], // type, MapId, ZoneId
        self::EVENT_TARGET_HEALTH_PCT       => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_VICTIM_CASTING          => [['numRange', -1, true],       null,                       Type::SPELL,            null,                   null, 0], // RepeatMin, RepeatMax, spellid
        self::EVENT_FRIENDLY_HEALTH         => [null,                         null,                       ['numRange', -1, true], null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_FRIENDLY_IS_CC          => [null,                         ['numRange', -1, true],     null,                   null,                   null, 0], // Radius, RepeatMin, RepeatMax
        self::EVENT_FRIENDLY_MISSING_BUFF   => [Type::SPELL,                  null,                       ['numRange', -1, true], null,                   null, 0], // SpellId, Radius, RepeatMin, RepeatMax
        self::EVENT_SUMMONED_UNIT           => [Type::NPC,                    ['numRange', -1, true],     null,                   null,                   null, 0], // CreatureId(0 all), CooldownMin, CooldownMax
        self::EVENT_TARGET_MANA_PCT         => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_ACCEPTED_QUEST          => [Type::QUEST,                  ['numRange', -1, true],     null,                   null,                   null, 0], // QuestID (0 = any), CooldownMin, CooldownMax
        self::EVENT_REWARD_QUEST            => [Type::QUEST,                  ['numRange', -1, true],     null,                   null,                   null, 0], // QuestID (0 = any), CooldownMin, CooldownMax
        self::EVENT_REACHED_HOME            => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_RECEIVE_EMOTE           => [Type::EMOTE,                  ['numRange', -1, true],     null,                   null,                   null, 0], // EmoteId, CooldownMin, CooldownMax, condition, val1, val2, val3
        self::EVENT_HAS_AURA                => [Type::SPELL,                  null,                       ['numRange', -1, true], null,                   null, 0], // Param1 = SpellID, Param2 = Stack amount, Param3/4 RepeatMin, RepeatMax
        self::EVENT_TARGET_BUFFED           => [Type::SPELL,                  null,                       ['numRange', -1, true], null,                   null, 0], // Param1 = SpellID, Param2 = Stack amount, Param3/4 RepeatMin, RepeatMax
        self::EVENT_RESET                   => [null,                         null,                       null,                   null,                   null, 0], // Called after combat, when the creature respawn and spawn.
        self::EVENT_IC_LOS                  => [['hostilityMode', 10, false], null,                       ['numRange', -1, true], null,                   null, 0], // hostilityModes, MaxRnage, CooldownMin, CooldownMax
        self::EVENT_PASSENGER_BOARDED       => [['numRange', -1, true],       null,                       null,                   null,                   null, 0], // CooldownMin, CooldownMax
        self::EVENT_PASSENGER_REMOVED       => [['numRange', -1, true],       null,                       null,                   null,                   null, 0], // CooldownMin, CooldownMax
        self::EVENT_CHARMED                 => [null,                         null,                       null,                   null,                   null, 0], // onRemove (0 - on apply, 1 - on remove)
        self::EVENT_CHARMED_TARGET          => [null,                         null,                       null,                   null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_SPELLHIT_TARGET         => [Type::SPELL,                  ['magicSchool', 10, false], ['numRange', -1, true], null,                   null, 0], // SpellID, School, CooldownMin, CooldownMax
        self::EVENT_DAMAGED                 => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // MinDmg, MaxDmg, CooldownMin, CooldownMax
        self::EVENT_DAMAGED_TARGET          => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // MinDmg, MaxDmg, CooldownMin, CooldownMax
        self::EVENT_MOVEMENTINFORM          => [['motionType', 10, false],    null,                       null,                   null,                   null, 0], // MovementType(any), PointID
        self::EVENT_SUMMON_DESPAWNED        => [Type::NPC,                    ['numRange', -1, true],     null,                   null,                   null, 0], // Entry, CooldownMin, CooldownMax
        self::EVENT_CORPSE_REMOVED          => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_AI_INIT                 => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_DATA_SET                => [null,                         null,                       ['numRange', -1, true], null,                   null, 0], // Id, Value, CooldownMin, CooldownMax
        self::EVENT_WAYPOINT_START          => [null,                         null,                       null,                   null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_WAYPOINT_REACHED        => [null,                         null,                       null,                   null,                   null, 0], // PointId(0any), pathID(0any)
        self::EVENT_TRANSPORT_ADDPLAYER     => [null,                         null,                       null,                   null,                   null, 2], // NONE
        self::EVENT_TRANSPORT_ADDCREATURE   => [null,                         null,                       null,                   null,                   null, 2], // Entry (0 any)
        self::EVENT_TRANSPORT_REMOVE_PLAYER => [null,                         null,                       null,                   null,                   null, 2], // NONE
        self::EVENT_TRANSPORT_RELOCATE      => [null,                         null,                       null,                   null,                   null, 2], // PointId
        self::EVENT_INSTANCE_PLAYER_ENTER   => [null,                         null,                       null,                   null,                   null, 2], // Team (0 any), CooldownMin, CooldownMax
        self::EVENT_AREATRIGGER_ONTRIGGER   => [Type::AREATRIGGER,            null,                       null,                   null,                   null, 0], // TriggerId(0 any)
        self::EVENT_QUEST_ACCEPTED          => [null,                         null,                       null,                   null,                   null, 2], // none
        self::EVENT_QUEST_OBJ_COMPLETION    => [null,                         null,                       null,                   null,                   null, 2], // none
        self::EVENT_QUEST_COMPLETION        => [null,                         null,                       null,                   null,                   null, 2], // none
        self::EVENT_QUEST_REWARDED          => [null,                         null,                       null,                   null,                   null, 2], // none
        self::EVENT_QUEST_FAIL              => [null,                         null,                       null,                   null,                   null, 2], // none
        self::EVENT_TEXT_OVER               => [null,                         Type::NPC,                  null,                   null,                   null, 0], // GroupId from creature_text,  creature entry who talks (0 any)
        self::EVENT_RECEIVE_HEAL            => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // MinHeal, MaxHeal, CooldownMin, CooldownMax
        self::EVENT_JUST_SUMMONED           => [null,                         null,                       null,                   null,                   null, 0], // none
        self::EVENT_WAYPOINT_PAUSED         => [null,                         null,                       null,                   null,                   null, 0], // PointId(0any), pathID(0any)
        self::EVENT_WAYPOINT_RESUMED        => [null,                         null,                       null,                   null,                   null, 0], // PointId(0any), pathID(0any)
        self::EVENT_WAYPOINT_STOPPED        => [null,                         null,                       null,                   null,                   null, 0], // PointId(0any), pathID(0any)
        self::EVENT_WAYPOINT_ENDED          => [null,                         null,                       null,                   null,                   null, 0], // PointId(0any), pathID(0any)
        self::EVENT_TIMED_EVENT_TRIGGERED   => [null,                         null,                       null,                   null,                   null, 0], // id
        self::EVENT_UPDATE                  => [['numRange', 10, true],       null,                       ['numRange', -1, true], null,                   null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
        self::EVENT_LINK                    => [null,                         null,                       null,                   null,                   null, 0], // INTERNAL USAGE, no params, used to link together multiple events, does not use any extra resources to iterate event lists needlessly
        self::EVENT_GOSSIP_SELECT           => [null,                         null,                       null,                   null,                   null, 0], // menuID, actionID
        self::EVENT_JUST_CREATED            => [null,                         null,                       null,                   null,                   null, 0], // none
        self::EVENT_GOSSIP_HELLO            => [null,                         null,                       null,                   null,                   null, 0], // noReportUse (for GOs)
        self::EVENT_FOLLOW_COMPLETED        => [null,                         null,                       null,                   null,                   null, 0], // none
        self::EVENT_EVENT_PHASE_CHANGE      => [null,                         null,                       null,                   null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_IS_BEHIND_TARGET        => [['numRange', -1, true],       null,                       null,                   null,                   null, 1], // UNUSED, DO NOT REUSE
        self::EVENT_GAME_EVENT_START        => [Type::WORLDEVENT,             null,                       null,                   null,                   null, 0], // game_event.Entry
        self::EVENT_GAME_EVENT_END          => [Type::WORLDEVENT,             null,                       null,                   null,                   null, 0], // game_event.Entry
        self::EVENT_GO_LOOT_STATE_CHANGED   => [['lootState', 10, false],     null,                       null,                   null,                   null, 0], // go LootState
        self::EVENT_GO_EVENT_INFORM         => [null,                         null,                       null,                   null,                   null, 0], // eventId
        self::EVENT_ACTION_DONE             => [null,                         null,                       null,                   null,                   null, 0], // eventId (SharedDefines.EventId)
        self::EVENT_ON_SPELLCLICK           => [null,                         null,                       null,                   null,                   null, 0], // clicker (unit)
        self::EVENT_FRIENDLY_HEALTH_PCT     => [['numRange', 10, false],      null,                       ['numRange', -1, true], null,                   null, 0], // minHpPct, maxHpPct, repeatMin, repeatMax
        self::EVENT_DISTANCE_CREATURE       => [null,                         Type::NPC,                  null,                   ['numRange', -1, true], null, 0], // guid, entry, distance, repeat
        self::EVENT_DISTANCE_GAMEOBJECT     => [null,                         Type::OBJECT,               null,                   ['numRange', -1, true], null, 0], // guid, entry, distance, repeat
        self::EVENT_COUNTER_SET             => [null,                         null,                       ['numRange', -1, true], null,                   null, 0], // id, value, cooldownMin, cooldownMax
        self::EVENT_SCENE_START             => [null,                         null,                       null,                   null,                   null, 2], // don't use on 3.3.5a
        self::EVENT_SCENE_TRIGGER           => [null,                         null,                       null,                   null,                   null, 2], // don't use on 3.3.5a
        self::EVENT_SCENE_CANCEL            => [null,                         null,                       null,                   null,                   null, 2], // don't use on 3.3.5a
        self::EVENT_SCENE_COMPLETE          => [null,                         null,                       null,                   null,                   null, 2], // don't use on 3.3.5a
        self::EVENT_SUMMONED_UNIT_DIES      => [Type::NPC,                    ['numRange', -1, true],     null,                   null,                   null, 0], // CreatureId(0 all), CooldownMin, CooldownMax
        self::EVENT_ON_SPELL_CAST           => [Type::SPELL,                  ['numRange', -1, true],     null,                   null,                   null, 0], // SpellID, CooldownMin, CooldownMax
        self::EVENT_ON_SPELL_FAILED         => [Type::SPELL,                  ['numRange', -1, true],     null,                   null,                   null, 0], // SpellID, CooldownMin, CooldownMax
        self::EVENT_ON_SPELL_START          => [Type::SPELL,                  ['numRange', -1, true],     null,                   null,                   null, 0], // SpellID, CooldownMin, CooldownMax
        self::EVENT_ON_DESPAWN              => [null,                         null,                       null,                   null,                   null, 0], // NONE
        self::EVENT_SEND_EVENT_TRIGGER      => [null,                         null,                       null,                   null,                   null, 2], // UNUSED NEEDS CHERRYPICK
        self::EVENT_AREATRIGGER_EXIT        => [null,                         null,                       null,                   null,                   null, 2], // don't use on 3.3.5a
        self::EVENT_ON_AURA_APPLIED         => [Type::SPELL,                  ['numRange', -1, true],     null,                   null,                   null, 0], // SpellID, CooldownMin, CooldownMax
        self::EVENT_ON_AURA_REMOVED         => [Type::SPELL,                  ['numRange', -1, true],     null,                   null,                   null, 0]  // SpellID, CooldownMin, CooldownMax
    );

    private array $jsGlobals = [];

    public function __construct(
        private int $id,
        public readonly int $type,
        public readonly int $phaseMask,
        public readonly int $chance,
        private int $flags,
        private array $param,
        private SmartAI &$smartAI)
    {
        // additional parameters
        Util::checkNumeric($this->param, NUM_CAST_INT);
        $this->param = array_pad($this->param, 15, '');
    }

    public function process() : array
    {
        $body   =
        $footer = '';

        $phases = Util::mask2bits($this->phaseMask, 1) ?: [0];
        $eventTT = Lang::smartAI('eventTT', array_merge([$this->type, $phases, $this->chance, $this->flags], $this->param));

        for ($i = 0; $i < 5; $i++)
        {
            $eParams = $this->data[$this->type];

            if (is_array($eParams[$i]))
            {
                [$fn, $idx, $extraParam] = $eParams[$i];

                if ($idx < 0)
                    $footer = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
                else
                    $this->param[$idx] = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
            }
            else if (is_int($eParams[$i]) && $this->param[$i])
                $this->jsGlobals[$eParams[$i]][$this->param[$i]] = $this->param[$i];
        }

        // non-generic cases
        switch ($this->type)
        {
            case self::EVENT_UPDATE_IC:                     // 0   -  In combat.
            case self::EVENT_UPDATE_OOC:                    // 1   -  Out of combat.
                if ($this->smartAI->srcType == SmartAI::SRC_TYPE_ACTIONLIST)
                    $this->param[11] = 1;
                // do not break;
            case self::EVENT_GOSSIP_HELLO:                  // 64  -  On Right-Click Creature/Gameobject that have gossip enabled.
                if ($this->smartAI->srcType == SmartAI::SRC_TYPE_OBJECT)
                    $footer = array(
                        $this->param[0] == 1,
                        $this->param[0] == 2,
                    );
                break;
            case self::EVENT_RESPAWN:                       // 11  -  On Creature/Gameobject Respawn in Zone/Map
                if ($this->param[0] == 1)                   // per map
                {
                    switch ($this->param[1])
                    {
                        case 0:   $this->param[10] = Lang::maps('EasternKingdoms'); break;
                        case 1:   $this->param[10] = Lang::maps('Kalimdor');        break;
                        case 530: $this->param[10] = Lang::maps('Outland');         break;
                        case 571: $this->param[10] = Lang::maps('Northrend');       break;
                        default:
                            if ($aId = DB::Aowow()->selectCell('SELECT `id` FROM ::zones WHERE `mapId` = %i', $this->param[1]))
                            {
                                $this->param[11] = $aId;
                                $this->jsGlobals[Type::ZONE][$aId] = $aId;
                            }
                            else
                                $this->param[11] = '[span class=q10]Unknown Map[/span] #'.$this->param[1];
                    };
                }
                else if ($this->param[0] == 2)              // per zone
                    $this->param[11] = $this->param[2];

                break;
            case self::EVENT_LINK:                          // 61  -  Used to link together multiple events as a chain of events.
                if ($links = DB::World()->selectCol('SELECT `id` FROM smart_scripts WHERE `link` = %i AND `entryorguid` = %i AND `source_type` = %i', $this->id, $this->smartAI->entry, $this->smartAI->srcType))
                    $this->param[10] = Lang::concat($links, Lang::CONCAT_OR, fn($x) => "#[b]".$x."[/b]");
                break;
            case self::EVENT_GOSSIP_SELECT:                 // 62  -  On gossip clicked (gossip_menu_option335).
                $gmo = DB::World()->selectRow(
                   'SELECT    gmo.`OptionText` AS "text_loc0" %if', Lang::getLocale() != Locale::EN, ', gmol.`OptionText` AS %s', 'text_loc' . Lang::getLocale()->value, '%end
                    FROM      gossip_menu_option gmo
                    LEFT JOIN gossip_menu_option_locale gmol ON gmo.`MenuID` = gmol.`MenuID` AND gmo.`OptionID` = gmol.`OptionID` AND gmol.`Locale` = %s
                    WHERE     gmo.`MenuId` = %i AND gmo.`OptionID` = %i',
                    Lang::getLocale()->json(), $this->param[0], $this->param[1]
                );

                if ($gmo)
                    $this->param[10] = Util::localizedString($gmo, 'text');
                else
                    trigger_error('SmartAI::event - could not find gossip menu option for event #'.$this->type);
                break;
            case self::EVENT_DISTANCE_CREATURE:             // 75  -  On creature guid OR any instance of creature entry is within distance.
                if ($this->param[0])
                    if ($_ = $this->resolveGuid(Type::NPC, $this->param[0]))
                    {
                        $this->jsGlobals[Type::NPC][$this->param[0]] = $this->param[0];
                        $this->param[10] = $_;
                    }
                // do not break;
            case self::EVENT_DISTANCE_GAMEOBJECT:           // 76  -  On gameobject guid OR any instance of gameobject entry is within distance.
                if ($this->param[0] && !$this->param[10])
                {
                    if ($_ = $this->resolveGuid(Type::OBJECT, $this->param[0]))
                    {
                        $this->jsGlobals[Type::OBJECT][$this->param[0]] = $this->param[0];
                        $this->param[10] = $_;
                    }
                }
                else if ($this->param[1])
                    $this->param[10] = $this->param[1];
                else if (!$this->param[10])
                    trigger_error('SmartAI::event - entity for event #'.$this->type.' not defined');
                break;
            case self::EVENT_EVENT_PHASE_CHANGE:            // 66  -  On event phase mask set
                $this->param[10] = Lang::concat(Util::mask2bits($this->param[0]), Lang::CONCAT_OR);
                break;
        }

        $this->smartAI->addJsGlobals($this->jsGlobals);

        $body = Lang::smartAI('events', $this->type, 0, $this->param) ?? Lang::smartAI('eventUNK', [$this->type]);
        if ($footer)
            $footer = Lang::smartAI('events', $this->type, 1, (array)$footer);

        // resolve conditionals
        $i = 0;
        while (strstr($body, ')?') && $i++ < 3)
            $body   = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $body);

        $i = 0;
        while (strstr($footer, ')?') && $i++ < 3)
            $footer = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $footer);

        if ($_ = $this->formatFlags())
            $footer = $_ . ($footer ? '; '.$footer : '');

        if (User::isInGroup(U_GROUP_EMPLOYEE))
        {
            if ($eParams[5] == 1)
                $footer = '[span class=rep2]DEPRECATED[/span] ' . $footer;
            else if ($eParams[5] == 2)
                $footer = '[span class=rep0]RESERVED[/span] ' . $footer;
        }

        // wrap body in tooltip
        return [sprintf(self::EVENT_CELL_TPL, $eventTT, $body), $footer];
    }

    public function hasPhases() : bool
    {
        return $this->phaseMask && ($this->phaseMask & 0xFFF) != 0xFFF;
    }

    private function formatFlags() : string
    {
        $flags = $this->flags;

        if ($x = ($flags & ~self::FLAG_VALIDATE))
        {
            trigger_error('SmartEvent::formatFlags - unused SmartEventFlags '.Util::asBin($x).' set on id #'.$this->id, E_USER_NOTICE);
            $flags &= self::FLAG_VALIDATE;
        }

        if (($flags & self::FLAG_ALL_DIFFICULTIES) == self::FLAG_ALL_DIFFICULTIES)
            $flags &= ~self::FLAG_ALL_DIFFICULTIES;

        $ef = [];
        for ($i = 1; $i <= self::FLAG_WHILE_CHARMED; $i <<= 1)
            if ($flags & $i)
                if ($x = Lang::smartAI('eventFlags', $i))
                    $ef[] = $x;

        return Lang::concat($ef);
    }
}

?>
