// Copyright (C) 2004 WoW Daemon
//
// 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.h
//

#ifndef __WORLD_H
#define __WORLD_H

class Object;
class WorldPacket;
class WorldSession;
class Unit;
class Creature;
class GameObject;
class DynamicObject;
class Player;
class EventableObjectHolder;
class MapMgr;

enum Rates
{
    RATE_HEALTH=0,
    RATE_POWER1,    
    RATE_POWER2,    
    RATE_POWER3,    
    RATE_DROP,
    RATE_XP,
    RATE_RESTXP,
    RATE_QUESTXP,
    MAX_RATES
};        

enum IntRates
{
    INTRATE_SAVE=0,
    INTRATE_COMPRESSION,
    INTRATE_PVPTIMER,
    MAX_INTRATES
};


enum EnviromentalDamage
{
    DAMAGE_EXHAUSTED = 0,
    DAMAGE_DROWNING = 1,
    DAMAGE_FALL = 2,
    DAMAGE_LAVA = 3,
    DAMAGE_SLIME = 4,
    DAMAGE_FIRE = 5
};

enum CharCreateErrors
{
    SUCCESS,
    FAILURE,
    CANCELLED,
    DISCONNECT_FROM_SERVER,
    FAILED_TO_CONNECT,
    CONNECTED,
    WRONG_CLIENT_VERSION,
    CONNECTING_TO_SERVER,
    NEGOTIATING_SECURITY,
    NEGOTIATING_SECURITY_COMPLETE,
    NEGOTIATING_SECURITY_FAILED,
    AUTHENTICATING,
    AUTHENTICATION_SUCCESSFUL,
    AUTHENTICATION_FAILED,
    LOGIN_UNAVAIBLE,
    SERVER_IS_NOT_VALID,
    SYSTEM_UNAVAIBLE,
    SYSTEM_ERROR,
    BILLING_SYSTEM_ERROR,
    ACCOUNT_BILLING_EXPIRED,
    WRONG_CLIENT_VERSION_2,
    UNKNOWN_ACCOUNT,
    INCORRECT_PASSWORD,
    SESSION_EXPIRED,
    SERVER_SHUTTING_DOWN,
    ALREADY_LOGGED_IN,
    INVALID_LOGIN_SERVER,
    POSITION_IN_QUEUE_0,
    THIS_ACCOUNT_HAS_BEEN_BANNED,
    THIS_CHARACTER_STILL_LOGGED_ON,
    YOUR_WOW_SUBSCRIPTION_IS_EXPIRED,
    THIS_SESSION_HAS_TIMED_OUT,
    THIS_ACCOUNT_TEMP_SUSPENDED,
    ACCOUNT_BLOCKED_BY_PARENTAL_CONTROL,
    RETRIEVING_REALMLIST,
    REALMLIST_RETRIEVED,
    UNABLE_TO_CONNECT_REALMLIST_SERVER,
    INVALID_REALMLIST,
    GAME_SERVER_DOWN,
    CREATING_ACCOUNT,
    ACCOUNT_CREATED,
    ACCOUNT_CREATION_FAIL,
    RETRIEVE_CHAR_LIST,
    CHARLIST_RETRIEVED,
    CHARLIST_ERROR,
    CREATING_CHARACTER,
    CHARACTER_CREATED,
    ERROR_CREATING_CHARACTER,
    CHARACTER_CREATION_FAIL,
    NAME_IS_IN_USE,
    CREATION_OF_RACE_DISABLED,
    ALL_CHARS_ON_PVP_REALM_MUST_AT_SAME_SIDE,
    ALREADY_HAVE_MAXIMUM_CHARACTERS,
    ALREADY_HAVE_MAXIMUM_CHARACTERS_2,
    SERVER_IS_CURRENTLY_QUEUED,
    ONLY_PLAYERS_WHO_HAVE_CHARACTERS_ON_THIS_REALM,
    NEED_EXPANSION_ACCOUNT,
    DELETING_CHARACTER,
    CHARACTER_DELETED,
    CHARACTER_DELETION_FAILED,
    ENTERING_WOW,
    LOGIN_SUCCESFUL,
    WORLD_SERVER_DOWN,
    A_CHARACTER_WITH_THAT_NAME_EXISTS,
    NO_INSTANCE_SERVER_AVAIBLE,
    LOGIN_FAILED,
    LOGIN_FOR_THAT_RACE_DISABLED,
    LOGIN_FOR_THAT_RACE_CLASS_DISABLED,//check
    ENTER_NAME_FOR_CHARACTER,
    NAME_AT_LEAST_TWO_CHARACTER,
    NAME_AT_MOST_12_CHARACTER,
    NAME_CAN_CONTAIN_ONLY_CHAR,
    NAME_CONTAIN_ONLY_ONE_LANG,
    NAME_CONTAIN_PROFANTY,
    NAME_IS_RESERVED,
    YOU_CANNOT_USE_APHOS,
    YOU_CAN_ONLY_HAVE_ONE_APHOS,
    YOU_CANNOT_USE_SAME_LETTER_3_TIMES,
    NO_SPACE_BEFORE_NAME,
    BLANK,
    INVALID_CHARACTER_NAME,
    BLANK_1
    //All further codes give the number in dec.
};

// ServerMessages.dbc
enum ServerMessageType
{
    SERVER_MSG_SHUTDOWN_TIME      = 1,
    SERVER_MSG_RESTART_TIME       = 2,
    SERVER_MSG_STRING             = 3,
    SERVER_MSG_SHUTDOWN_CANCELLED = 4,
    SERVER_MSG_RESTART_CANCELLED  = 5
};

enum WorldMapInfoFlag
{
    WMI_INSTANCE_ENABLED   = 0x1,
    WMI_INSTANCE_WELCOME   = 0x2,
    WMI_INSTANCE_MULTIMODE = 0x4,
    WMI_INSTANCE_XPACK_01  = 0x8, //The Burning Crusade expansion
};

enum AccountFlags
{
    ACCOUNT_FLAG_VIP         = 0x1,
    ACCOUNT_FLAG_XTEND_INFO  = 0x4,
    ACCOUNT_FLAG_XPACK_01    = 0x8,
};

struct MapInfo
{
    bool HasFlag(uint32 flag)
    {
         return (flags & flag) != 0;
    }

    uint32 mapid;
    uint32 screenid;
    uint32 type;
    uint32 playerlimit;
    uint32 minlevel;
    float repopx;
    float repopy;
    float repopz;
    uint32 repopmapid;
    uint32 flags;
    std::string name;
};
struct AreaTable;

typedef std::set<WorldSession*> SessionSet;

class WorldSocket;

// Slow for remove in middle, oh well, wont get done much.
typedef std::vector<WorldSocket*> QueueSet;
typedef set<WorldSession*> SessionSet;

class WOWD_SERVER_DECL World : public Singleton<World>, public EventableObject
{
public:
    World();
    ~World();

    WorldSession* FindSession(uint32 id) const;
    WorldSession* FindSessionByName(std::string Name);
    void AddSession(WorldSession *s);
    void RemoveSession(uint32 id);

    void AddGlobalSession(WorldSession *session);
    void RemoveGlobalSession(WorldSession *session);
    void DeleteSession(WorldSession *session);

    inline uint32 GetSessionCount() const { return m_sessions.size(); }
    inline uint32 GetNonGmSessionCount();

    inline uint32 GetPlayerLimit() const { return m_playerLimit; }
    void SetPlayerLimit(uint32 limit) { m_playerLimit = limit; }

    inline bool getAllowMovement() const { return m_allowMovement; }
    void SetAllowMovement(bool allow) { m_allowMovement = allow; }
    inline bool getGMTicketStatus() { return m_gmTicketSystem; };
    bool toggleGMTicketStatus()
    {
        m_gmTicketSystem = !m_gmTicketSystem;
        return m_gmTicketSystem;
    };

    inline bool getReqGmClient() { return reqGmClient; }
    inline std::string getGmClientChannel() { return GmClientChannel; }

    void SetMotd(const char *motd) { m_motd = motd; }
    inline const char* GetMotd() const { return m_motd.c_str(); }

    inline time_t GetGameTime() const { return m_gameTime; }

    void SetInitialWorldSettings();

    void SendWorldText(const char *text, WorldSession *self = 0);
    void SendWorldWideScreenText(const char *text, WorldSession *self = 0);
    void SendGlobalMessage(WorldPacket *packet, WorldSession *self = 0);
    void SendZoneMessage(WorldPacket *packet, uint32 zoneid, WorldSession *self = 0);

    inline void SetStartTime(uint32 val) { m_StartTime = val; }
    inline uint32 GetUptime(void) { return (uint32)time(NULL) - m_StartTime; }
    inline uint32 GetStartTime(void) { return m_StartTime; }
    inline std::string GetUptimeString()
    {
        int seconds = (uint32)time(NULL) - m_StartTime;
        int mins=0;
        int hours=0;
        int days=0;
        if(seconds >= 60)
        {
            mins = seconds / 60;
            if(mins)
            {
                seconds -= mins*60;
                if(mins >= 60)
                {
                    hours = mins / 60;
                    if(hours)
                    {
                        mins -= hours*60;
                        if(hours >= 24)
                        {
                            days = hours/24;
                            if(days)
                                hours -= days*24;
                        }
                    }
                }
            }
        }
        char str[200];
        sprintf(str, "%d days, %d hours, %d minutes, %d seconds.", days, hours, mins, seconds);
        return str;
    }

    // update the world server every frame
    void Update(time_t diff);

    void UpdateAuctions();
    void UpdateSessions(uint32 diff);

    //Global objects(not in any map)
    void AddGlobalObject(Object *obj); 
    void RemoveGlobalObject(Object *obj);
    bool HasGlobalObject(Object *obj) { return !(m_globalObjects.find(obj) == m_globalObjects.end()); }

    inline void setRate(int index,float value)
    {
        regen_values[index]=value;
    }

    inline float getRate(int index)
    {
        return regen_values[index];
    }
    
    inline uint32 getIntRate(int index)
    {
        return int_rates[index];
    }

    inline void setIntRate(int index, uint32 value)
    {
        int_rates[index] = value;
    }

    // map text emote to spell prices
    typedef std::map< uint32, uint32> SpellPricesMap;
    SpellPricesMap mPrices;

    // area trigger
    void LoadAreaTriggerInformation();
    void AddAreaTrigger(AreaTrigger *pArea);
    AreaTrigger *GetAreaTrigger(uint32 id);

    //world map information
    void LoadMapInformation();
    void AddMapInformation(MapInfo *mapinfo);
    MapInfo *GetMapInformation(uint32 mapid);
    inline uint32 GetTimeOut(){return TimeOut;}

    std::string GenerateName(uint32 type = 0);

    void SetUpdateDistance(float dist) { m_UpdateDistance = (float)pow(dist, 2); }
    inline float GetUpdateDistance() { return (m_UpdateDistance ? m_UpdateDistance : 79.1f); }

    static Unit*          GetUnit(const uint64 & guid);
    static Player*        GetPlayer(const uint64 & guid);
    static Creature*      GetCreature(const uint64 & guid);
    static GameObject*    GetGameObject(const uint64 & guid);
    static DynamicObject* GetDynamicObject(const uint64 & guid);

    uint32 mActiveCellCount;
    uint32 mInactiveCellCount;
    uint32 mHalfActiveCellCount;
    uint32 mTotalCells;
    
    uint32 mLoadedCreatures[2];
    uint32 mLoadedGameObjects[2];

    std::map<uint32, AreaTable*> mAreaIDToTable;
    std::map<uint32, AreaTable*> mZoneIDToTable;

    uint32 AddQueuedSocket(WorldSocket* Socket);
    void RemoveQueuedSocket(WorldSocket* Socket);
    uint32 GetQueuePos(WorldSocket* Socket);
    void UpdateQueuedSessions(uint32 diff);

    Mutex queueMutex;

    uint32 mQueueUpdateInterval;
    bool m_useIrc;
    bool sendRevisionOnJoin;

    void SaveAllPlayers();
    uint32 LevelCap;
    string MapPath;
    bool UnloadMapFiles;
    bool BreathingEnabled;
    bool SpeedhackProtection;
    void EventDeleteInstance(uint32 mapid, uint32 instanceid);
    uint32 mInWorldPlayerCount;
    uint32 mAcceptedConnections;

    inline void AddExtendedSession(WorldSession * session) { mExtendedSessions.insert(session); }
    inline void RemoveExtendedSession(WorldSession * session) { mExtendedSessions.erase(session); }
    void BroadcastExtendedMessage(WorldSession * self, const char* str, ...);    

    uint32 HordePlayers;
    uint32 AlliancePlayers;
    SessionSet gmList;

    void ShutdownClasses();

protected:
    // update Stuff, FIXME: use diff
    time_t _UpdateGameTime()
    {
        // Update Server time
        time_t thisTime = time(NULL);
        m_gameTime += thisTime - m_lastTick;
        m_lastTick = thisTime;

        return m_gameTime;
    }
    void FillSpellReplacementsTable();

private:
    EventableObjectHolder * eventholder;
    //! Timers
    typedef HM_NAMESPACE::hash_map<uint32, WorldSession*> SessionMap;
    SessionMap m_sessions;

    typedef HM_NAMESPACE::hash_map<uint32, AreaTrigger*> AreaTriggerMap;
    AreaTriggerMap m_AreaTrigger;

public:

    typedef HM_NAMESPACE::hash_map<uint32, MapInfo*> MapInfoMap;
    inline MapInfoMap::iterator GetMapInfoBegin() { return m_mapinfo.begin(); }
    inline MapInfoMap::iterator GetMapInfoEnd() { return m_mapinfo.end(); }

protected:
    MapInfoMap m_mapinfo;
    SessionSet Sessions;

    //Global objects(not in any map)
    typedef std::set<Object*> ObjectSet;
    ObjectSet m_globalObjects;

    float regen_values[MAX_RATES];
    uint32 int_rates[MAX_INTRATES];

    uint32 m_playerLimit;
    bool m_allowMovement;
    bool m_gmTicketSystem;
    std::string m_motd;

    bool reqGmClient;
    std::string GmClientChannel;

    time_t m_gameTime;
    time_t m_lastTick;
    uint32 TimeOut;

    float m_UpdateDistance;
    uint32 m_StartTime;
    uint32 m_queueUpdateTimer;

    QueueSet mQueuedSessions;
    SessionSet mExtendedSessions;
};

#define sWorld World::getSingleton()

#endif
