/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.vysper.xmpp.modules.extension.xep0050_adhoc_commands;

import org.apache.commons.lang.StringUtils;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.modules.DefaultDiscoAwareModule;
import org.apache.vysper.xmpp.modules.ServerRuntimeContextService;
import org.apache.vysper.xmpp.modules.servicediscovery.management.Feature;
import org.apache.vysper.xmpp.modules.servicediscovery.management.Identity;
import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoElement;
import org.apache.vysper.xmpp.modules.servicediscovery.management.InfoRequest;
import org.apache.vysper.xmpp.modules.servicediscovery.management.Item;
import org.apache.vysper.xmpp.modules.servicediscovery.management.ItemRequestListener;
import org.apache.vysper.xmpp.modules.servicediscovery.management.ServerInfoRequestListener;
import org.apache.vysper.xmpp.modules.servicediscovery.management.ServiceDiscoveryRequestException;
import org.apache.vysper.xmpp.protocol.HandlerDictionary;
import org.apache.vysper.xmpp.protocol.NamespaceHandlerDictionary;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
import org.apache.vysper.xmpp.server.ServerRuntimeContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * A module for <a href="http://xmpp.org/extensions/xep-0050.html">XEP-0050 Ad-hoc Commands</a>.
 *
 * @author The Apache MINA Project (dev@mina.apache.org)
 */
public class AdhocCommandsModule extends DefaultDiscoAwareModule 
        implements ItemRequestListener, ServerInfoRequestListener, 
                   ServerRuntimeContextService, AdhocCommandsService {

    public static final String ADHOC_COMMANDS = "adhoc_commands";
    
    private final Logger logger = LoggerFactory.getLogger(org.apache.vysper.xmpp.modules.extension.xep0050_adhoc_commands.AdhocCommandsModule.class);

    protected ServerRuntimeContext serverRuntimeContext;

    protected AdhocCommandIQHandler iqHandler;
    
    protected final List<AdhocCommandSupport> adhocCommandSupporters = new ArrayList<AdhocCommandSupport>();

    @Override
    public void initialize(ServerRuntimeContext serverRuntimeContext) {
        super.initialize(serverRuntimeContext);

        this.serverRuntimeContext = serverRuntimeContext;

        serverRuntimeContext.registerServerRuntimeContextService(this);
    }

    @Override
    public String getName() {
        return "XEP-0050 Ad-hoc Commands";
    }

    @Override
    public String getVersion() {
        return "1.2";
    }

    /**
     * Make this object available for disco#items requests.
     */
    @Override
    protected void addItemRequestListeners(List<ItemRequestListener> itemRequestListeners) {
        itemRequestListeners.add(this);
    }

    @Override
    protected void addServerInfoRequestListeners(List<ServerInfoRequestListener> serverInfoRequestListeners) {
        serverInfoRequestListeners.add(this);
    }

    /**
     * Implements the getItemsFor method from the {@link ItemRequestListener} interface.
     * Makes this modules available via disco#items and returns the associated nodes.
     * 
     * @see ItemRequestListener#getItemsFor(InfoRequest)
     */
    public List<Item> getItemsFor(InfoRequest request) throws ServiceDiscoveryRequestException {
        if (!NamespaceURIs.XEP0050_ADHOC_COMMANDS.equals(request.getNode())) {
            return null;
        }

        List<CommandInfo> allCommandInfos = new ArrayList<CommandInfo>();
        for (AdhocCommandSupport adhocCommandSupporter : adhocCommandSupporters) {
            final Collection<CommandInfo> commandInfos = adhocCommandSupporter.getCommandInfosForInfoRequest(request, true);
            if (commandInfos != null) allCommandInfos.addAll(commandInfos);
        }
        if (allCommandInfos.size() == 0) return null; // do not announce when no command available

        // formulate info response from collected command infos
        List<Item> items = new ArrayList<Item>();
        for (CommandInfo commandInfo : allCommandInfos) {
            Entity jid = commandInfo.getJid();
            if (jid == null) jid = serverRuntimeContext.getServerEnitity();

            String node = commandInfo.getNode();
            if (node == null) {
                logger.warn("no node for command info, ignoring. command name = " + commandInfo.getName());
                continue;
            }

            String name = commandInfo.getName() == null ? commandInfo.getNode() : commandInfo.getName();
            items.add(new Item(jid, name, node));
        }

        return items;
    }

    public List<InfoElement> getServerInfosFor(InfoRequest request) throws ServiceDiscoveryRequestException {
        if (adhocCommandSupporters.size() == 0) return null; // do not announce when no command available

        if (StringUtils.isEmpty(request.getNode())) {
            return Arrays.asList((InfoElement)new Feature(NamespaceURIs.XEP0050_ADHOC_COMMANDS));
        }
        
        // info for specific node has been asked
        List<CommandInfo> allCommandInfos = new ArrayList<CommandInfo>();
        for (AdhocCommandSupport adhocCommandSupporter : adhocCommandSupporters) {
            final Collection<CommandInfo> commandInfos = adhocCommandSupporter.getCommandInfosForInfoRequest(request, false);
            if (commandInfos != null) allCommandInfos.addAll(commandInfos);
        }
        if (allCommandInfos.size() == 0) return null; // do not announce when no command available

        final CommandInfo commandInfo = allCommandInfos.get(0);
        final ArrayList<InfoElement> infoElements = new ArrayList<InfoElement>();
        infoElements.add(new Identity("automation", "command-node", commandInfo.getName()));
        infoElements.add(new Feature(NamespaceURIs.XEP0050_ADHOC_COMMANDS));
        infoElements.add(new Feature(NamespaceURIs.JABBER_X_DATA));
        return infoElements;
        
    }

    public String getServiceName() {
        return ADHOC_COMMANDS;
    }

    public void registerCommandSupport(AdhocCommandSupport adhocCommandSupport) {
        adhocCommandSupporters.add(adhocCommandSupport);
    }

    @Override
    protected void addHandlerDictionaries(List<HandlerDictionary> dictionary) {
        iqHandler = new AdhocCommandIQHandler(Collections.unmodifiableCollection(adhocCommandSupporters));
        dictionary.add(new NamespaceHandlerDictionary(NamespaceURIs.XEP0050_ADHOC_COMMANDS, iqHandler));
    }

}
