/*
Copyright (C) 2005 David Green <green@couchpotato.net>
All Rights Reserved.

This file is part of Aelfengard.

Aelfengard is proprietary software. You may not redistribute it without
prior written permission from the copyright holder.
*/

package server.command;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;

import server.Group;
import server.Player;
import server.SyntaxException;
import server.Utils;
import server.token.TokenString;

// TODO: Use token strings throughout
public class GroupCommand extends Command {

    private static final TokenString TS_GROUP_KICK = new TokenString("%N1 %v1/has/have/ kicked %Mc21/%x2/%n2/ out of the group.");

    private static final LinkedHashSet<Invite> invites = new LinkedHashSet<Invite>();
    
    public GroupCommand() {
        super(CommandCategory.INTERACTION, "Grouping system.");
    }
    
    @Override
    public void run(Player player) throws SyntaxException {
        String subcmd = CommandProcessor.nextToken();
        if (subcmd == null) {
            throw new SyntaxException();
        }
        else if (subcmd.equals("invite")) {
            String token = CommandProcessor.nextToken();
            if (token == null || CommandProcessor.nextToken() != null) {
                throw new SyntaxException();
            }
            Group group = player.getGroup();
            if (group.getLeader() != player) {
                player.sendText(true, "Only the group leader can invite people.");
                return;
            }
            Player other = player.matchLocalPlayer(token);
            if (other == null) {
                player.sendText(true, "There's noone here by that name.");
                return;
            }
            if (other == player) {
                player.sendText(true, "You can't invite yourself.");
                return;
            }
            Invite invite = new Invite(player, other);
            invites.remove(invite);
            invites.add(invite);
            player.sendText(true, "You have invited " + other.getName(player, false) + " to join your group.");
            other.sendText(true, Utils.capitalize(player.getName(other, false)) + 
                    " has invited you to join " + 
                    player.getPossessiveAdjective(other) + 
                    " group. Use @10FGROUP JOIN " + 
                    player.getId() + 
                    "@ZZZ to accept " + 
                    player.getPossessiveAdjective(other) + 
                    " offer.");
        }
        else if (subcmd.equals("kick")) {
            String token = CommandProcessor.nextToken();
            if (token == null || CommandProcessor.nextToken() != null) {
                throw new SyntaxException();
            }
            Group group = player.getGroup();
            if (group.getLeader() != player) {
                player.sendText(true, "Only the group leader can kick people out of the group.");
                return;
            }
            Player other = player.getPlayerByNickname(token);
            if (other == null) {
                player.sendText(true, "Unknown player: " + token);
                return;
            }
            if (other == player) {
                player.sendText(true, "You can't kick yourself out of the group. " +
                        "Use @10FGROUP LEAVE@ZZZ if you want to leave the " +
                        "group.");
                return;
            }
            if (group != other.getGroup()) {
                player.sendText(true, "That player is not in your group.");
                return;
            }
            group.sendText(null, null, null, null, TS_GROUP_KICK, null, player, other);
            other.setGroup(new Group());
        }
        else if (subcmd.equals("join")) {
            String token = CommandProcessor.nextToken();
            if (token == null || CommandProcessor.nextToken() != null) {
                throw new SyntaxException();
            }
            Player other = player.getPlayerByNickname(token);
            if (other == null) {
                player.sendText(true, "Unknown player: " + token);
                return;
            }
            if (other == player) {
                player.sendText(true, "You can't group with yourself.");
                return;
            }
            purgeOldInvites();
            boolean found = invites.remove(new Invite(other, player));
            if (!found) {
                player.sendText(
                        true, Utils.capitalize(other.getName(player, false)) + 
                        " hasn't invited you to " + 
                        other.getPossessiveAdjective(player) + 
                        " group.");
                return;
            }
            Group group = other.getGroup();
            if (group.players().size() == 1) {
                // newly formed group
                other.sendText(true, "You have formed the group.");
            }
            else {
                for (Player target : group.players()) {
                    target.sendText(true, Utils.capitalize(player.getName(target, false)) +
                            " has joined the group.");
                }
            }
            player.setGroup(group);
            player.sendText(true, "You have joined the group.");
        }
        else if (subcmd.equals("leave")) {
            if (CommandProcessor.nextToken() != null) {
                throw new SyntaxException();
            }
            Group group = player.getGroup();
            if (group.players().size() == 1) {
                player.sendText(true, "You're not in a group.");
                return;
            }
            player.setGroup(new Group());
            player.sendText(true, "You have left the group.");
            for (Player target : group.players()) { 
                target.sendText(
                        true, Utils.capitalize(player.getName(target, false)) +
                        " has left the group.");
            }
        }
        else if (subcmd.equals("disband")) {
            if (CommandProcessor.nextToken() != null) {
                throw new SyntaxException();
            }
            Group group = player.getGroup();
            if (group.players().size() == 1) {
                player.sendText(true, "You're not in a group.");
                return;
            }
            if (group.getLeader() != player) {
                player.sendText(true, "Only the group leader can disband the group.");
                return;
            }
            Collection<Player> players = new HashSet<Player>(group.players());
            for (Player groupMember : players) {
                if (groupMember == player) {
                    groupMember.sendText(true, "You have disbanded the group.");
                }
                else {
                    groupMember.sendText(
                            true, Utils.capitalize(player.getName(groupMember, false)) +
                            " has disbanded the group.");
                }
                groupMember.setGroup(new Group());
            }
        }
        else {
            throw new SyntaxException();
        }
    }

    @Override
    public String[] getSyntax(Player player) {
        return new String[] {
                "INVITE <player>",
                "KICK <player>",
                "JOIN <player>",
                "LEAVE",
                "DISBAND",
        };
    }
    
    private static void purgeOldInvites() {
        Iterator<Invite> iter = invites.iterator();
        while (iter.hasNext()) {
            Invite invite = iter.next();
            if (invite.endTime < System.currentTimeMillis()) {
                iter.remove();
            }
        }
    }
    
    private static class Invite {
        public final long endTime = 
            System.currentTimeMillis() + 60000; // 1 minute invite timeout
        public final Player invitor;
        public final Player invitee;
        public Invite(Player invitor, Player invitee) {
            this.invitor = invitor;
            this.invitee = invitee;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj instanceof Invite) {
                Invite other = (Invite) obj;
                return other.invitor == invitor && 
                       other.invitee == invitee;
            }
            return false;
        }

        @Override
        public int hashCode() {
            return invitor.getId() * 12349899 + invitee.getId();
        }
    }
    
}