Data structures
----------------------

GEM3_QA_Channels = { -- Current character channels configuration
  [index] = { -- array (process table with ipairs())
    ["name"] = STR -- Channel name (internal channel name if this is special guild channel. You can use GUILD constant for display)
    ["password"] = STR -- Channel password (nil if not used)
    ["alias"] = STR -- Alias to be displayed (nil if not used)
    ["slash"] = STR -- Slash command to talk on the channel (nil if not used)
    ["notify"] = INT -- Notify join/leave (0/1)
    ["guild"] = BOOL -- True if this is special guild channel
  }
}

GEM3_InstanceTag = {
  [GEM3_ACCESS_INST_TAG_ID] = INT -- ID 
  [GEM3_ACCESS_INST_TAG_RESET] = INT -- UTC reset time for this tag
}

GEM3_Banned = {
  ["name"] = STR -- Name of the banned player
  ["reason"] = STR -- Reason of the ban
}

GEM3_ChannelPlayer = { -- Infos about a player in a channel
  ["name"] = STR -- Player's name
  ["guild"] = STR -- Guild's name
  ["location"] = STR -- 
  ["level"] = INT -- Player's level
  ["class"] = STR -- Player's class
  ["role"] = INT -- Player's role
  ["officer"] = -- 
  ["grank_name"] = STR -- 
  ["grank_idx"] = -- 
  ["version"] = -- GEM version
  ["comment"] = STR -- 
  ["lastlog"] = INT -- Last time I got infos from this player - Used to purge old players
  ["tags] = table of GEM3_InstanceTag -- Instance tags for the player (indexed by GEM3 instance tags, one of GEM3_INST_TAG_xx)
  -- Runtime built values
  ["isLeader"] = BOOL -- If player is currently the GUILD channel leader
  ["isTimeRef"] = BOOL -- If player is a time reference
  ["connected"] = BOOL -- If player is currently in the channel
  ["lastleave"] = INT or nil -- Last time the player left the channel (or nil if connected)
}

GEM3_QA_Config = { -- Current character GEM configuration
  -- CORE VALUES
  ["channels"] = GEM3_QA_Channels
  ["BipOnChannel"] = BOOL -- If a sound must be played when "BipOnChannelValue" is seen in a GEM channel
  ["BipOnChannelValue"] = STR -- String value that will play a "bip" sound when seen in a GEM channel
  ["debug"] = INT -- Debug value
  ["role"] = INT -- My role
  ["next_event_id"] = INT -- Next event ID
  -- Runtime built values
  ["my_bcast_offset"] = INT -- Delay to wait before broadcasting an event or a command, if not a channel leader
  
  -- GUI VALUES
  ["FilterChannel"] = BOOL -- Only show events from this character's channel
  ["FilterEvents"] = BOOL -- Only show events in this character's level range
  ["DateFormat"] = STR -- Displayed dates format
  ["comment"] = STR -- Comment shown in player's window
  ["UseServerTime"] = BOOL -- If displayed dates use server time
  ["scale"] = INT -- GUI scale value
  ["MinimapArcOffset"] = INT -- 
  ["MinimapRadiusOffset"] = INT -- 
  ["MinimapTexture"] = STR -- 
  ["SeeOffline"] = BOOL -- If showing offline members
}

GEM3_Config = { -- Saved structure for GEM configuration
  [STR RealmName] = {
    [STR CharacterName] = GEM3_QA_Config
  }
  ["version"] = STR -- GEM Version
}

GEM3_QA_Events = { -- Events for this Realm (init in _GEM3_StartupInitVars)
  ["events"] = array of GEM3_Event -- Events data (indexed by EventId)
  ["commands"] = array of GEM3_Commands -- (indexed by EventId)
  ["my_deleted_events"] = array of GEM3_Event -- Events I deleted (indexed by EventId)
  ["ignored"] = array of BOOL -- Ignored events (indexed by EventId)
  ["subscribed"][] = array of array of GEM3_Subscription -- My subscriptions (indexed by EventID then by name)
  ["kicked"][] = array of array of STR -- Events I'm kicked from with reason (indexed by EventID then by name)
  ["banned"][] = array of array of STR -- Events I'm banned from with reason (indexed by EventID then by name)
  ["ignored_players"] = array of BOOL (or Nil) -- Player's in my ignore list (GEM list, not WoW list) (indexed by name)
  ["forward"] = array of STR -- Name of new leader for events I released to new leader (indexed by EventID)
  ["archived"] = array of GEM3_Event -- Archived events
  ["assistants"] = array of array of BOOL -- Name of event assistants (indexed by EventID then by name)
  ["others_deleted_events"] = array of INT -- Deleted events I'm not the leader of (indexed by EventId)
}

GEM3_Commands = { -- Commands struct for an Event (returned by GEM3_CMD_GetCommandInfosForEvent)
  ["id"] = STR -- Event ID
  ["channel"] = STR -- Channel
  ["leader"] = STR -- Leader
  ["ev_date"] = INT -- Event Date
  ["cmds"][] = array of array GEM3_PlayerCommand -- Indexed by PlayerName then sorted by stamp
}

GEM3_PlayerCommand = { -- Commands struct for a player (src)
  ["cmd"] = INT -- Command
  ["id"] = STR -- Event ID
  ["channel"] = STR -- Channel
  ["stamp"] = INT -- Stamp the command was first issued
  ["params"] = array of XYZ -- The params associated this the command
}

GEM3_Subscription = { -- Local subscription struct (init in GEM3_SUB_SubscribeMyself) - Infos for events I've subscribed to. Use GEM3_SUB_GetSubscribedInfos() to get infos for a player. Use GEM3_SUB_SubscribeUpdate() to send updated subscription
  ["name"] = --
  ["class"] = --
  ["role"] = --
  ["alt_role"] = --
  ["guild"] = --
  ["level"] = --
  ["comment"] = --
  ["force_queue"] = -- (one of GEM3_SUB_FORCE_QUEUE_xx)
  ["state"] = -- (one of GEM3_SUB_STATE_xx)
  ["unsub_comment"] = STRING -- Unsubscription comment
  ["main_time"] = --
  ["update_time"] = --
  ["source"] = --
}

GEM3_Event = { -- Event struct
  ["id"] = STR -- 
  ["channel"] = STR -- 
  ["leader"] = STR -- 
  ["ev_date"] = INT -- UTC time of event
  ["deadline"] = INT or NIL -- UTC time of signup deadline (Nil if no deadline)
  ["revision"] = INT -- Revision number of the event
  ["ev_place"] = STR -- 
  ["ev_comment"] = STR -- 
  ["ev_duration"] = INT -- Duration of the event, in seconds
  ["update_time"] = INT -- UTC time of last event modification
  ["recover_time"] = INT -- UTC time of last recover operation
  ["max_count"] = INT -- 
  ["min_lvl"] = INT -- 
  ["max_lvl"] = INT -- 
  ["classes"] = array of GEM3_Limit -- Classes limits (indexed by class name - english uppercase names)
  ["roles"] = array of GEM3_Limit -- Roles limits (indexed by role - GEM3_ROLE_xx)
  ["players"] = array of GEM3_Player -- All subscriptions received (and rejects), indexed by name
  ["sorttype"] = INT -- Sorting plugin's unique id
  ["closed_comment"] = STR or NIL -- Closed reason if event is closed, nil otherwise
  ["ev_type"] = INT -- Event type (one of GEM3_EVT_TYPE_xx)
  ["ev_subtype"] = INT -- Event sub-type (any "number")
  -- Runtime built values
  ["tit_count"] = INT
  ["sub_count"] = INT
  ["repl_count"] = INT
  ["rej_count"] = INT
}

GEM3_Limit = {
  ["min"] = INT or NIL -- Min requiered for this limit, or nil if no min requiered
  ["max"] = INT or NIL -- Max requiered for this limit, or nil if no max requiered
  -- Runtime built values
  ["tit_count"] = INT -- Current subscribed titulars for this limit
  ["sub_count"] = INT -- Current subscribed substitutes for this limit
  ["repl_count"] = INT -- Current subscribed replacements for this limit
}

GEM3_Player = { -- Subscriber struct (init in GEM3_SUB_BuildSubscriberInfos) - Infos for players having subscribe to the event
  ["name"] = STR -- Player's name
  ["class"] = STR -- Player's class
  ["role"] = INT -- Player's role (one of GEM3_ROLE_xx)
  ["level"] = INT -- Player's level
  ["guild"] = STR or NIL -- Guild name
  ["update_time"] = INT -- UTC time of player's updated subscription
  ["comment"] = STR -- Subscription comment
  ["force_queue"] = INT -- Queue the player should to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
  ["alt_role"] = INT -- Player's alternative role (one of GEM3_ROLE_xx)
  ["main_time"] = -- UTC time of player's main subscription
  ["source"] = STR -- Subscription source (one of GEM3_SUB_SOURCE_xx) -- Might be nil too
  -- Leader modified values
  ["current_queue"] = INT -- Queue the player is currently in (one of GEM3_SUB_STATE_xx)
  ["queue_pos"] = INT -- Pos in queue
  ["lead_force_queue"] = INT -- Queue forced by the leader (one of GEM3_SUB_FORCE_QUEUE_xx)
  -- Leader not shared values (not sent to others, except during a leader change)
}

GEM3_NonAckedSubscription = { -- Non acked subscription infos - Returned by GEM3_CMD_GetNonAckedSubscriptions as table element
  ["name"] = STR -- Player's name
  ["class"] = STR -- Player's class
  ["role"] = INT -- Player's role (one of GEM3_ROLE_xx)
  ["level"] = INT -- Player's level
  ["guild"] = STR or nil -- Guild name
  ["update_time"] = INT -- UTC time of player's subscription
  ["comment"] = STR -- Subscription comment
  ["force_queue"] = INT -- Queue the player should to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
  ["alt_role"] = INT -- Player's alternative role (one of GEM3_ROLE_xx)
  ["main_time"] = -- UTC time of player's main subscription
  ["source"] = STR -- Subscription source (one of GEM3_SUB_SOURCE_xx) -- Might be nil too
}

GEM3_SortPlugin = hidden struct

GEM3_SORT_Plugins = array of GEM3_SortPlugin -- Indexed by Plugin's name


----------------------- QUICK ACCESS

GEM3_QA_Events = GEM3_Events[GEM3_Realm];
GEM3_QA_Config = GEM3_Config[GEM3_Realm][GEM3_PlayerName];
GEM3_QA_Players = GEM3_Players[GEM3_Realm];
GEM3_QA_Channels = GEM3_Config[GEM3_Realm][GEM3_PlayerName].channels;


API functions
----------------------
Global:
--------
 GEM3_IsChannelInRerollList(pl_name,channel)
  Input:
   pl_name:  string -- Reroll name to check for channel
   channel:  string -- Real name of the channel to check
  Output:
   result: bool  -- True if reroll 'pl_name' has 'channel' in his configuration
  Purpose:
   Checks if one of my reroll has configured specified channel

 GEM3_IsMyReroll(name)
  Input:
   name:  string -- Reroll name to check
  Output:
   result: bool  -- True if 'name' is one of my rerolls
  Purpose:
   Checks if player is one of my rerolls


Channels:
--------
 GEM3_CHAN_AddChannel(name,pwd,alias,slash)
  Input:
   name:  string -- Name of the channel to add (automatically lowercased). If name is the GUILD constant, it will add the special guild channel
   pwd:   string -- Password to access the channel (not used for GUILD channel)
   alias: string -- Alias to be displayed (not used for GUILD channel)
   slash: string -- Slash command to talk on the channel (not used for GUILD channel)
  Output:
   result: bool  -- True if ok, false if error
  Events:
   "OnAddChannel"(chanstruct) is called on successful AddChannel (check chanstruct.saved.name for lowercased added name)
   "OnAddChannelFailed"(name,message) is called on error
  Purpose:
   Adds a new GEM channel

 GEM3_CHAN_DelChannel(name)
  Input:
   name: string -- Name of the channel to remove (lowercased). If name is the GUILD constant, it will remove the special guild channel
  Output:
   None
  Events:
   "OnDelChannel"(name) is called on successful DelChannel
  Purpose:
   Removes an existing GEM channel

 GEM3_CHAN_UpdateChannel(name,pwd,alias,slash)
  Input:
   name:  string -- Name of the channel to update (automatically lowercased). If name is the GUILD constant it won't do anything, because there is nothing to edit to this channel
   pwd:   string -- Password to access the channel
   alias: string -- Alias to be displayed
   slash: string -- Slash command to talk on the channel
  Output:
   None
  Events:
   "OnUpdateChannel"(chanstruct) is called on successful UpdateChannel
   "OnUpdateChannelFailed"(name,message) is called on error
  Purpose:
   Updates an existing channel (password and/or alias-slash)

 GEM3_CHAN_IsChannelActive(name)
  Input:
   name:  string -- Name of the channel to check (real name, not GUILD constant if checking special guild channel)
  Output:
   active: bool -- True if channel is a joined GEM channel for this character
  Purpose:
   Checks if a channel is a GEM channel, and initialized

 GEM3_CHAN_IsChannelConfiguredForAnyReroll(channel)
  Input:
   channel:  string -- Name of the channel to check (real name, not GUILD constant if checking special guild channel)
  Output:
   thisReroll: bool -- True if current player has specified channel configured (anyReroll is true if thisReroll is true)
   anyReroll: bool -- True if any of my rerolls has specified channel configured
  Purpose:
   Checks if any of my rerolls has specified channel in its config

 GEM3_CHAN_GetChannelID(name)
  Input:
   name:  string -- Name of the channel to check (real name, not GUILD constant if checking special guild channel)
  Output:
   chanid: int -- Internal ID used by WoW for channels (0 if not joined, -1 if special guild channel, >0 otherwise)
  Purpose:
   Gets a channel's internal ID (0 if not joined, -1 if special guild channel)

 GEM3_CHAN_GetChannelInfos(name)
  Input:
   name:  string -- Name of the channel to get infos (real name, not GUILD constant if checking special guild channel)
  Output:
   infos: array -- Saved channel struct (don't modify!!)
     infos.name     : string -- Real channel name
     infos.password : string -- Channel password (or "" if none)
     infos.alias    : string -- Alias for channel (or "" if none)
     infos.slash    : string -- Slash cmd to speak in channel (or "" if none)
     infos.guild    : bool   -- If channel is special guild channel
  Purpose:
   Gets channel's infos


Events:
--------
 GEM3_EVT_CreateNewEvent(channel,ev_leader,ev_date,deadline,ev_place,ev_comment,max_count,min_lvl,max_lvl,classes,roles,sortname,ev_type,ev_subtype,ev_duration)
  Input:
   channel:  string -- Channel tied to the event
   ev_leader:   string -- Leader of the event
   ev_date: timestamp -- UTC stamp for the event's date
   deadline: timestamp -- UTC stamp for the deadline signup
   ev_place: string -- Location of the event
   ev_comment: string -- Comment for the event
   max_count: number -- Max titulars count
   min_lvl: number -- Min level required
   max_lvl: number -- Max level required
   classes: array of GEM3_Limit -- Classes limits - indexed by CLASS_NAME (english uppercase names)
   roles: array of GEM3_Limit -- Roles limits - indexed by GEM3_ROLE_xx
   sortname: string -- Sort plugin name to use
   ev_type: number -- Event type (one of GEM3_EVT_TYPE_xx)
   ev_subtype: number -- Event sub-type (any "number")
   ev_duration: number -- Event duration (in seconds)
  Output:
   ev_id: string -- ev_id for the created event, or nil on error
   reason: string -- Failure message, Nil if ev_id is not nil
  Events:
   "OnEventCreated"(event) is called on successful event creation
  Purpose:
   Creates a new event

 GEM3_EVT_ModifyEvent(ev_id,ev_date,deadline,ev_place,ev_comment,max_count,min_lvl,max_lvl,classes,roles,sortname,ev_type,ev_subtype,ev_duration)
  Input:
   ev_id:  string -- Event id
   ev_date: timestamp -- UTC stamp for the event's date
   deadline: timestamp -- UTC stamp for the deadline signup
   ev_place: string -- Location of the event
   ev_comment: string -- Comment for the event
   max_count: number -- Max titulars count
   min_lvl: number -- Min level required
   max_lvl: number -- Max level required
   classes: array of GEM3_Limit -- Classes limits - indexed by CLASS_NAME (english uppercase names)
   roles: array of GEM3_Limit -- Roles limits - indexed by GEM3_ROLE_xx
   sortname: string -- Sort plugin name to use
   ev_type: number -- Event type (one of GEM3_EVT_TYPE_xx)
   ev_subtype: number -- Event sub-type (any "number")
   ev_duration: number -- Event duration (in seconds)
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Events:
   "OnUpdatedEvent"(event) is called on successful event edition
  Purpose:
   Edits an existing event (leader only) and broadcast it

 GEM3_EVT_CloseEvent(ev_id,reason)
  Input:
   ev_id:  string -- Event id
   reason:  string -- Reason
  Events:
   "OnClosedEvent"(event) is called if event successfully closed
  Purpose:
   Closes an opened event - Leader only

 GEM3_EVT_UnCloseEvent(ev_id)
  Input:
   ev_id:  string -- Event id
  Events:
   "OnUnClosedEvent"(event) is called if event successfully reopened
  Purpose:
   Reopens a closed event - Leader only

 GEM3_EVT_DeleteEvent(ev_id)
  Input:
   ev_id:  string -- Event id
  Events:
   "OnDeletedEvent"(event) is called if event successfully deleted
  Purpose:
   Fully deletes an event, with no possible reopening - Leader only

 GEM3_EVT_IgnoreEvent(ev_id)
  Input:
   ev_id:  string -- Event id
  Purpose:
   Ignores an event and all associated commmands

 GEM3_EVT_UnIgnoreEvent(ev_id)
  Input:
   ev_id:  string -- Event id
  Purpose:
   Unignores an event - Might takes some time before you fully recover it

 GEM3_EVT_IsEventIgnored(ev_id)
  Input:
   ev_id:  string -- Event id
  Output:
   ignored: boolean -- True if event is ignored, false otherwise
  Purpose:
   Checks if an event is currently ignored

 GEM3_EVT_SetAssistant(ev_id,pl_name)
  Input:
   ev_id:  string -- Event id
   pl_name: string -- Player to set as event assistant
  Purpose:
   Sets a player as event assistant. He'll have some rights over the event - Leader only

 GEM3_EVT_IsAssistant(ev_id,pl_name)
  Input:
   ev_id:  string -- Event id
   pl_name: string -- Player to check
  Output:
   isAssistant: boolean -- True if player is an event assistant, false otherwise
  Purpose:
   Checks if player is an event assistant

 GEM3_EVT_ArchiveEvent(ev_id|event)
  Input:
   ev_id|event:  string|GEM3_Event -- Event id (or event struct) to archive
  Output:
   archived: boolean -- True if the event has successfully been archived
  Purpose:
   Archives an event (fully duplicate the event)

 GEM3_EVT_DeleteArchivedEvent(ev_id)
  Input:
   ev_id:  string -- Event id to remove from archive
  Output:
   deleted: boolean -- True if the event has successfully been deleted from archive
  Purpose:
   Deletes an archived event

 GEM3_EVT_KickFromEvent(ev_id,pl_name,reason)
  Input:
   ev_id:  string -- Event id to remove from archive
   pl_name: string -- Player to kick from event
   reason: string -- Reason to kick the player from the event
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Kicks a subscriber from an event. Leader or assistant only

 GEM3_EVT_BanFromEvent(ev_id,pl_name,reason)
  Input:
   ev_id:  string -- Event id
   pl_name: string -- Player to ban from event
   reason: string -- Reason to ban the player from the event
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Bans a player from an event. Leader or assistant only

 GEM3_EVT_UnBanFromEvent(ev_id,pl_name)
  Input:
   ev_id:  string -- Event id
   pl_name: string -- Player to unban from event
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Un bans a player from an event. Leader or assistant only

 GEM3_EVT_GetBannedPlayers(ev_id)
  Input:
   ev_id:  string -- Event id
  Output:
   bans: Table of GEM3_Banned struct -- Returned table must be freed using GA_ReleaseTable(bans,2) when no longer needed -- Indexed by INT (use ipairs() to process)
  Purpose:
   Retrieves the list of banned players from an event


Commands:
--------
 GEM3_CMD_GetNonAckedSubscriptions(ev_id)
  Input:
   ev_id:  string -- EventID you want non acked subscriptions
  Output:
   subs: Table of GEM3_NonAckedSubscription struct (or nil) -- Returned table must be freed using GA_ReleaseTable(subs,10) when no longer needed -- Indexed by INT (use ipairs() to process)


Players:
--------
 GEM3_PLAY_IsPlayerInChannel(channel,player)
  Input:
   channel: string -- Name of the channel to check (real name, not GUILD constant if checking special guild channel)
   player:  string -- Name of the player to check
  Output:
   IsIn: bool -- True if player is in the joined GEM channel
  Purpose:
   Checks if a player is currently is a GEM channel

 GEM3_PLAY_GetPlayerInfos(player,channel)
  Input:
   player:  string -- Name of the player to get infos from
   channel: string or nil -- Name of the channel to get infos (nil will get infos for any channel)
  Output:
   Infos: Table -- Infos for the player, or Nil if nothing found (GEM3_ChannelPlayer struct)

 GEM3_PLAY_GetMyPlayerInfos(pl_name)
  Input:
   player:  string -- Name of my reroll to get infos from
  Output:
   Infos: Table -- Infos for the selected reroll, or Nil if nothing found (GEM3_ChannelPlayer struct)

 GEM3_PLAY_GetMyRerolls(channel)
  Input:
   channel:  string or nil -- Channel to get rerolls from, or all rerolls if nil
  Output:
   rerolls: Table of GEM3_ChannelPlayer struct -- Returned table must be freed using GA_ReleaseTable(rerolls,0) when no longer needed -- Indexed by INT (use ipairs() to process)

 GEM3_PLAY_IgnorePlayer(pl_name)
  Input:
   pl_name: string -- Name of the player
  Purpose:
   Ignores a player

 GEM3_PLAY_UnIgnorePlayer(pl_name)
  Input:
   pl_name: string -- Name of the player
  Purpose:
   Unignores a player

 GEM3_PLAY_IsPlayerIgnored(pl_name)
  Input:
   pl_name: string -- Name of the player
  Output:
   Ignored: bool -- True if player is ignored, false otherwise
  Purpose:
   Checks if a player is currently ignored

 GEM3_PLAY_GetAllowedRoles(class)
  Input:
   class: string -- Player's class
  Output:
   allowed_classes: bitfield -- Allowed roles for this class (one or many of GEM3_ROLE_xx)

 GEM3_PLAY_IsOfficer()
  Output:
   isOfficer: bool -- True if player is an officer, false otherwise
  Purpose:
   Checks if a player is an officer


Subscribers:
--------
 GEM3_SUB_GetSubscribedInfos(ev_id,pl_name)
  Input:
   ev_id       : String -- The event I have subscribed to
   pl_name     : String -- Name of my toon
  Output:
   infos      : GEM3_Subscription -- My subscription info for this event, or nil if none found
  Purpose:
   Gets my subscription infos for specified event and reroll name. If "pl_name" is nil, get any subscribed infos for this event

 GEM3_SUB_IsPlayerBanned(ev_id,pl_name)
  Input:
   ev_id       : String -- The event 
   pl_name     : String -- Name of the player to check (or nil to search all, and only, my toons)
  Output:
   Banned      : Boolean -- Returns true if "pl_name" is banned from "ev_id", false otherwise
   Reason      : String -- Returns banned reason if Banned is true, nil otherwise
  Purpose:
   Checks if a player is banned from an event. If "pl_name" is nil, search for any banned reroll from "ev_id"

 GEM3_SUB_IsPlayerKicked(ev_id,pl_name)
  Input:
   ev_id       : String -- The event
   pl_name     : String -- Name of my toon
  Output:
   Kicked      : Boolean -- Returns true if "pl_name" has been kicked from "ev_id", false otherwise
   Reason      : String -- Returns kicked reason if Kicked is true, nil otherwise
  Purpose:
   Checks if I've been kicked from an event. If "pl_name" is nil, search for any kicked reroll from "ev_id"

 GEM3_SUB_Subscribe(ev_id,pl_name,guild,level,class,role,comment,force_queue,alt_role)
  Input:
   ev_id       : String -- The event I have subscribed to
   pl_name     : String -- Name of my toon
   guild       : String -- Guild of my toon (or nil)
   level       : Int    -- My current level
   class       : String -- My class
   role        : Int -- Role of my class (one of GEM3_ROLE_xx)
   comment     : String -- My subscription comment
   force_queue : Int    -- Queue I want to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
   alt_role    : Int    -- Alternative role of my class (one of GEM3_ROLE_xx)
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Events:
   "OnSubscribed"(ev_id,pl_name,infos.state,GEM3_SUB_SOURCE_SELF) is called if I'm the leader, and I've successfully subscribed to my event
  Purpose:
   Subscribe to an event

 GEM3_SUB_SubscribeUpdate(ev_id,pl_name,guild,level,class,role,comment,force_queue,alt_role)
  Input:
   ev_id       : String -- The event I have subscribed to
   pl_name     : String -- Name of my toon
   guild       : String -- Guild of my toon (or nil)
   level       : Int    -- My current level
   class       : String -- My class
   role        : Int -- Role of my class (one of GEM3_ROLE_xx)
   comment     : String -- My subscription comment
   force_queue : Int    -- Queue I want to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
   alt_role    : Int    -- Alternative role of my class (one of GEM3_ROLE_xx)
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Send the updated version of my subscription

 GEM3_SUB_SubscribeExternal(ev_id,pl_name,guild,level,class,role,comment,force_queue,alt_role)
  Input:
   ev_id       : String -- The event id
   pl_name     : String -- Name of the player
   guild       : String -- Guild of the player (or nil)
   level       : Int    -- Current level of the player
   class       : String -- Class of the player
   role        : Int -- Role of the player (one of GEM3_ROLE_xx)
   comment     : String -- Player's subscription comment
   force_queue : Int    -- Queue to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
   alt_role    : Int    -- Alternative role of my class (one of GEM3_ROLE_xx)
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Subscribe another player to an event

 GEM3_SUB_ResendSubscription(ev_id,pl_name)
  Input:
   ev_id       : String -- The event I have subscribed to
   pl_name     : String -- Name of my toon
  Output:
   Result      : Boolean -- True if success, false otherwise
   Reason      : String  -- Failure message, Nil if Result is true
  Purpose:
   Resend subscription to the event (only when leader is connected), if the 1st one seems to have been lost

 GEM3_SUB_IsLatestSubscriptionReceived(ev_id,pl_name)
  Input:
   ev_id       : String -- The event I have subscribed to
   pl_name     : String -- Name of my toon
  Output:
   Result      : Boolean -- True if leader acked my latest subscription infos
  Purpose:
   Checks if my latest subscription informations has been received by the leader

 GEM3_SUB_ForceQueueForPlayer(ev_id,pl_name,force_queue)
  Input:
   ev_id       : String -- The event ID
   pl_name     : String -- Name of my toon
   force_queue : Int    -- Queue I want to be forced into (one of GEM3_SUB_FORCE_QUEUE_xx)
  Events:
   "OnUpdatedEvent"(event) is called if I'm the leader, and I've successfully altered the event
  Purpose:
   Force a queue for a subscriber - Leader only


Comm:
--------
 GEM3_COM_RegisterCustomMessage(identifier,func)
  Input:
   identifier: String   -- Unique identifier for your custom message
   func:       Function -- Function to be called when you receive a message with this identifier: function func(channel,from,stamp,...)
  Output:
   result: bool -- True if the custom dispatch has been registered. False if 'identifier' is already registered
  Purpose:
   Adds a command dispatch callback function

 GEM3_COM_RegisterCustomCommand(identifier,func)
  Input:
   identifier: String   -- Unique identifier for your custom message
   func:       Function -- Function to be called when you receive a command with this identifier: function func(channel,from,stamp,ev_id,...)
  Output:
   result: bool -- True if the custom dispatch has been registered. False if 'identifier' is already registered
  Purpose:
   Adds a command dispatch callback function

 GEM3_COM_SendCustomMessage(channel,identifier,...)
  Input:
   channel:    String -- Channel to send message to
   identifier: String -- Unique identifier for your custom message
   ...:        Any    -- Parameters to send
  Output:
   result: bool -- True if the custom dispatch for 'identifier' exists, false otherwise
  Purpose:
   Sends a custom message to all connected GEM users (message is volatile and not saved by GEM3_Core)

 GEM3_COM_SendCustomCommand(channel,identifier,ev_id,pl_dest,...)
  Input:
   channel:    String -- Channel to send message to
   identifier: String -- Unique identifier for your custom message
   ev_id     : String -- Event the command is tied to
   pl_dest   : String -- Player the command is destinated to
   ...:        Any    -- Parameters to send
  Output:
   result: bool -- True if the custom dispatch for 'identifier' and the event exists, false otherwise
  Purpose:
   Sends a custom command to all connected GEM users (command is stored by all users and broadcasted until pl_dest receives it)

 GEM3_COM_IsDisabled()
  Output:
   disabled: bool -- True if GEM communication have been disabled, false otherwise
   reason: string or nil -- The reason for GEM to have been disabled, or nil if GEM is fully functionnal
  Purpose:
   Checks if GEM communication have been disabled, and why.

 GEM3_COM_Disable(disabled,reason)
  Input:
   disabled: bool -- Set to true to disable GEM3 communications, false to re-enable it
   reason: string or nil -- The reason to disable GEM (ignored if disabled=true)
  Purpose:
   Enables or disables GEM communications.


Sync:
--------
 GEM3_SYNC_CheckForExternalCommands()
  Purpose:
   Checks GEM3_ExternalCommands table for external commands



API callbacks
----------------------
OnCrashedEvent(event):
 Either GEM3_EVT_RecoverLostEvent(event) or GEM3_EVT_RejectLostEvent(event) or GEM3_EVT_DeleteLostEvent(event,reason) must be called when the user has finished analysing the crashed event.
 WARNING: This CB can be called multiple times in a second, it's the UI's job to store and process all passed event. It's possible for the same event.id to be passed (but only with an updated version), so the UI must refresh (and inform the user) if an updated version comes when he looks at the previous one.

OnUpdatedRecoveredEvent(new_event,previous_event)
 Either GEM3_EVT_AcceptUpdatedRecoveredEvent(new_event) or GEM3_EVT_RejectUpdatedRecoveredEvent(new_event) must be called when the user has finished analysing the updated event.
 WARNING: This CB can be called multiple times in a second, it's the UI's job to store and process all passed event. It's possible for the same event.id to be passed (but only with an updated version), so the UI must refresh (and inform the user) if an updated version comes when he looks at the previous one.


Imported commands (SavedVariables file)
----------------------------------------
You must modify the GEM3_ExternalCommands variable (table, INT indexed).
Supported commands: "Subscribe"
Mandatory fields for all commands:
 "protocol" (INT) -- Current GEM3_COM_PROTOCOL_REVISION
 "cmd" (STRING) -- One of the supported commands
 "channel" (STRING) -- Channel the event is tied to
 "ev_id" (STRING) -- Event's id
 "leader" (STRING) -- Event's leader
 "ev_date" (INT) -- Event's date

"Subscribe" command:
--------
The command must have the following fields:
 "pl_name" (STRING) -- Name of the subscriber
 "guild" (STRING) -- Subscriber's guild
 "level" (INT) -- Subscriber's level
 "class" (STRING) -- Subscriber's class (uppercase english class name)
 "role" (INT) -- One of the GEM3_ROLE_xx constants
 "comment" (STRING)
 "queue" (INT) -- One of the GEM3_SUB_FORCE_QUEUE_xx constants
 "main_time" (INT) -- UTC time the subscription was first issued or majorly modified (role/queue change) (using web server's os.time() function, be careful that the server's clock is correctly set!!)
 "update_time" (INT) -- UTC time the subscription was changed (using web server's os.time() function, be careful that the server's clock is correctly set!!)
 "alt_role" (INT) -- Alternative role (One of the GEM3_ROLE_xx constants)
 "source" (STRING) -- Source (must be GEM3_SUB_SOURCE_WEB)

Exemple:
GEM3_ExternalCommands[1] = { protocol=3.003, cmd="Subscribe", channel="guild-myguild", ev_id="Kiki1.342", leader="Kiki", ev_date=1229999500, pl_name="Loona", guild="my guild", level=70, class="DRUID", role=2, comment="", queue=1, stamp=212151570};

----------------------- A CHECK / VIRER
Occurence GEM3_DefaultSendChannel
Remplacer GEM3_Defaults_Banned par GEM3_Defaults["banned"]
Verifier l'init de toutes les SharedVars de GEM3_core.lua
Replacer GEM3_COM_Channels par GEM3_RT_Channels
Virer GEM3_COM_Channels[channame].default (ancien GEM3_DefaultSendChannel)
Remplacer les "== GUILD" par "GEM3_RT_Channels[name].guild"
Virer les GEM3_YouAreDrunk
GEM3_Events.next_event_id -> GEM3_Config.next_event_id
GEM3_Events.realms -> GEM3_Events[] (direct tableau des realms... utiliser GEM3_QA_Events)
Rechercher les "GEM3_Events." a remplacer par "GEM3_Config."
GEM3_Events.realms[GEM3_Realm].my_names[GEM3_PlayerName] -> GEM3_QA_Config
GEM3_Events.realms[GEM3_Realm].my_names[XX] -> GEM3_Config[GEM3_Realm][XX]
GEM3_Events.realms[GEM3_Realm] -> GEM3_QA_Events
GEM3_GetChannelNameOrGuild -> GEM3_CHAN_GetChannelID

--------------- A AJOUTER
Queues de message
GEM3_COM_PurgeQueueMessageForChannel



Subscription
----------------------
result,reason = GEM3_SUB_Subscribe(ev_id,pl_name,guild,level,class,role,comment,force_queue)
if(reason == GEM3_LOC_SUB_SUBSCRIBE_PENDING)
then
 -- Check if leader is connected to the channel
 if(GEM3_PLAY_IsPlayerInChannel(channel,leader))
 then
   -- If connected, ask the user if he want to resend his subscription (dialog box)
   -- If he accepts
   GEM3_SUB_ResendSubscription(ev_id,pl_name)
 end
-- GUI CB "OnSubscriptionLost" can do the same

Subscription update
----------------------
result,reason = GEM3_SUB_SubscribeUpdate(ev_id,pl_name,guild,level,class,role,comment,force_queue)
if(reason == GEM3_LOC_SUB_SUBSCRIBE_UPDATE_SAME and not GEM3_SUB_IsLatestSubscriptionReceived(ev_id,pl_name) and GEM3_PLAY_IsPlayerInChannel(channel,leader)) -- Same data, but not acked and leader is online
then
 -- Ask the user if he want to resend his subscription (dialog box)
 -- If he accepts
 GEM3_SUB_ResendSubscription(ev_id,pl_name)
end
