package org.rhq.plugins.irc;

import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertyList;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.jibble.pircbot.PircBot;


/**
 * @author Greg Hinkle
 */
public class IRCServerComponent implements ResourceComponent<ResourceComponent<?>>, OperationFacet {
    private final Log log = LogFactory.getLog(this.getClass());

    private Bot bot;

    private Map<String, IRCChannelComponent> channels = new HashMap<String, IRCChannelComponent>();

    private String host;
    private String port;
    private String nick;
    private List<String> activeChannels;

    private Map<String, ChannelInfo> info = new HashMap<String, ChannelInfo>();


    /**
     * Return availability of this resource
     *
     * @see org.rhq.core.pluginapi.inventory.ResourceComponent#getAvailability()
     */
    public AvailabilityType getAvailability() {

        if (!this.bot.isConnected()) {
            try {
                this.bot.connect(host);

            } catch (Exception e) {
                log.warn("Failure to connect to IRC server " + host + " reason: " + e.getMessage());
            }
        }
        activeChannels = Arrays.asList(this.bot.getChannels());

        return this.bot.isConnected() ? AvailabilityType.UP : AvailabilityType.DOWN;
    }


    /**
     * Start the resource connection
     *
     * @see org.rhq.core.pluginapi.inventory.ResourceComponent#start(org.rhq.core.pluginapi.inventory.ResourceContext)
     */
    public void start(ResourceContext context) throws InvalidPluginConfigurationException, Exception {

        Configuration conf = context.getPluginConfiguration();

        host = conf.getSimple(IRCServerDiscoveryComponent.CONFIG_HOST).getStringValue();
        port = conf.getSimple(IRCServerDiscoveryComponent.CONFIG_PORT).getStringValue();
        nick = conf.getSimple(IRCServerDiscoveryComponent.CONFIG_NICK).getStringValue();

        this.bot = new Bot(nick);
        // bot.setVerbose(true);
        bot.setAutoNickChange(true);
        bot.connect(host);

    }

    public void registerChannel(IRCChannelComponent channelComponent) {
        this.channels.put(channelComponent.getChannel(), channelComponent);

        this.bot.joinChannel(channelComponent.getChannel());
        updateChannels();
    }

    public void unregisterChannel(IRCChannelComponent channelComponent) {
        this.bot.partChannel(channelComponent.getChannel());
        this.channels.remove(channelComponent.getChannel());
    }

    public boolean isInChannel(String channel) {
        return activeChannels.contains(channel);
    }


    public void sendMessage(String channel, String message) {
        this.bot.sendMessage(channel, message);
    }

    public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException, Exception {
        if (name.equals("listChannels")) {
            OperationResult result = new OperationResult();
            Configuration resultConfig = result.getComplexResults();
            PropertyList channelList = new PropertyList("channelList");

            this.bot.listChannels();

            Thread.sleep(5000); // TODO is this long enough... any other way to know when the list is done?

            for (ChannelInfo channelInfo : info.values()) {
                PropertyMap channelMap = new PropertyMap("channelMap");
                channelMap.put(new PropertySimple("channel", channelInfo.channel));
                channelMap.put(new PropertySimple("userCount", channelInfo.userCount));
                channelMap.put(new PropertySimple("topic", channelInfo.topic));
                channelList.add(channelMap);
            }
            resultConfig.put(channelList);
            return result;
        }
        return null;
    }

    public int getUserCount(String channel) {
        return this.bot.getUsers(channel).length;
    }

    public static class ChannelInfo {
        String channel;
        int userCount;
        String topic;

        public ChannelInfo(String channel, int userCount, String topic) {
            this.channel = channel;
            this.userCount = userCount;
            this.topic = topic;
        }
    }

    public class Bot extends PircBot {

        public Bot(String nick) {
            this.setName(nick);
        }

        @Override
        protected void onChannelInfo(String channel, int userCount, String topic) {
            info.put(channel, new ChannelInfo(channel, userCount, topic));
        }

        public void onMessage(String channel, String sender, String login, String hostname, String message) {

            IRCChannelComponent component = IRCServerComponent.this.channels.get(channel);
            if (component != null) {
                component.acceptMessage(sender, login, hostname, message);
            }

            if (message.contains(getName()) && sender.contains("ghinkle")) {
                sendMessage(channel, "monitoring " + channels.size() + " channels");
            }
        }
    }


    /**
     * Tear down the rescource connection
     *
     * @see org.rhq.core.pluginapi.inventory.ResourceComponent#stop()
     */
    public void stop() {
        this.bot.disconnect();
    }


    private void updateChannels() {
        activeChannels = Arrays.asList(this.bot.getChannels());
    }

}