/**
 * MaNGOS is a full featured server for World of Warcraft, supporting
 * the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
 *
 * Copyright (C) 2005-2022 MaNGOS <https://getmangos.eu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * World of Warcraft, and all World of Warcraft or Warcraft art, images,
 * and lore are copyrighted by Blizzard Entertainment, Inc.
 */

#include "WaypointManager.h"
#include "Database/DatabaseEnv.h"
#include "GridDefines.h"
#include "Policies/Singleton.h"
#include "ProgressBar.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "ScriptMgr.h"

INSTANTIATE_SINGLETON_1(WaypointManager);

/**
 * If the emote, spell, model1, or model2 variables are not 0, then the function returns false.
 * Otherwise, if any of the textid variables are not 0, then the function returns false. Otherwise, the
 * function returns true
 * @return a boolean value.
 */
bool WaypointBehavior::isEmpty()
{
    if (emote || spell || model1 || model2)
    {
        return false;
    }

    for (int i = 0; i < MAX_WAYPOINT_TEXT; ++i)
        if (textid[i])
        {
            return false;
        }

    return true;
}

/**
 * This function is a copy constructor for the WaypointBehavior class.
 * @param b The waypoint behavior to copy.
 */
WaypointBehavior::WaypointBehavior(const WaypointBehavior& b)
{
    emote = b.emote;
    spell = b.spell;
    model1 = b.model1;
    model2 = b.model2;
    for (int i = 0; i < MAX_WAYPOINT_TEXT; ++i)
    {
        textid[i] = b.textid[i];
    }
}

/**
 * It loads the waypoints from the database
 * @return a pointer to a WaypointPath object.
 */
void WaypointManager::Load()
{
    uint32 total_paths = 0;
    uint32 total_nodes = 0;
    uint32 total_behaviors = 0;

    /* Getting the script chain map for the DBS_ON_CREATURE_MOVEMENT event. */
    ScriptChainMap const* scm = sScriptMgr.GetScriptChainMap(DBS_ON_CREATURE_MOVEMENT);
    if (!scm)
    {
        return;
    }

    std::set<uint32> movementScriptSet;

    /* Iterating through the map and printing out the key and value. */
    for (ScriptChainMap::const_iterator itr = scm->begin(); itr != scm->end(); ++itr)
    {
        movementScriptSet.insert(itr->first);
    }

    // /////////////////////////////////////////////////////
    // creature_movement
    // /////////////////////////////////////////////////////

    QueryResult* result = WorldDatabase.Query("SELECT `id`, COUNT(`point`) FROM `creature_movement` GROUP BY `id`");

    if (!result)
    {
        BarGoLink bar(1);
        bar.step();
        sLog.outString(">> Loaded 0 paths. DB table `creature_movement` is empty.");
        sLog.outString();
    }
    else
    {
        total_paths = (uint32)result->GetRowCount();

        do                                                  // Count expected amount of nodes
        {
            Field* fields   = result->Fetch();

            // uint32 id    = fields[0].GetUInt32();
            uint32 count    = fields[1].GetUInt32();

            total_nodes += count;
        }
        while (result->NextRow());
        delete result;

        //                                   0   1      2           3           4           5         6
        result = WorldDatabase.Query("SELECT `id`, `point`, `position_x`, `position_y`, `position_z`, `waittime`, `script_id`,"
                                     //   7        8        9        10       11       12     13     14           15      16
                                     "`textid1`, `textid2`, `textid3`, `textid4`, `textid5`, `emote`, `spell`, `orientation`, `model1`, `model2` FROM `creature_movement`");

        BarGoLink bar(result->GetRowCount());

        // error after load, we check if creature guid corresponding to the path id has proper MovementType
        std::set<uint32> creatureNoMoveType;

        do
        {
            bar.step();
            Field* fields = result->Fetch();

            uint32 id           = fields[0].GetUInt32();
            uint32 point        = fields[1].GetUInt32();

            const CreatureData* cData = sObjectMgr.GetCreatureData(id);

            if (!cData)
            {
                sLog.outErrorDb("Table creature_movement contain path for creature guid %u, but this creature guid does not exist. Skipping.", id);
                continue;
            }

            if (cData->movementType != WAYPOINT_MOTION_TYPE)
            {
                creatureNoMoveType.insert(id);
            }

            WaypointPath& path  = m_pathMap[id];
            WaypointNode& node  = path[point];

            node.x              = fields[2].GetFloat();
            node.y              = fields[3].GetFloat();
            node.z              = fields[4].GetFloat();
            node.orientation    = fields[14].GetFloat();
            node.delay          = fields[5].GetUInt32();
            node.script_id      = fields[6].GetUInt32();

            // prevent using invalid coordinates
            if (!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation))
            {
                QueryResult* result1 = WorldDatabase.PQuery("SELECT `id`, `map` FROM `creature` WHERE `guid` = '%u'", id);
                if (result1)
                    sLog.outErrorDb("Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
                                    id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y);
                else
                    sLog.outErrorDb("Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
                                    id, point, node.x, node.y);

                MaNGOS::NormalizeMapCoord(node.x);
                MaNGOS::NormalizeMapCoord(node.y);

                if (result1)
                {
                    node.z = sTerrainMgr.LoadTerrain(result1->Fetch()[1].GetUInt32())->GetHeightStatic(node.x, node.y, node.z);
                    delete result1;
                }

                WorldDatabase.PExecute("UPDATE `creature_movement` SET `position_x` = '%f', `position_y` = '%f', `position_z` = '%f' WHERE `id` = '%u' AND `point` = '%u'", node.x, node.y, node.z, id, point);
            }

            if (node.script_id)
            {
                if (scm->find(node.script_id) == scm->end())
                {
                    sLog.outErrorDb("Table creature_movement for id %u, point %u have script_id %u that does not exist in `dbscripts_on_creature_movement`, ignoring", id, point, node.script_id);
                    continue;
                }

                movementScriptSet.erase(node.script_id);
            }

            // WaypointBehavior can be dropped in time. Script_id added may 2010 and can handle all the below behavior.

            WaypointBehavior be;
            be.model1           = fields[15].GetUInt32();
            be.model2           = fields[16].GetUInt32();
            be.emote            = fields[12].GetUInt32();
            be.spell            = fields[13].GetUInt32();

            for (int i = 0; i < MAX_WAYPOINT_TEXT; ++i)
            {
                be.textid[i]    = fields[7 + i].GetInt32();

                if (be.textid[i])
                {
                    if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID)
                    {
                        sLog.outErrorDb("Table `creature_movement` Id %u, point %u has textid%u has value %d out of range. Must be in %u-%u", id, point, i + 1, be.textid[i], MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID - 1);
                        be.textid[i] = 0;
                    }
                }
            }

            if (be.spell && ! sSpellStore.LookupEntry(be.spell))
            {
                sLog.outErrorDb("Table creature_movement references unknown spellid %u. Skipping id %u with point %u.", be.spell, id, point);
                be.spell = 0;
            }

            if (be.emote)
            {
                if (!sEmotesStore.LookupEntry(be.emote))
                {
                    sLog.outErrorDb("Waypoint path %u (Point %u) are using emote %u, but emote does not exist.", id, point, be.emote);
                }
            }

            // save memory by not storing empty behaviors
            if (!be.isEmpty())
            {
                node.behavior = new WaypointBehavior(be);
                ++total_behaviors;
            }
            else
            {
                node.behavior = NULL;
            }
        }
        while (result->NextRow());

        if (!creatureNoMoveType.empty())
        {
            for (std::set<uint32>::const_iterator itr = creatureNoMoveType.begin(); itr != creatureNoMoveType.end(); ++itr)
            {
                const CreatureData* cData = sObjectMgr.GetCreatureData(*itr);
                const CreatureInfo* cInfo = ObjectMgr::GetCreatureTemplate(cData->id);

                ERROR_DB_STRICT_LOG("Table creature_movement has waypoint for creature guid %u (entry %u), but MovementType is not WAYPOINT_MOTION_TYPE(2). Make sure that this is actually used in a script!", *itr, cData->id);

                if (cInfo->MovementType == WAYPOINT_MOTION_TYPE)
                {
                    sLog.outErrorDb("    creature_template for this entry has MovementType WAYPOINT_MOTION_TYPE(2), did you intend to use creature_movement_template ?");
                }
            }
        }

        sLog.outString(">> Loaded %u paths, %u nodes and %u behaviors from waypoints", total_paths, total_nodes, total_behaviors);
        sLog.outString();

        delete result;
    }

    // /////////////////////////////////////////////////////
    // creature_movement_template
    // /////////////////////////////////////////////////////

    result = WorldDatabase.Query("SELECT `entry`, COUNT(`point`) FROM `creature_movement_template` GROUP BY `entry`");

    if (!result)
    {
        BarGoLink bar(1);
        bar.step();
        sLog.outString(">> Loaded 0 path templates. DB table `creature_movement_template` is empty.");
        sLog.outString();
    }
    else
    {
        total_nodes = 0;
        total_behaviors = 0;
        total_paths = (uint32)result->GetRowCount();

        do                                                  // Count expected amount of nodes
        {
            Field* fields = result->Fetch();

            // uint32 entry = fields[0].GetUInt32();
            uint32 count    = fields[1].GetUInt32();

            total_nodes += count;
        }
        while (result->NextRow());
        delete result;

        //                                   0      1      2           3           4           5         6
        result = WorldDatabase.Query("SELECT `entry`, `point`, `position_x`, `position_y`, `position_z`, `waittime`, `script_id`,"
                                     //   7        8        9        10       11       12     13     14           15      16
                                     "`textid1`, `textid2`, `textid3`, `textid4`, `textid5`, `emote`, `spell`, `orientation`, `model1`, `model2` FROM `creature_movement_template`");

        BarGoLink bar(result->GetRowCount());

        do
        {
            bar.step();
            Field* fields = result->Fetch();

            uint32 entry        = fields[0].GetUInt32();
            uint32 point        = fields[1].GetUInt32();

            const CreatureInfo* cInfo = ObjectMgr::GetCreatureTemplate(entry);

            if (!cInfo)
            {
                sLog.outErrorDb("Table creature_movement_template references unknown creature template %u. Skipping.", entry);
                continue;
            }

            WaypointPath& path  = m_pathTemplateMap[entry << 8];
            WaypointNode& node  = path[point];

            node.x              = fields[2].GetFloat();
            node.y              = fields[3].GetFloat();
            node.z              = fields[4].GetFloat();
            node.orientation    = fields[14].GetFloat();
            node.delay          = fields[5].GetUInt32();
            node.script_id      = fields[6].GetUInt32();

            // prevent using invalid coordinates
            if (!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation))
            {
                sLog.outErrorDb("Table creature_movement_template for entry %u (point %u) are using invalid coordinates position_x: %f, position_y: %f)",
                                entry, point, node.x, node.y);

                MaNGOS::NormalizeMapCoord(node.x);
                MaNGOS::NormalizeMapCoord(node.y);

                sLog.outErrorDb("Table creature_movement_template for entry %u (point %u) are auto corrected to normalized position_x=%f, position_y=%f",
                                entry, point, node.x, node.y);

                WorldDatabase.PExecute("UPDATE `creature_movement_template` SET `position_x` = '%f', `position_y` = '%f' WHERE `entry` = %u AND `point` = %u", node.x, node.y, entry, point);
            }

            if (node.script_id)
            {
                if (scm->find(node.script_id) == scm->end())
                {
                    sLog.outErrorDb("Table creature_movement_template for entry %u, point %u have script_id %u that does not exist in `dbscripts_on_creature_movement`, ignoring", entry, point, node.script_id);
                    continue;
                }

                movementScriptSet.erase(node.script_id);
            }

            WaypointBehavior be;
            be.model1           = fields[15].GetUInt32();
            be.model2           = fields[16].GetUInt32();
            be.emote            = fields[12].GetUInt32();
            be.spell            = fields[13].GetUInt32();

            for (int i = 0; i < MAX_WAYPOINT_TEXT; ++i)
            {
                be.textid[i]    = fields[7 + i].GetUInt32();

                if (be.textid[i])
                {
                    if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID)
                    {
                        sLog.outErrorDb("Table `creature_movement_template` Entry %u, point %u has textid%u has value %d out of range. Must be in %u-%u", entry, point, i + 1, be.textid[i], MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID - 1);
                        be.textid[i] = 0;
                    }
                }
            }

            if (be.spell && ! sSpellStore.LookupEntry(be.spell))
            {
                sLog.outErrorDb("Table creature_movement_template references unknown spellid %u. Skipping id %u with point %u.", be.spell, entry, point);
                be.spell = 0;
            }

            if (be.emote)
            {
                if (!sEmotesStore.LookupEntry(be.emote))
                {
                    sLog.outErrorDb("Waypoint template path %u (point %u) are using emote %u, but emote does not exist.", entry, point, be.emote);
                }
            }

            // save memory by not storing empty behaviors
            if (!be.isEmpty())
            {
                node.behavior   = new WaypointBehavior(be);
                ++total_behaviors;
            }
            else
            {
                node.behavior   = NULL;
            }
        }
        while (result->NextRow());

        delete result;

        sLog.outString(">> Loaded %u path templates with %u nodes and %u behaviors from waypoint templates", total_paths, total_nodes, total_behaviors);
        sLog.outString();
    }

    if (!movementScriptSet.empty())
    {
        for (std::set<uint32>::const_iterator itr = movementScriptSet.begin(); itr != movementScriptSet.end(); ++itr)
        {
            sLog.outErrorDb("Table `db_scripts` (on creature movement) contain unused script, id %u.", *itr);
        }
        sLog.outString();
    }
}

/**
 * It clears all the waypoints from the path map, path template map, and external path template map
 */
void WaypointManager::Unload()
{
    for (WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr)
    {
        _clearPath(itr->second);
    }
    m_pathMap.clear();

    for (WaypointPathMap::iterator itr = m_pathTemplateMap.begin(); itr != m_pathTemplateMap.end(); ++itr)
    {
        _clearPath(itr->second);
    }
    m_pathTemplateMap.clear();

    for (WaypointPathMap::iterator itr = m_externalPathTemplateMap.begin(); itr != m_externalPathTemplateMap.end(); ++itr)
    {
        _clearPath(itr->second);
    }
    m_externalPathTemplateMap.clear();

}

/**
 * It deletes all the waypoints in a path
 * @param path The path to clear.
 */
void WaypointManager::_clearPath(WaypointPath& path)
{
    for (WaypointPath::const_iterator itr = path.begin(); itr != path.end(); ++itr)
    {
        delete itr->second.behavior;
    }
    path.clear();
}

/**
 * It adds a waypoint to the external waypoint map
 * @param entry The entry of the NPC you want to add the waypoint to.
 * @param pathId This is the path ID. It's a number between 0 and 255.
 * @param pointId The point ID of the waypoint.
 * @param x The X coordinate of the waypoint.
 * @param y The Y coordinate of the waypoint.
 * @param z The Z coordinate of the waypoint.
 * @param o The orientation
 * @param waittime The time in milliseconds that the NPC will wait at this node before moving to the
 * next node.
 * @return a boolean value.
 */
bool WaypointManager::AddExternalNode(uint32 entry, int32 pathId, uint32 pointId, float x, float y, float z, float o, uint32 waittime)
{
    if (pathId < 0 || pathId >= 0xFF)
    {
        sLog.outErrorScriptLib("WaypointManager::AddExternalNode: (Npc-Entry %u, PathId %i) Invalid pathId", entry, pathId);
        return false;
    }

    if (!MaNGOS::IsValidMapCoord(x, y, z, o))
    {
        sLog.outErrorScriptLib("WaypointManager::AddExternalNode: (Npc-Entry %u, PathId %i) Invalid coordinates", entry, pathId);
        return false;
    }

    m_externalPathTemplateMap[(entry << 8) + pathId][pointId] = WaypointNode(x, y, z, o, waittime, 0, NULL);
    return true;
}

/**
 * It adds a new waypoint to the waypoint path
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param pointId The waypoint ID.
 * @param wpDest
 * @param x The X coordinate of the waypoint.
 * @param y The Y coordinate of the waypoint.
 * @param z The Z coordinate of the waypoint.
 * @return A pointer to the WaypointNode object that was just created.
 */
WaypointNode const* WaypointManager::AddNode(uint32 entry, uint32 dbGuid, uint32& pointId, WaypointPathOrigin wpDest, float x, float y, float z)
{
    // Support only normal movement tables
    if (wpDest != PATH_FROM_GUID && wpDest != PATH_FROM_ENTRY)
    {
        return NULL;
    }

    // Prepare information
    char const* const table     = wpDest == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpDest == PATH_FROM_GUID ? "id" : "entry";
    uint32 key            = wpDest == PATH_FROM_GUID ? dbGuid : ((entry << 8) /*+ pathId*/);
    WaypointPathMap* wpMap      = wpDest == PATH_FROM_GUID ? &m_pathMap : &m_pathTemplateMap;

    WaypointPath& path = (*wpMap)[key];

    if (pointId == 0 && !path.empty())                      // Start with highest waypoint
    {
        pointId = path.rbegin()->first + 1;
    }
    else if (pointId == 0)
    {
        pointId = 1;
    }

    uint32 nextPoint = pointId;
    WaypointNode temp = WaypointNode(x, y, z, 100, 0, 0, NULL);
    WaypointPath::iterator find = path.find(nextPoint);
    if (find != path.end())                                 // Point already exists
    {
        do                                                  // Move points along until a free spot is found
        {
            std::swap(temp, find->second);
            ++find;
            ++nextPoint;
        } while (find != path.end() && find->first == nextPoint);
        // After this, we have:
        // pointId, pointId+1, ..., nextPoint [ Can be == path.end ]]
    }

    // Insert new or remaining
    path[nextPoint] = temp;

    // Update original waypoints
    for (WaypointPath::reverse_iterator rItr = path.rbegin(); rItr != path.rend() && rItr->first > pointId; ++rItr)
    {
        if (rItr->first <= nextPoint)
        {
            WorldDatabase.PExecuteLog("UPDATE `%s` SET `point`=`point`+1 WHERE `%s`=%u AND `point`=%u", table, key_field, key, rItr->first - 1);
        }
    }
    // Insert new Point to database
    WorldDatabase.PExecuteLog("INSERT INTO `%s` (`%s`,`point`,`position_x`,`position_y`,`position_z`,`orientation`) VALUES (%u,%u, %f,%f,%f, 100)", table, key_field, key, pointId, x, y, z);

    return &path[pointId];
}

/**
 * It deletes a waypoint from the database
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param point The point number to delete.
 * @param pathId The path ID of the waypoint path you want to delete a node from.
 * @param wpOrigin This is the type of waypoint path you want to delete.
 * @return a pointer to a WaypointPath object.
 */
void WaypointManager::DeleteNode(uint32 entry, uint32 dbGuid, uint32 point, int32 pathId, WaypointPathOrigin wpOrigin)
{
    // Support only normal movement tables
    if (wpOrigin != PATH_FROM_GUID && wpOrigin != PATH_FROM_ENTRY)
    {
        return;
    }

    WaypointPath* path = GetPathFromOrigin(entry, dbGuid, pathId, wpOrigin);
    if (!path)
    {
        return;
    }

    char const* const table     = wpOrigin == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpOrigin == PATH_FROM_GUID ? "id" : "entry";
    uint32 const key            = wpOrigin == PATH_FROM_GUID ? dbGuid : entry;
    WorldDatabase.PExecuteLog("DELETE FROM `%s` WHERE `%s`=%u AND `point`=%u", table, key_field, key, point);

    path->erase(point);
}

/**
 * It deletes a path from the database, but not from the map
 * @param id The ID of the path you want to delete.
 */
void WaypointManager::DeletePath(uint32 id)
{
    WorldDatabase.PExecuteLog("DELETE FROM `creature_movement` WHERE `id`=%u", id);
    WaypointPathMap::iterator itr = m_pathMap.find(id);
    if (itr != m_pathMap.end())
    {
        _clearPath(itr->second);
    }
    // the path is not removed from the map, just cleared
    // WMGs have pointers to the path, so deleting them would crash
    // this wastes some memory, but these functions are
    // only meant to be called by GM commands
}

/**
 * It updates the position of a waypoint in the database
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param point The point number in the path.
 * @param pathId The path ID of the waypoint.
 * @param wpOrigin This is the type of waypoint path. It can be PATH_FROM_GUID, PATH_FROM_ENTRY, or
 * PATH_FROM_EXTERNAL.
 * @param x The X coordinate of the waypoint.
 * @param y The Y coordinate of the waypoint.
 * @param z The Z coordinate of the waypoint.
 * @return a pointer to a WaypointPath object.
 */
void WaypointManager::SetNodePosition(uint32 entry, uint32 dbGuid, uint32 point, int32 pathId, WaypointPathOrigin wpOrigin, float x, float y, float z)
{
    // Support only normal movement tables
    if (wpOrigin != PATH_FROM_GUID && wpOrigin != PATH_FROM_ENTRY)
    {
        return;
    }

    WaypointPath* path = GetPathFromOrigin(entry, dbGuid, pathId, wpOrigin);
    if (!path)
    {
        return;
    }

    char const* const table     = wpOrigin == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpOrigin == PATH_FROM_GUID ? "id" : "entry";
    uint32 const key            = wpOrigin == PATH_FROM_GUID ? dbGuid : entry;
    WorldDatabase.PExecuteLog("UPDATE `%s` SET `position_x`=%f, `position_y`=%f, `position_z`=%f WHERE `%s`=%u AND `point`=%u", table, x, y, z, key_field, key, point);

    WaypointPath::iterator find = path->find(point);
    if (find != path->end())
    {
        find->second.x = x;
        find->second.y = y;
        find->second.z = z;
    }
}

/**
 * It updates the waittime of a waypoint in the database
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param point The point number in the path.
 * @param pathId The path ID of the waypoint path you want to modify.
 * @param wpOrigin This is the type of waypoint path you want to modify.
 * @param waittime The time in milliseconds that the NPC will wait at the waypoint before moving to the
 * next one.
 * @return a pointer to a WaypointPath object.
 */
void WaypointManager::SetNodeWaittime(uint32 entry, uint32 dbGuid, uint32 point, int32 pathId, WaypointPathOrigin wpOrigin, uint32 waittime)
{
    // Support only normal movement tables
    if (wpOrigin != PATH_FROM_GUID && wpOrigin != PATH_FROM_ENTRY)
    {
        return;
    }

    WaypointPath* path = GetPathFromOrigin(entry, dbGuid, pathId, wpOrigin);
    if (!path)
    {
        return;
    }

    char const* const table     = wpOrigin == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpOrigin == PATH_FROM_GUID ? "id" : "entry";
    uint32 const key            = wpOrigin == PATH_FROM_GUID ? dbGuid : entry;
    WorldDatabase.PExecuteLog("UPDATE `%s` SET `waittime`=%u WHERE `%s`=%u AND `point`=%u", table, waittime, key_field, key, point);

    WaypointPath::iterator find = path->find(point);
    if (find != path->end())
    {
        find->second.delay = waittime;
    }
}

/**
 * It updates the orientation of a waypoint in the database
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param point The point number in the path.
 * @param pathId The path ID of the waypoint path you want to modify.
 * @param wpOrigin This is the type of waypoint path you want to modify.
 * @param orientation The new orientation of the waypoint.
 * @return a pointer to a WaypointPath object.
 */
void WaypointManager::SetNodeOrientation(uint32 entry, uint32 dbGuid, uint32 point, int32 pathId, WaypointPathOrigin wpOrigin, float orientation)
{
    // Support only normal movement tables
    if (wpOrigin != PATH_FROM_GUID && wpOrigin != PATH_FROM_ENTRY)
    {
        return;
    }

    WaypointPath* path = GetPathFromOrigin(entry, dbGuid, pathId, wpOrigin);
    if (!path)
    {
        return;
    }

    char const* const table     = wpOrigin == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpOrigin == PATH_FROM_GUID ? "id" : "entry";
    uint32 const key            = wpOrigin == PATH_FROM_GUID ? dbGuid : entry;
    WorldDatabase.PExecuteLog("UPDATE `%s` SET `orientation`=%f WHERE `%s`=%u AND `point`=%u", table, orientation, key_field, key, point);

    WaypointPath::iterator find = path->find(point);
    if (find != path->end())
    {
        find->second.orientation = orientation;
    }
}

/**
 * It updates the script_id field of the creature_movement table for the given entry, dbGuid, point,
 * pathId, and wpOrigin
 * @param entry The entry of the creature.
 * @param dbGuid The GUID of the creature in the database.
 * @param point The waypoint number.
 * @param pathId The path ID of the waypoint path you want to modify.
 * @param wpOrigin This is the type of waypoint path you want to modify.
 * @param scriptId The scriptId of the script you want to run.
 * @return The script_id of the waypoint.
 */
bool WaypointManager::SetNodeScriptId(uint32 entry, uint32 dbGuid, uint32 point, int32 pathId, WaypointPathOrigin wpOrigin, uint32 scriptId)
{
    // Support only normal movement tables
    if (wpOrigin != PATH_FROM_GUID && wpOrigin != PATH_FROM_ENTRY)
    {
        return false;
    }

    WaypointPath* path = GetPathFromOrigin(entry, dbGuid, pathId, wpOrigin);
    if (!path)
    {
        return false;
    }

    char const* const table     = wpOrigin == PATH_FROM_GUID ? "creature_movement" : "creature_movement_template";
    char const* const key_field = wpOrigin == PATH_FROM_GUID ? "id" : "entry";
    uint32 const key            = wpOrigin == PATH_FROM_GUID ? dbGuid : entry;
    WorldDatabase.PExecuteLog("UPDATE `%s` SET `script_id`=%u WHERE `%s`=%u AND `point`=%u", table, scriptId, key_field, key, point);

    WaypointPath::iterator find = path->find(point);
    if (find != path->end())
    {
        find->second.script_id = scriptId;
    }

    ScriptChainMap const* scm = sScriptMgr.GetScriptChainMap(DBS_ON_CREATURE_MOVEMENT);
    if (!scm)
    {
        return false;
    }

    return scm->find(scriptId) != scm->end();
}

/**
 * It checks if the textid is valid and if it is, it removes it from the set of textids.
 * @param isTemplate Whether the waypoint is for a creature template or a creature.
 * @param entryOrGuid The entry or guid of the creature.
 * @param point The point ID of the waypoint.
 * @param be WaypointBehavior*
 * @param ids A set of all the text IDs used in the database.
 */
inline void CheckWPText(bool isTemplate, uint32 entryOrGuid, uint32 point, WaypointBehavior* be, std::set<int32>& ids)
{
    int zeroCount = 0;                                      // Counting leading zeros for futher textid shift
    for (int j = 0; j < MAX_WAYPOINT_TEXT; ++j)
    {
        if (!be->textid[j])
        {
            ++zeroCount;
            continue;
        }
        if (!sObjectMgr.GetMangosStringLocale(be->textid[j]))
        {
            sLog.outErrorDb("Table `creature_movement%s %u, PointId %u has textid%u with non existing textid %i.",
                            isTemplate ? "_template` Entry:" : "` Id:", entryOrGuid, point, j, be->textid[j]);
            be->textid[j] = 0;
            ++zeroCount;
            continue;
        }
        ids.erase(uint32(be->textid[j]));

        // Shifting check
        if (zeroCount)
        {
            // Correct textid but some zeros leading, so move it forward.
            be->textid[j - zeroCount] = be->textid[j];
            be->textid[j] = 0;
        }
    }
}

/**
 * It checks if the waypoint text exists in the database
 * @param ids The set of ids that are being checked for existence.
 */
void WaypointManager::CheckTextsExistance(std::set<int32>& ids)
{
    for (WaypointPathMap::const_iterator pmItr = m_pathMap.begin(); pmItr != m_pathMap.end(); ++pmItr)
    {
        for (WaypointPath::const_iterator pItr = pmItr->second.begin(); pItr != pmItr->second.end(); ++pItr)
            if (pItr->second.behavior)
            {
                CheckWPText(false, pmItr->first, pItr->first, pItr->second.behavior, ids);
            }
    }

    for (WaypointPathMap::const_iterator pmItr = m_pathTemplateMap.begin(); pmItr != m_pathTemplateMap.end(); ++pmItr)
    {
        for (WaypointPath::const_iterator pItr = pmItr->second.begin(); pItr != pmItr->second.end(); ++pItr)
            if (pItr->second.behavior)
            {
                CheckWPText(true, pmItr->first, pItr->first, pItr->second.behavior, ids);
            }
    }
}
