/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package net.java.sip.communicator.impl.protocol.irc;

import java.util.*;

import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;

/**
 * Implementation of support for Persistent Presence for IRC.
 *
 * @author Danny van Heumen
 */
public class OperationSetPersistentPresenceIrcImpl
    extends
    AbstractOperationSetPersistentPresence<ProtocolProviderServiceIrcImpl>
{
    /**
     * Logger.
     */
    private static final Logger LOGGER = Logger
        .getLogger(OperationSetPersistentPresenceIrcImpl.class);

    /**
     * Root contact group for IRC contacts.
     */
    private final ContactGroupIrcImpl rootGroup = new ContactGroupIrcImpl(
        this.parentProvider);

    /**
     * IRC implementation for OperationSetPersistentPresence.
     *
     * @param parentProvider IRC instance of protocol provider service.
     */
    protected OperationSetPersistentPresenceIrcImpl(
        final ProtocolProviderServiceIrcImpl parentProvider)
    {
        super(parentProvider);
    }

    /**
     * Create a volatile contact.
     *
     * @param id contact id
     * @return returns instance of volatile contact
     */
    private ContactIrcImpl createVolatileContact(final String id)
    {
        // Get non-persistent group for volatile contacts.
        ContactGroupIrcImpl volatileGroup = getNonPersistentGroup();

        // Create volatile contact
        ContactIrcImpl newVolatileContact =
            new ContactIrcImpl(this.parentProvider, id, volatileGroup);
        volatileGroup.addContact(newVolatileContact);

        this.fireSubscriptionEvent(newVolatileContact, volatileGroup,
            SubscriptionEvent.SUBSCRIPTION_CREATED);

        return newVolatileContact;
    }

    /**
     * Get group for non-persistent contacts.
     *
     * @return returns group instance
     */
    private ContactGroupIrcImpl getNonPersistentGroup()
    {
        String groupName
            = IrcActivator.getResources().getI18NString(
                "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME");

        for (int i = 0; i < getRootGroup().countSubgroups(); i++)
        {
            ContactGroupIrcImpl gr =
                (ContactGroupIrcImpl) getRootGroup().getGroup(i);

            if (!gr.isPersistent() && gr.getGroupName().equals(groupName))
            {
                return gr;
            }
        }

        ContactGroupIrcImpl volatileGroup =
            new ContactGroupIrcImpl(this.parentProvider, this.rootGroup,
                groupName);

        this.rootGroup.addSubGroup(volatileGroup);

        this.fireServerStoredGroupEvent(volatileGroup,
            ServerStoredGroupEvent.GROUP_CREATED_EVENT);

        return volatileGroup;
    }

    /**
     * Get root contact group.
     *
     * @return returns root contact group
     */
    public ContactGroup getRootGroup()
    {
        return rootGroup;
    }

    /**
     * IRC currently does not implement subscribing.
     *
     * @param contactIdentifier contact
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void subscribe(final String contactIdentifier)
        throws IllegalArgumentException,
        IllegalStateException,
        OperationFailedException
    {
        LOGGER.trace("subscribe(\"" + contactIdentifier + "\") called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * IRC currently does not implement subscribing.
     *
     * @param parent contact group
     * @param contactIdentifier contact
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void subscribe(final ContactGroup parent,
        final String contactIdentifier)
        throws IllegalArgumentException,
        IllegalStateException,
        OperationFailedException
    {
        LOGGER.trace("subscribe(\"" + parent.getGroupName() + "\", \""
            + contactIdentifier + "\") called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * IRC currently does not implement unsubscribing.
     *
     * @param contact contact to unsubscribe
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void unsubscribe(final Contact contact)
        throws IllegalArgumentException,
        IllegalStateException,
        OperationFailedException
    {
        LOGGER.trace("unsubscribe(\"" + contact.getAddress() + "\") called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Creating a contact group is currently not implemented.
     *
     * @param parent parent contact group
     * @param groupName new group's name
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void createServerStoredContactGroup(final ContactGroup parent,
        final String groupName) throws OperationFailedException
    {
        LOGGER.trace("createServerStoredContactGroup(...) called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Removing a contact group is currently not implemented.
     *
     * @param group contact group to remove
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void removeServerStoredContactGroup(final ContactGroup group)
        throws OperationFailedException
    {
        LOGGER.trace("removeServerStoredContactGroup called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Rename contact group.
     *
     * @param group contact group to rename
     * @param newName new name
     */
    @Override
    public void renameServerStoredContactGroup(final ContactGroup group,
        final String newName)
    {
        LOGGER.trace("renameServerStoredContactGroup called");
        ((ContactGroupIrcImpl) group).setGroupName(newName);
    }

    /**
     * Moving contacts to a different group is currently not implemented.
     *
     * @param contactToMove contact to move
     * @param newParent new parent group
     * @throws OperationFailedException if not implemented
     */
    @Override
    public void moveContactToGroup(final Contact contactToMove,
        final ContactGroup newParent) throws OperationFailedException
    {
        LOGGER.trace("moveContactToGroup called");
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Get group of contacts that have been discovered while using IRC.
     *
     * @return returns root contact group
     */
    @Override
    public ContactGroup getServerStoredContactListRoot()
    {
        // TODO consider using this for contacts that are registered at NickServ
        // for the IRC network. Store contacts and possibly some whois info if
        // useful for these contacts as persistent data.
        return this.rootGroup;
    }

    @Override
    public Contact createUnresolvedContact(final String address,
        final String persistentData, final ContactGroup parentGroup)
    {
        LOGGER.warn("Unresolved contact: " + address + " " + persistentData
            + " group: " + parentGroup.getGroupName());
        // FIXME implement createUnresolvedContact
        return null;
    }

    @Override
    public Contact createUnresolvedContact(final String address,
        final String persistentData)
    {
        LOGGER.warn("Unresolved contact: " + address + " " + persistentData);
        // FIXME implement createUnresolvedContact
        return null;
    }

    @Override
    public ContactGroup createUnresolvedContactGroup(final String groupUID,
        final String persistentData, final ContactGroup parentGroup)
    {
        LOGGER.warn("Unresolved contactgroup: " + groupUID + " "
            + persistentData + " parent: " + parentGroup.getGroupName());
        // TODO implement createUnresolvedContactGroup
        return null;
    }

    /**
     * Get current IRC presence status.
     *
     * The presence status currently is ONLINE if we are connected or OFFLINE if
     * we aren't connected.
     *
     * @return returns status ONLINE if connected or OFFLINE if not connected
     */
    @Override
    public PresenceStatus getPresenceStatus()
    {
        // TODO implement AWAY presence if available in irc-api
        if (this.parentProvider.getIrcStack().isConnected())
        {
            return IrcStatusEnum.ONLINE;
        }
        else
        {
            return IrcStatusEnum.OFFLINE;
        }
    }

    /**
     * "Publishing" presence status in IRC is currently not implemented.
     */
    @Override
    public void publishPresenceStatus(final PresenceStatus status,
        final String statusMessage)
        throws IllegalArgumentException,
        IllegalStateException,
        OperationFailedException
    {
        // TODO implement publishPresenceStatus (we might be able to do
        // something with modes invisible and away (IIRC) in IRC.
        throw new OperationFailedException("Not implemented.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Get set of statuses supported in IRC.
     *
     * @return returns iterator for supported statuses
     */
    @Override
    public Iterator<PresenceStatus> getSupportedStatusSet()
    {
        final HashSet<PresenceStatus> statuses = new HashSet<PresenceStatus>();
        final Iterator<IrcStatusEnum> supported =
            IrcStatusEnum.supportedStatusSet();
        while (supported.hasNext())
        {
            statuses.add(supported.next());
        }
        return statuses.iterator();
    }

    /**
     * IRC currently does not implement querying presence status.
     *
     * @param contactIdentifier contact id
     * @return returns current presence status
     * @throws OperationFailedException for not supporting this feature
     */
    @Override
    public PresenceStatus queryContactStatus(final String contactIdentifier)
        throws IllegalArgumentException,
        IllegalStateException,
        OperationFailedException
    {
        // TODO implement querying presence status of contact
        throw new OperationFailedException("Not supported.",
            OperationFailedException.NOT_SUPPORTED_OPERATION);
    }

    /**
     * Find a contact by its ID.
     *
     * @param contactID ID to look up
     * @return contact instance if found or null if nothing found
     */
    @Override
    public Contact findContactByID(final String contactID)
    {
        LOGGER.trace("Finding contact for nick name '" + contactID + "'");
        if (contactID == null)
        {
            return null;
        }
        Contact contact = this.rootGroup.getContact(contactID);
        if (contact != null)
        {
            return contact;
        }
        Iterator<ContactGroup> groups = this.rootGroup.subgroups();
        while (groups.hasNext())
        {
            ContactGroup group = groups.next();
            contact = group.getContact(contactID);
            if (contact != null)
            {
                return contact;
            }
        }
        LOGGER.trace("No contact found for nick name '" + contactID + "'");
        return null;
    }

    /**
     * IRC does not support authorization handling, so this is not supported.
     *
     * @param handler authorization handler
     */
    @Override
    public void setAuthorizationHandler(final AuthorizationHandler handler)
    {
    }

    /**
     * IRC does not have a status message, so it will always return an empty
     * string.
     *
     * @return returns empty string
     */
    @Override
    public String getCurrentStatusMessage()
    {
        return "";
    }

    /**
     * Find or create contact by ID.
     *
     * In IRC every chat room member is also a contact. Try to find a contact by
     * its ID. If a contact cannot be found, then create one.
     *
     * @param id id of the contact
     * @return returns instance of contact
     */
    Contact findOrCreateContactByID(final String id)
    {
        Contact contact = findContactByID(id);
        if (contact == null)
        {
            contact = createVolatileContact(id);
            LOGGER.debug("No existing contact found. Created volatile contact"
                + " for nick name '" + id + "'.");
        }
        return contact;
    }
}