/**
 * 
 */
package org.cometd.oort;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.ServletContext;

import org.cometd.Bayeux;
import org.cometd.Channel;
import org.cometd.Client;
import org.cometd.RemoveListener;
import org.mortbay.cometd.BayeuxService;
import org.mortbay.log.Log;

public class OortChatService extends BayeuxService
{
    /**
     * A map(channel, map(userName, clientId))
     */
    private final ConcurrentMap<String, Set<String>> _members = new ConcurrentHashMap<String, Set<String>>();
    private Oort _oort;
    private Seti _seti;
    
    public OortChatService(ServletContext context)
    {
        super((Bayeux)context.getAttribute(Bayeux.ATTRIBUTE), "chat");

        _oort = (Oort)context.getAttribute(Oort.OORT_ATTRIBUTE);
        if (_oort==null)
            throw new RuntimeException("!"+Oort.OORT_ATTRIBUTE);
        _seti = (Seti)context.getAttribute(Seti.SETI_ATTRIBUTE);
        if (_seti==null)
            throw new RuntimeException("!"+Seti.SETI_ATTRIBUTE);

        _oort.observeChannel("/chat/**");
        subscribe("/chat/**", "trackMembers");
        subscribe("/service/privatechat", "privateChat");
    }
    
    public void trackMembers(final Client joiner, final String channelName, Object data, final String messageId)
    {
        if (data instanceof Object[])
        {
            Set<String> members = _members.get(channelName);
            if (members == null)
            {
                Set<String> newMembers = new HashSet<String>();
                members = _members.putIfAbsent(channelName, newMembers);
                if (members == null) members = newMembers;
            }
            boolean added=false;
            for (Object user : (Object[])data)
                added|=members.add(user.toString());
            if (added)
            {
                Log.info("Members: " + members);
                // Broadcast the members to all existing members
                getBayeux().getChannel(channelName, false).publish(getClient(), members, messageId);
            }
        }
        else if (data instanceof Map)
        {
            Map<String, Object> map = (Map<String, Object>) data;

            if (Boolean.TRUE.equals(map.get("join")))
            {
                
                Set<String> members = _members.get(channelName);
                if (members == null)
                {
                    Set<String> newMembers = new HashSet<String>();
                    members = _members.putIfAbsent(channelName, newMembers);
                    if (members == null) members = newMembers;
                }
                
                final String userName = (String)map.get("user");
                
                members.add(userName);
                
                if (!_oort.isOort(joiner))
                    _seti.associate(userName,joiner);

                joiner.addListener(new RemoveListener(){

                    public void removed(String clientId, boolean timeout)
                    {
                        if (!_oort.isOort(joiner))
                            _seti.disassociate(userName);
                        if (timeout)
                        {
                            Channel channel=getBayeux().getChannel(channelName,false);
                            if (channel!=null)
                            {
                                Map<String,Object> leave = new HashMap<String,Object>();
                                leave.put("leave",Boolean.TRUE);
                                leave.put("user",userName);
                                channel.publish(null,leave,null);
                            }
                        }
                    }
                    
                });

                Log.info("Members: " + members);
                // Broadcast the members to all existing members
                getBayeux().getChannel(channelName, false).publish(getClient(), members, messageId);
                
            }
            
            if (Boolean.TRUE.equals(map.get("leave")))
            {
                Set<String> members = _members.get(channelName);
                if (members == null)
                {
                    Set<String> newMembers = new HashSet<String>();
                    members = _members.putIfAbsent(channelName, newMembers);
                    if (members == null) members = newMembers;
                }

                String userName = (String)map.get("user");
                members.remove(userName);

                Log.info("Members: " + members);
                // Broadcast the members to all existing members
                getBayeux().getChannel(channelName, true).publish(getClient(), members, messageId);
            }
        }
    }

    public void privateChat(Client source, String channel, Map<String, Object> data, String messageId)
    {
        System.err.println("\nPRIVATE: "+data);
        String toUid=(String)data.get("peer");
        String toChannel=(String)data.get("room");
        
        _seti.sendMessage(toUid,toChannel,data);
    }
}

