/**
 * This file is part of Aion-Lightning <aion-lightning.org>.
 *
 *  Aion-Lightning 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Aion-Lightning 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 Aion-Lightning.
 *  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Credits goes to all Open Source Core Developer Groups listed below
 * Please do not change here something, ragarding the developer credits, except the "developed by XXXX".
 * Even if you edit a lot of files in this source, you still have no rights to call it as "your Core".
 * Everybody knows that this Emulator Core was developed by Aion Lightning 
 * @-Aion-Unique-
 * @-Aion-Lightning
 * @Aion-Engine
 * @Aion-Extreme
 * @Aion-NextGen
 * @Aion-Core Dev.
 */
package com.aionemu.gameserver.model.team2.group;
import com.aionemu.commons.callbacks.metadata.GlobalCallback;
import com.aionemu.gameserver.configs.main.GroupConfig;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.team2.TeamType;
import com.aionemu.gameserver.model.team2.common.events.PlayerLeavedEvent.LeaveReson;
import com.aionemu.gameserver.model.team2.common.events.ShowBrandEvent;
import com.aionemu.gameserver.model.team2.common.events.TeamKinahDistributionEvent;
import com.aionemu.gameserver.model.team2.common.legacy.GroupEvent;
import com.aionemu.gameserver.model.team2.common.legacy.LootGroupRules;
import com.aionemu.gameserver.model.team2.group.callback.AddPlayerToGroupCallback;
import com.aionemu.gameserver.model.team2.group.callback.PlayerGroupCreateCallback;
import com.aionemu.gameserver.model.team2.group.callback.PlayerGroupDisbandCallback;
import com.aionemu.gameserver.model.team2.group.events.*;
import com.aionemu.gameserver.network.aion.serverpackets.SM_QUESTION_WINDOW;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.restrictions.RestrictionsManager;
import com.aionemu.gameserver.services.AutoGroupService;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.ThreadPoolManager;
import com.aionemu.gameserver.utils.TimeUtil;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import javolution.util.FastMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
 * @author ATracer
 */
public class PlayerGroupService {
    private static final Logger log = LoggerFactory.getLogger(PlayerGroupService.class);
    private static final Map<Integer, PlayerGroup> groups = new ConcurrentHashMap<Integer, PlayerGroup>();
    private static final AtomicBoolean offlineCheckStarted = new AtomicBoolean();
    private static FastMap<Integer, PlayerGroup> groupMembers;
    public static final void inviteToGroup(final Player inviter, final Player invited) {
        if (canInvite(inviter, invited)) {
            PlayerGroupInvite invite = new PlayerGroupInvite(inviter, invited);
            if (invited.getResponseRequester().putRequest(SM_QUESTION_WINDOW.STR_PARTY_DO_YOU_ACCEPT_INVITATION, invite)) {
                PacketSendUtility.sendPacket(invited, new SM_QUESTION_WINDOW(SM_QUESTION_WINDOW.STR_PARTY_DO_YOU_ACCEPT_INVITATION, 0, 0,
                        inviter.getName()));
            }
        }
    }
    public static final boolean canInvite(Player inviter, Player invited) {
        if (inviter.isInInstance()) {
            if (AutoGroupService.getInstance().isAutoInstance(inviter.getInstanceId())) {
                PacketSendUtility.sendPacket(inviter, SM_SYSTEM_MESSAGE.STR_MSG_INSTANCE_CANT_INVITE_PARTY_COMMAND);
                return false;
            }
        }
        if (invited.isInInstance()) {
            if (AutoGroupService.getInstance().isAutoInstance(invited.getInstanceId())) {
                PacketSendUtility.sendPacket(inviter, SM_SYSTEM_MESSAGE.STR_MSG_INSTANCE_CANT_INVITE_PARTY_COMMAND);
                return false;
            }
        }
        return RestrictionsManager.canInviteToGroup(inviter, invited);
    }
    @GlobalCallback(PlayerGroupCreateCallback.class)
    public static final PlayerGroup createGroup(Player leader, Player invited, TeamType type) {
        PlayerGroup newGroup = new PlayerGroup(new PlayerGroupMember(leader), type);
        groups.put(newGroup.getTeamId(), newGroup);
        addPlayer(newGroup, leader);
        addPlayer(newGroup, invited);
        if (offlineCheckStarted.compareAndSet(false, true)) {
            initializeOfflineCheck();
        }
        return newGroup;
    }
    private static void initializeOfflineCheck() {
        ThreadPoolManager.getInstance().scheduleAtFixedRate(new OfflinePlayerChecker(), 1000, 30 * 1000);
    }
    @GlobalCallback(AddPlayerToGroupCallback.class)
    public static final void addPlayerToGroup(PlayerGroup group, Player invited) {
        group.addMember(new PlayerGroupMember(invited));
    }
    /**
     * Change group's loot rules and notify team members
     */
    public static final void changeGroupRules(PlayerGroup group, LootGroupRules lootRules) {
        group.onEvent(new ChangeGroupLootRulesEvent(group, lootRules));
    }
    /**
     * Player entered world - search for non expired group
     */
    public static final void onPlayerLogin(Player player) {
        for (PlayerGroup group : groups.values()) {
            PlayerGroupMember member = group.getMember(player.getObjectId());
            if (member != null) {
                group.onEvent(new PlayerConnectedEvent(group, player));
            }
        }
    }
    /**
     * Player leaved world - set last online on member
     */
    public static final void onPlayerLogout(Player player) {
        PlayerGroup group = player.getPlayerGroup2();
        if (group != null) {
            PlayerGroupMember member = group.getMember(player.getObjectId());
            member.updateLastOnlineTime();
            group.onEvent(new PlayerDisconnectedEvent(group, player));
        }
    }
    /**
     * Update group members to some event of player
     */
    public static final void updateGroup(Player player, GroupEvent groupEvent) {
        PlayerGroup group = player.getPlayerGroup2();
        if (group != null) {
            group.onEvent(new PlayerGroupUpdateEvent(group, player, groupEvent));
        }
    }
    /**
     * Add player to group
     */
    public static final void addPlayer(PlayerGroup group, Player player) {
        Preconditions.checkNotNull(group, "Group should not be null");
        group.onEvent(new PlayerEnteredEvent(group, player));
    }
    /**
     * Remove player from group (normal leave, or kick offline player)
     */
    public static final void removePlayer(Player player) {
        PlayerGroup group = player.getPlayerGroup2();
        if (group != null) {
            group.onEvent(new PlayerGroupLeavedEvent(group, player));
        }
    }
    /**
     * Remove player from group (ban)
     */
    public static final void banPlayer(Player bannedPlayer, Player banGiver) {
        Preconditions.checkNotNull(bannedPlayer, "Banned player should not be null");
        Preconditions.checkNotNull(banGiver, "Bangiver player should not be null");
        PlayerGroup group = banGiver.getPlayerGroup2();
        if (group != null) {
            if (group.hasMember(bannedPlayer.getObjectId())) {
                group.onEvent(new PlayerGroupLeavedEvent(group, bannedPlayer, LeaveReson.BAN, banGiver.getName()));
            } else {
                log.warn("TEAM2: banning player not in group {}", group.onlineMembers());
            }
        }
    }
    /**
     * Disband group by removing all players one by one
     */
    @GlobalCallback(PlayerGroupDisbandCallback.class)
    public static void disband(PlayerGroup group) {
        Preconditions.checkState(group.onlineMembers() <= 1, "Can't disband group with more than one online member");
        groups.remove(group.getTeamId());
        group.onEvent(new GroupDisbandEvent(group));
    }
    /**
     * Share specific amount of kinah between group members
     */
    public static void distributeKinah(Player player, long kinah) {
        PlayerGroup group = player.getPlayerGroup2();
        if (group != null) {
            group.onEvent(new TeamKinahDistributionEvent<PlayerGroup>(group, player, kinah));
        }
    }
    /**
     * Show specific mark on top of player
     */
    public static void showBrand(Player player, int targetObjId, int brandId) {
        PlayerGroup group = player.getPlayerGroup2();
        if (group != null) {
            group.onEvent(new ShowBrandEvent<PlayerGroup>(group, targetObjId, brandId));
        }
    }
    public static void changeLeader(Player player) {


Answer:         PlayerGroup group = player.getPlayerGroup2();