vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
gGroupCalendar_Invites =
{
        Event = nil,
        Status = nil,
        Group = nil,
        Inviting = false,
        ChangedFunc = nil,
        ChangedFuncParam = nil,
        GroupChanged = false,
        NextInviteDelay = 0,
        MaxInvitesPerTimeSlice = 1,
        InvitationSliceInterval = 0.2,
};

GroupCalendar_cInviteStatusMessages =
{
        NeedSelection = GroupCalendar_cInviteNeedSelectionStatus,
        Ready = GroupCalendar_cInviteReadyStatus,
        InitialInvites = GroupCalendar_cInviteInitialInvitesStatus,
        AwaitingAcceptance = GroupCalendar_cInviteAwaitingAcceptanceStatus,
        ConvertingToRaid = GroupCalendar_cInviteConvertingToRaidStatus,
        Inviting = GroupCalendar_cInviteInvitingStatus,
        Complete = GroupCalendar_cInviteCompleteStatus,
        ReadyToRefill = GroupCalendar_cInviteReadyToRefillStatus,
-- The "no more available" message is kind of useless, so for now
-- I'm just showing it as completed instead until I figure out a 
-- better use for that state
        NoMoreAvailable = GroupCalendar_cInviteNoMoreAvailableStatus,
--      NoMoreAvailable = GroupCalendar_cInviteCompleteStatus,
        RaidFull = GroupCalendar_cRaidFull,
};

function CalendarGroupInvites_BeginEvent(pDatabase, pEvent)
        -- Just return if it's still the same event
        
        if pEvent == gGroupCalendar_Invites.Event then
                return;
        end
        
        gGroupCalendar_Invites.Database = pDatabase;
        gGroupCalendar_Invites.Event = pEvent;
        gGroupCalendar_Invites.Limits = pEvent.mLimits;
        
        if not gGroupCalendar_Invites.Limits then
                gGroupCalendar_Invites.Limits = EventDatabase_GetStandardLimitsByID(pEvent.mType);
        end
        
        if gGroupCalendar_Invites.Limits
        and not gGroupCalendar_Invites.Limits.mPriorityOrder then
                gGroupCalendar_Invites.Limits.mPriorityOrder = gGroupCalendar_PlayerSettings.AutoSelectPriorityOrder;
        end
        
        gGroupCalendar_Invites.Group = CalendarAttendanceList_New();
        gGroupCalendar_Invites.Group.NumJoinedMembers = 0;
        gGroupCalendar_Invites.Group.NumJoinedOrInvited = 0;
        
        gGroupCalendar_Invites.GroupChanged = true;
        gGroupCalendar_Invites.SortBy = "Date";

        CalendarGroupInvites_UpdateGroup(gGroupCalendar_Invites.Group);
        
        -- Determine the status
        
        local   vEventMaximumAttendance = CalendarGroupInvites_GetEventMaxAttendance(pEvent);
        
        if gGroupCalendar_Invites.Group.NumJoinedOrInvited >= vEventMaximumAttendance then
                CalendarGroupInvites_SetStatus("Complete");
        else
                CalendarGroupInvites_SetReadyStatus();
        end
        
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_EndEvent(pEvent)
        if pEvent ~= gGroupCalendar_Invites.Event then
                return;
        end
        
        CalendarGroupInvites_SetChangedFunc(nil, nil);
        
        gGroupCalendar_Invites.Event = nil;
        gGroupCalendar_Invites.Status = nil;
        gGroupCalendar_Invites.Group = nil;
end

function CalendarGroupInvites_SetChangedFunc(pChangedFunc, pChangedFuncParam)
        gGroupCalendar_Invites.ChangedFunc = pChangedFunc;
        gGroupCalendar_Invites.ChangedFuncParam = pChangedFuncParam;
end

function CalendarGroupInvites_EventChanged(pEvent)
        if pEvent ~= gGroupCalendar_Invites.Event then
                return;
        end
        
        CalendarGroupInvites_UpdateGroup(gGroupCalendar_Invites.Group);
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_SetReadyStatus()
        if CalendarGroupInvites_HasSelection() then
                CalendarGroupInvites_SetStatus("Ready");
        else
                CalendarGroupInvites_SetStatus("NeedSelection");
        end
end

function CalendarGroupInvites_SetStatus(pStatus)
        gGroupCalendar_Invites.Status = pStatus;
        gGroupCalendar_Invites.GroupChanged = true;
end

function CalendarGroupInvites_NotifyGroupChanged()
        if gGroupCalendar_Invites.GroupChanged then
                if gGroupCalendar_Invites.ChangedFunc then
                        gGroupCalendar_Invites.ChangedFunc(gGroupCalendar_Invites.ChangedFuncParam);
                end
                
                gGroupCalendar_Invites.GroupChanged = false;
        end
end

function CalendarGroupInvites_GetUpdateDelay()
end

function CalendarGroupInvites_NeedsUpdateTimer()
        return gGroupCalendar_Invites.Inviting;
end

function CalendarGroupInvites_Update(pElapsed)
        if not gGroupCalendar_Invites.Inviting then
                return;
        end
        
        gGroupCalendar_Invites.NextInviteDelay = gGroupCalendar_Invites.NextInviteDelay - pElapsed;
        
        if gGroupCalendar_Invites.NextInviteDelay <= 0 then
                CalendarGroupInvites_InviteNow();
                gGroupCalendar_Invites.NextInviteDelay = gGroupCalendar_Invites.InvitationSliceInterval;
        end
end

function CalendarGroupInvites_PartyMembersChanged()
        if not gGroupCalendar_Invites.Event then
                return;
        end

        CalendarGroupInvites_UpdateGroup(gGroupCalendar_Invites.Group);
        
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_PartyLootMethodChanged()
        gGroupCalendar_Invites.PartyFormed = true;
end

function CalendarGroupInvites_SystemMessage(pMessage)
        -- See if someone declined an invitation
        
        local   vStartIndex, vEndIndex, vName = string.find(pMessage, GroupCalendar_cInviteDeclinedSysMsg);
        
        if vStartIndex then
                CalendarGroupInvites_PlayerDeclinedInvitation(vName);
                return;
        end
        
        -- See if they are already in a group
        
        vStartIndex, vEndIndex, vName = string.find(pMessage, GroupCalendar_cAlreadyGroupedSysMsg);
        
        if vStartIndex then
                CalendarGroupInvites_PlayerAlreadyGrouped(vName);
                return;
        end
        
        -- See if they're not online
        
        vStartIndex, vEndIndex, vName = string.find(pMessage, GroupCalendar_cNoSuchPlayerSysMsg);
        
        if vStartIndex then
                CalendarGroupInvites_NoSuchPlayer(vName);
                return;
        end
end

function CalendarGroupInvites_GuildRosterChanged()
        if gGroupCalendar_Invites.Group then
                CalendarGroupInvites_UpdateGroup(gGroupCalendar_Invites.Group);
        end
end

function CalendarGroupInvites_UpdateGroup(pGroup)
        -- Add the attendance info
        
        CalendarGroupInvites_MergeEventAttendance(pGroup, gGroupCalendar_Invites.Event, true);
        
        -- First mark all the joined members as "Left"  and
        -- count the invited members. Also check their offline
        -- status using the guild roster
        
        local   vGuildRoster = CalendarNetwork_GetGuildRosterCache();
        
        for vIndex, vPlayer in pGroup.Items do
                if vPlayer.mGroupStatus == "Joined" then
                        vPlayer.mGroupStatus = "Left";
                end
                
                if vGuildRoster then
                        local   vUpperName = strupper(vPlayer.mName);
                        local   vMemberInfo = vGuildRoster[vUpperName];
                        
                        if vMemberInfo then
                                vPlayer.mOffline = not vMemberInfo.Online;
                        end
                end
        end
        
        -- Now scan the group and update everyone who's in
        
        local   vNumRaidMembers = GetNumRaidMembers();
        
        if vNumRaidMembers > 0 then
                for vIndex = 1, vNumRaidMembers do
                        local   vName, vRank, vSubgroup, vLevel, vClass, vFileName, vZone, vOnline, vIsDead = GetRaidRosterInfo(vIndex);
                        
                        if vName then
                                local   vClassCode = EventDatabase_GetClassCodeByClass(vClass);
                                local   vPlayer = pGroup.Items[vName];
                                
                                if vPlayer then
                                        vPlayer.mRaidRank = vRank;
                                        vPlayer.mZone = vZone;
                                        vPlayer.mOffline = not vOnline;
                                        vPlayer.mDead = vIsDead;
                                        vPlayer.mGroupStatus = "Joined";
                                        vPlayer.mSelected = nil; -- Deselect players as they join the group
                                else
                                        vPlayer = 
                                        {
                                                mType = "Player",
                                                mName = vName,
                                                mRaidRank = vRank,
                                                mGroupNumber = vSubGroup,
                                                mLevel = vLevel,
                                                mClassCode = vClassCode,
                                                mZone = vZone,
                                                mOffline = not vOnline,
                                                mDead = vIsDead,
                                                mGroupStatus = "Joined",
                                        };
                                        
                                        pGroup.Items[vName] = vPlayer;
                                end
                        end
                end
        else
                for vIndex = 0, MAX_PARTY_MEMBERS do
                        local   vUnitID = nil;
                        
                        if vIndex == 0 then
                                vUnitID = "player";
                        elseif GetPartyMember(vIndex) then
                                vUnitID = "party"..vIndex;
                        else
                                vUnitID = nil;
                        end
                        
                        if vUnitID then
                                local   vName = UnitName(vUnitID);
                                
                                -- Map the party info to a raid rank
                                
                                local   vRank = 0;
                                
                                if GetNumPartyMembers() == 0
                                or UnitIsPartyLeader(vUnitID) then
                                        vRank = 2;
                                end
                                
                                --
                                
                                local   vClassCode = EventDatabase_GetClassCodeByClass(UnitClass(vUnitID));
                                local   vPlayer = pGroup.Items[vName];
                                
                                if vPlayer then
                                        vPlayer.mRaidRank = vRank;
                                        vPlayer.mOffline = not UnitIsConnected(vUnitID);
                                        vPlayer.mDead = UnitIsDeadOrGhost(vUnitID);
                                        vPlayer.mGroupStatus = "Joined";
                                        vPlayer.mSelected = nil; -- Deselect players as they join the group
                                else
                                        vPlayer =
                                        {
                                                mType = "Player",
                                                mName = vName,
                                                mRaidRank = vRank,
                                                mGroupNumber = 1,
                                                mLevel = UnitLevel(vUnitID),
                                                mClassCode = vClassCode,
                                                --mZone = vZone,
                                                mOffline = not UnitIsConnected(vUnitID),
                                                mDead = UnitIsDeadOrGhost(vUnitID),
                                                mGroupStatus = "Joined",
                                        };
                                        
                                        pGroup.Items[vName] = vPlayer;
                                end
                        end
                end
        end
        
        -- 
        
        CalendarGroupInvites_SelectionChanged();
        gGroupCalendar_Invites.GroupChanged = true;
end

function CalendarGroupInvites_SetSortMode(pSortBy)
        gGroupCalendar_Invites.SortBy = pSortBy;
        
        if gGroupCalendar_Invites.Group then
                CalendarGroupInvites_SortGroup(gGroupCalendar_Invites.Group);
                CalendarGroupInvites_NotifyGroupChanged();
        end
end

function CalendarGroupInvites_EventChanged(pDatabase, pEvent)
        if gGroupCalendar_Invites.Event ~= pEvent
        or not gGroupCalendar_Invites.Group then
                return;
        end
        
        CalendarGroupInvites_UpdateGroup(gGroupCalendar_Invites.Group);
end

function CalendarGroupInvites_GetGroupItemCategory(pItem)
        if pItem.mStatus == "Y"
        or pItem.mGroupStatus == "Joined"
        or pItem.mGroupStatus == "Left"
        or pItem.mSelected then
                if pItem.mClassCode then
                        return pItem.mClassCode;
                else
                        return "?";
                end
        end

        return CalendarAttendanceList_GetRSVPClassCategory(pItem);
end

function CalendarGroupInvites_SortGroup(pGroup)
        -- Sort into categories
        
        local   vNumAttendees = pGroup.NumAttendees;
        CalendarAttendanceList_SortIntoCategories(pGroup, CalendarGroupInvites_GetGroupItemCategory);
        pGroup.NumAttendees = vNumAttendees;
        
        -- Sort the categories
        
        table.sort(pGroup.SortedCategories, EventDatabase_CompareClassCodes);
        
        -- Sort the attendance within each category
        
        for vCategory, vClassInfo in pGroup.Categories do
                table.sort(vClassInfo.mAttendees, CalendarGroupInvites_GetGroupItemCompare(gGroupCalendar_Invites.SortBy));
        end

        gGroupCalendar_Invites.GroupChanged = true;
end

GroupCalendar_cGroupStatusOrder =
{
        Joined = 1,
        Invited = 2,
        Ready = 3,
        Grouped = 4,
        Standby = 5,
        Declined = 6,
        Offline = 7,
        Left = 8,
};

GroupCalendar_cGroupStatusMessages =
{
        Joined = GroupCalendar_cJoinedGroupStatus,
        Invited = GropuCalendar_cInvitedGroupStatus,
        Ready = GropuCalendar_cReadyGroupStatus,
        Grouped = GroupCalendar_cGroupedGroupStatus,
        Standby = GroupCalendar_cStandbyGroupStatus,
        Declined = GroupCalendar_cDeclinedGroupStatus,
        Offline = GroupCalendar_cOfflineGroupStatus,
        Left = GroupCalendar_cLeftGroupStatus,
};

GroupCalendar_cRSVPStatusToGroupStatus = 
{
        Y = "Ready",
        S = "Standby",
};

function CalendarGroupInvites_CompareGroupItems(pItem1, pItem2, pSecondaryCompareFunc)
        if not pItem1 then
                if not pItem2 then
                        Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem1 is nil");
                else
                        Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem2 is nil");
                end
                
                return false;
        end
        
        if not pItem2 then
                Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem2 is nil");
                return true;
        end
        
        -- Compare by status first
        
        local   vOrder1 = GroupCalendar_cGroupStatusOrder[pItem1.mGroupStatus];
        local   vOrder2 = GroupCalendar_cGroupStatusOrder[pItem2.mGroupStatus];
        
        if not vOrder1 then
                Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem1: Unknown status "..pItem1.mGroupStatus);
                
                if not vOrder2 then
                        Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem2: Unknown status "..pItem2.mGroupStatus);
                end
                
                return false;
        end
        
        if not vOrder2 then
                Calendar_ErrorMessage("CalendarGroupInvites_CompareGroupItems: pItem2: Unknown status "..pItem2.mGroupStatus);
                return true;
        end
        
        if vOrder1 ~= vOrder2 then
                return vOrder1 < vOrder2;
        end
        
        -- Use the secondary comparison
        
        return pSecondaryCompareFunc(pItem1, pItem2);
end

function CalendarGroupInvites_CompareGroupItemsByRank(pItem1, pItem2)
        return CalendarGroupInvites_CompareGroupItems(pItem1, pItem2, EventDatabase_CompareRSVPsByRankAndDate);
end

function CalendarGroupInvites_CompareGroupItemsByDate(pItem1, pItem2)
        return CalendarGroupInvites_CompareGroupItems(pItem1, pItem2, EventDatabase_CompareRSVPsByDate);
end

function CalendarGroupInvites_CompareGroupItemsByName(pItem1, pItem2)
        return CalendarGroupInvites_CompareGroupItems(pItem1, pItem2, EventDatabase_CompareRSVPsByName);
end

function CalendarGroupInvites_GetGroupItemCompare(pSortBy)
        if pSortBy == "Rank" then
                return CalendarGroupInvites_CompareGroupItemsByRank;
        elseif pSortBy == "Date" then
                return CalendarGroupInvites_CompareGroupItemsByDate;
        elseif pSortBy == "Name" then
                return CalendarGroupInvites_CompareGroupItemsByName;
        else
                Calendar_ErrorMessage("GroupCalendar: Unknown sorting method in CalendarGroupInvites_GetGroupItemCompare");
        end
end

function CalendarGroupInvites_ComparePlayerItemsByDate(pPlayer1, pPlayer2)
        if not pPlayer1.mDate then
                return false;
        elseif not pPlayer2.mDate then
                return true;
        end
        
        if pPlayer1.mDate < pPlayer2.mDate then
                return true;
        elseif pPlayer1.mDate > pPlayer2.mDate then
                return false;
        elseif pPlayer1.mTime ~= pPlayer2.mTime then
                return pPlayer1.mTime < pPlayer2.mTime;
        else
                return pPlayer1.mName < pPlayer2.mName;
        end
end

function CalendarGroupInvites_GetEventMaxAttendance(pEvent)
        if not pEvent.mLimits
        or not pEvent.mLimits.mMaxAttendance then
                return MAX_RAID_MEMBERS;
        end
                
        return pEvent.mLimits.mMaxAttendance;
end

function CalendarGroupInvites_GetNextInvitee(pGroup)
        for vCategory, vCategoryInfo in pGroup.Categories do
                for vIndex, vPlayer in vCategoryInfo.mAttendees do
                        if vPlayer.mNeedsInvite then
                                return vPlayer;
                        end
                end
        end
        
        return nil;
end

function CalendarGroupInvites_InviteNow()
        if not gGroupCalendar_Invites.Event then
                if gGroupCalendar_Settings.DebugInvites then
                        Calendar_DebugMessage("Skipping invites: no event associated");
                end
                
                gGroupCalendar_Invites.Inviting = false;
                return;
        end
        
        -- Get the maximum size for the group
        
        local   vMaxPartyMembers;
        
        CalendarGroupInvites_SetStatus("Inviting");
        
        if GetNumRaidMembers() == 0 then
                vMaxPartyMembers = MAX_PARTY_MEMBERS + 1; -- +1 because Blizzard doesn't include the player in the MAX_PARTY_MEMBERS count
        else
                vMaxPartyMembers = MAX_RAID_MEMBERS;
        end
        
        --
        
        if gGroupCalendar_Settings.DebugInvites then
                Calendar_DebugMessage("Starting invites: MaxPartyMembers: "..vMaxPartyMembers);
        end
        
        -- Count the number of outstanding invitations
        
        local   vNumInvitesSent = 0;
        
        for vExcessLooping = 1, 40 do
                -- Don't allow too many invitations in one burst in order to prevent
                -- Blizzard's spammer filters from kicking us offline
                
                if vNumInvitesSent >= gGroupCalendar_Invites.MaxInvitesPerTimeSlice then
                        if gGroupCalendar_Settings.DebugInvites then
                                Calendar_DebugMessage("Maximum invites per time slice reached");
                        end
                        
                        return;
                end
                
                if gGroupCalendar_Settings.DebugInvites then
                        Calendar_DebugMessage("NumJoinedOrInvited: "..gGroupCalendar_Invites.Group.NumJoinedOrInvited);
                end
                
                -- Get the next player to invite
                
                local   vPlayer = CalendarGroupInvites_GetNextInvitee(gGroupCalendar_Invites.Group);
                
                -- Done if there are no more players to add
                
                if not vPlayer then
                        CalendarGroupInvites_SetStatus("Complete");
                        gGroupCalendar_Invites.Inviting = false;
                        
                        if gGroupCalendar_Settings.DebugInvites then
                                Calendar_DebugMessage("No more players to invite");
                        end
                        
                        return;
                end
                
                -- See if there's room for more
                
                if gGroupCalendar_Invites.Group.NumJoinedOrInvited >= vMaxPartyMembers then
                        if GetNumRaidMembers() == 0 then
                                -- Convert to a raid
                                
                                if gGroupCalendar_Invites.Group.NumJoinedMembers > 1
                                and gGroupCalendar_Invites.PartyFormed then
                                        CalendarGroupInvites_SetStatus("ConvertingToRaid");
                                        
                                        if gGroupCalendar_Settings.DebugInvites then
                                                Calendar_DebugMessage("Converting to raid");
                                        end
                                        
                                        ConvertToRaid();
                                
                                -- Wait for at least one player to accept
                                
                                else
                                        CalendarGroupInvites_SetStatus("AwaitingAcceptance");
                                        if gGroupCalendar_Settings.DebugInvites then
                                                Calendar_DebugMessage("Waiting for players to accept");
                                        end
                                end
                        
                        -- This state is only reached if the raid is full and the player
                        -- tries to invite more
                        
                        else
                                CalendarGroupInvites_SetStatus("RaidFull");
                                gGroupCalendar_Invites.Inviting = false;
                        end
                        
                        return;
                end
                
                -- Invite the player
                
                if gGroupCalendar_Settings.DebugInvites then
                        Calendar_DebugMessage("Inviting "..vPlayer.mName);
                end
                
                SendChatMessage(
                                format(GroupCalendar_cInviteWhisperFormat, EventDatabase_GetEventDisplayName(gGroupCalendar_Invites.Event)),
                                "WHISPER",
                                nil,
                                vPlayer.mName);
                
                InviteByName(vPlayer.mName);
                
                vPlayer.mGroupStatus = "Invited";
                vPlayer.mNeedsInvite = nil;
                
                vNumInvitesSent = vNumInvitesSent + 1;
                
                gGroupCalendar_Invites.Group.NumJoinedOrInvited = gGroupCalendar_Invites.Group.NumJoinedOrInvited + 1;
                
                gGroupCalendar_Invites.GroupChanged = true;
        end -- for
        
        Calendar_ErrorMessage("GroupCalendar: Internal Error: InviteNow() not terminating properly");
end

function CalendarAttendanceList_GetRSVPClassCategory(pItem)
        local   vCategoryID = CalendarAttendanceList_GetRSVPStatusCategory(pItem);
        
        if not vCategoryID then
                return nil;
        end
        
        if vCategoryID ~= "YES" then
                return vCategoryID;
        end
        
        if pItem.mClassCode then
                return pItem.mClassCode;
        end
        
        return "?";
end

function CalendarGroupInvites_MergeEventAttendance(pGroup, pEvent, pSortByClass)
        if not pEvent
        or not pEvent.mAttendance then
                return;
        end
        
        for vAttendeeName, vRSVPString in pEvent.mAttendance do
                local   vRSVP = EventDatabase_UnpackEventRSVP(nil, vAttendeeName, pEvent.mID, vRSVPString);
                local   vCategoryID = CalendarAttendanceList_GetRSVPClassCategory(vRSVP);
                
                if vCategoryID
                and vCategoryID ~= "NO" then
                        local   vItem = pGroup.Items[vRSVP.mName];
                        
                        if not vItem then
                                vRSVP.mType = "RSVP";
                                vRSVP.mGroupStatus = GroupCalendar_cRSVPStatusToGroupStatus[vRSVP.mStatus];
                                
                                if vRSVP.mGroupStatus then
                                        pGroup.Items[vRSVP.mName] = vRSVP;
                                end
                        else
                                -- Just update the status and
                                -- player info fields
                                
                                vItem.mStatus = vRSVP.mStatus;
                                vItem.mComment = vRSVP.mComment;
                                vItem.mGuild = vRSVP.mGuild;
                                vItem.mGuildRank = vRSVP.mGuildRank;
                                
                                if vItem.mGroupStatus == "Standby"
                                or vItem.mGroupStatus == "Ready" then
                                        vItem.mGroupStatus = GroupCalendar_cRSVPStatusToGroupStatus[vRSVP.mStatus];
                                end
                        end
                end
        end
end

function CalendarGroupInvites_InviteSelectedPlayers()
        -- Reset the SentInvite flag
        
        for vCategory, vCategoryInfo in gGroupCalendar_Invites.Group.Categories do
                for vIndex, vPlayer in vCategoryInfo.mAttendees do
                        if vPlayer.mSelected then
                                vPlayer.mNeedsInvite = true;
                        end
                end
        end
        
        -- Arm the trigger for when the party is actually
        -- formed (needed to make ConvertToRaid() work properly)
        
        if GetNumRaidMembers() ~= 0
        or GetNumPartyMembers() ~= 0 then
                gGroupCalendar_Invites.PartyFormed = true;
        else
                gGroupCalendar_Invites.PartyFormed = false;
        end
        
        -- Start inviting
        
        gGroupCalendar_Invites.Inviting = true;
        gGroupCalendar_Invites.NextInviteDelay = 0;
        GroupCalendar_StartUpdateTimer();
        
        CalendarGroupInvites_InviteNow();
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_HasSelection()
        for vIndex, vPlayer in gGroupCalendar_Invites.Group.Items do
                if vPlayer.mSelected then
                        return true;
                end
        end
        
        return false;
end

function CalendarGroupInvites_ClearSelection()
        for vIndex, vPlayer in gGroupCalendar_Invites.Group.Items do
                vPlayer.mSelected = nil;
        end
        
        CalendarGroupInvites_SelectionChanged();
end

function CalendarGroupInvites_SetItemSelected(pItem, pSelected)
        if not pSelected then
                pSelected = nil;
        end
        
        if pItem.mSelected == pSelected then
                return;
        end
        
        pItem.mSelected = pSelected;
        
        CalendarGroupInvites_SelectionChanged();
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_SelectionChanged()
        local   vNumSelected = 0;
        local   vNumJoined = 0;
        local   vNumAttendees = 0;
        local   vNumJoinedOrInvited = 0;
        
        for vIndex, vPlayer in gGroupCalendar_Invites.Group.Items do
                if vPlayer.mGroupStatus == "Joined" then
                        vNumJoined = vNumJoined + 1;
                        vNumJoinedOrInvited = vNumJoinedOrInvited + 1;
                        vNumAttendees = vNumAttendees + 1;
                else
                        if vPlayer.mSelected then
                                vNumSelected = vNumSelected + 1;
                                vNumAttendees = vNumAttendees + 1;
                        end
                        
                        if vPlayer.mGroupStatus == "Invited" then
                                vNumJoinedOrInvited = vNumJoinedOrInvited + 1;
                        end
                end
        end
        
        gGroupCalendar_Invites.Group.NumAttendees =  vNumAttendees;
        gGroupCalendar_Invites.Group.NumJoinedMembers = vNumJoined;
        gGroupCalendar_Invites.Group.NumJoinedOrInvited = vNumJoinedOrInvited;
        
        gGroupCalendar_Invites.NumSelected = vNumSelected;
        
        CalendarGroupInvites_SortGroup(gGroupCalendar_Invites.Group);
        CalendarGroupInvites_SetReadyStatus();
end

function CalendarGroupInvites_AutoSelectPlayers()
        CalendarClassLimits_Open(gGroupCalendar_Invites.Limits, GroupCalendar_cAutoSelectWindowTitle, true, CalendarGroupInvites_AutoSelectFromLimits);
end

function CalendarGroupInvites_AutoSelectFromLimits(pLimits)
        gGroupCalendar_Invites.Limits = pLimits;
        gGroupCalendar_PlayerSettings.AutoSelectPriorityOrder = pLimits.mPriorityOrder;
        
        CalendarGroupInvites_ClearSelection();
        
        --
        
        local   vAvailableSlots = EventAvailableSlots_InitializeFromLimits(pLimits);
        
        -- Count existing players and accumulate the rest as prospective members
        
        local   vProspects = {};
        
        for vCategory, vCategoryInfo in gGroupCalendar_Invites.Group.Categories do
                for vIndex, vPlayer in vCategoryInfo.mAttendees do
                        if vPlayer.mGroupStatus == "Joined"
                        or vPlayer.mGroupStatus == "Invited" then
                                CalendarEvent_PlayerClassAdded(vAvailableSlots, vPlayer.mClassCode)
                        else
                                table.insert(vProspects, vPlayer);
                        end
                end
        end
        
        -- Sort the prospects by the selected priority
        
        table.sort(vProspects, CalendarGroupInvites_GetGroupItemCompare(pLimits.mPriorityOrder));
        
        -- Add them
        
        for vIndex, vPlayer in vProspects do
                if EventAvailableSlots_AcceptPlayer(vAvailableSlots, vPlayer.mClassCode) then
                        vPlayer.mSelected = true;
                end
        end
        
        CalendarGroupInvites_SelectionChanged();
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_PlayerDeclinedInvitation(pName)
        local   vPlayer = CalendarAttendanceList_FindItem(gGroupCalendar_Invites.Group, "mName", pName);
        
        if not vPlayer
        or not vPlayer.mGroupStatus == "Invited" then
                return;
        end
        
        vPlayer.mGroupStatus = "Declined";
        
        gGroupCalendar_Invites.GroupChanged = true;
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_PlayerAlreadyGrouped(pName)
        local   vPlayer = CalendarAttendanceList_FindItem(gGroupCalendar_Invites.Group, "mName", pName);
        
        if not vPlayer then
                return;
        end
        
        if vPlayer.mGroupStatus == "Invited" then
                SendChatMessage(GroupCalendar_cAlreadyGroupedWhisper, "WHISPER", nil, vPlayer.mName);
        end
        
        vPlayer.mGroupStatus = "Grouped";
        
        gGroupCalendar_Invites.GroupChanged = true;
        CalendarGroupInvites_NotifyGroupChanged();
end

function CalendarGroupInvites_NoSuchPlayer(pName)
        local   vPlayer = CalendarAttendanceList_FindItem(gGroupCalendar_Invites.Group, "mName", pName);
        
        if not vPlayer then
                return;
        end
        
        vPlayer.mGroupStatus = "Offline";
        
        gGroupCalendar_Invites.GroupChanged = true;
        CalendarGroupInvites_NotifyGroupChanged();
end