package org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.get;

import java.util.concurrent.BlockingQueue;

import org.buddycloud.channelserver.Configuration;
import org.buddycloud.channelserver.channel.ChannelManager;
import org.buddycloud.channelserver.db.exception.NodeStoreException;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubElementProcessorAbstract;
import org.buddycloud.channelserver.pubsub.model.NodeMembership;
import org.buddycloud.channelserver.pubsub.subscription.Subscriptions;
import org.buddycloud.channelserver.utils.XMLConstants;
import org.buddycloud.channelserver.utils.node.item.payload.Buddycloud;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.resultsetmanagement.ResultSet;

public class SubscriptionsGet extends PubSubElementProcessorAbstract {

    private IQ result;
    private String node;
    private JID actorJid;
    private IQ requestIq;
    private boolean ephemeral = false;

    public SubscriptionsGet(BlockingQueue<Packet> outQueue, ChannelManager channelManager) {
        setOutQueue(outQueue);
        setChannelManager(channelManager);
    }


    @Override
    public void process(Element elm, JID actorJID, IQ reqIQ, Element rsm) throws Exception {
        result = IQ.createResultIQ(reqIQ);
        actorJid = actorJID;
        requestIq = reqIQ;

        Element pubsub = result.setChildElement(XMLConstants.PUBSUB_ELEM, JabberPubsub.NAMESPACE_URI);
        Element subscriptions = pubsub.addElement(XMLConstants.SUBSCRIPTIONS_ELEM);

        node = reqIQ.getChildElement().element(XMLConstants.SUBSCRIPTIONS_ELEM).attributeValue(XMLConstants.NODE_ATTR);

        if (null == actorJid) {
            actorJid = reqIQ.getFrom();
        }

        boolean isProcessedLocally = true;

        if (node == null) {
            isProcessedLocally = getUserMemberships(subscriptions);
        } else {
            if (!Configuration.getInstance().isLocalNode(node)) {
                result.getElement().addAttribute(XMLConstants.REMOTE_SERVER_DISCOVER_ATTR, Boolean.FALSE.toString());

            }
            isProcessedLocally = getNodeMemberships(subscriptions);
        }
        if (!isProcessedLocally) {
            return;
        }

        outQueue.put(result);
    }

    /**
     * Don't include any subscriptions other than Subscriptions.subscriptions, unless the user is
     * one of owner, moderator or the user.
     * 
     * @param subscriptions
     * @return
     * @throws NodeStoreException
     * @throws InterruptedException
     */
    private boolean getNodeMemberships(Element subscriptions) throws NodeStoreException, InterruptedException {

        ResultSet<NodeMembership> cur = channelManager.getNodeMemberships(node);

        if (Configuration.getInstance().isLocalNode(node)) {
            subscriptions.addAttribute(XMLConstants.NODE_ATTR, node);

            for (NodeMembership ns : cur) {
                if (!isUserPriviledged(ns.getUser()) && !ns.getSubscription().equals(Subscriptions.subscribed)) {
                    continue;
                }

                Element subscription = subscriptions.addElement(XMLConstants.SUBSCRIPTION_ELEM);
                subscription.addAttribute(XMLConstants.NODE_ATTR, ns.getNodeId())
                        .addAttribute(XMLConstants.SUBSCRIPTION_ELEM, ns.getSubscription().toString())
                        .addAttribute(XMLConstants.JID_ATTR, ns.getUser().toBareJID());

                // Only add the invited by attribute if the user has the necessary priviledges
                if (null != ns.getInvitedBy() && isUserPriviledged(ns.getUser())) {
                    subscription.addAttribute(XMLConstants.INVITED_BY_ATTR, ns.getInvitedBy().toBareJID());
                }

            }
        } else {
            // TODO(garethf) this looks like makeRemoteRequest
            if (!channelManager.isCachedNode(node) || (null != requestIq.getElement().element(XMLConstants.PUBSUB_ELEM).element(XMLConstants.SET_ELEM))
                    && !cur.isEmpty()) {
                makeRemoteRequest(new JID(node.split("/")[2]).getDomain());

            }
            return false;
        }

        return true;
    }

    /**
     * Don't include any subscriptions other than Subscriptions.subscriptions, unless the user is
     * one of owner, moderator or the user.
     * 
     * @param ns
     * 
     * @return
     * @throws NodeStoreException
     */
    private boolean isUserPriviledged(JID jid) throws NodeStoreException {
        boolean isUser = actorJid.toBareJID().equals(jid.toBareJID());

        return (isUser || isOwnerModerator()) ? true : false;
    }

    private boolean getUserMemberships(Element subscriptions) throws NodeStoreException, InterruptedException {
        // let's get all subscriptions.
        ResultSet<NodeMembership> cur;
        
        String ephemeralValue =
            requestIq.getChildElement().element(XMLConstants.SUBSCRIPTIONS_ELEM)
                .attributeValue(new QName(XMLConstants.EPHEMERAL, Namespace.get(Buddycloud.NS)));
        if ((null != ephemeralValue) && ephemeralValue.equals("true")) {
          ephemeral = true;
        }

        cur = channelManager.getUserMemberships(actorJid, ephemeral);

        if ((null != requestIq.getElement().element(XMLConstants.PUBSUB_ELEM).element("set")) && (!cur.isEmpty())
                && (!Configuration.getInstance().isLocalJID(actorJid))) {

            makeRemoteRequest(actorJid.getDomain());
            return false;
        }
        for (NodeMembership ns : cur) {
            Element subscription = subscriptions.addElement(XMLConstants.SUBSCRIPTION_ELEM);
            subscription.addAttribute(XMLConstants.NODE_ATTR, ns.getNodeId())
                    .addAttribute(XMLConstants.SUBSCRIPTION_ELEM, ns.getSubscription().toString())
                    .addAttribute(XMLConstants.JID_ATTR, ns.getUser().toBareJID());
            if (null != ns.getInvitedBy() && isOwnerModerator()) {
                subscription.addAttribute(XMLConstants.INVITED_BY_ATTR, ns.getInvitedBy().toBareJID());

            }
        }
        return true;
    }

    private boolean isOwnerModerator() throws NodeStoreException {
        return channelManager.getNodeMembership(node, actorJid).getAffiliation().canAuthorize();
    }

    private void makeRemoteRequest(String to) throws InterruptedException {
        IQ forwarder = requestIq.createCopy();
        forwarder.setTo(to);
        if (null == forwarder.getElement().element(XMLConstants.PUBSUB_ELEM).element(XMLConstants.ACTOR_ELEM)) {
            Element actor = forwarder.getElement().element(XMLConstants.PUBSUB_ELEM).addElement(XMLConstants.ACTOR_ELEM, Buddycloud.NS);

            actor.addText(requestIq.getFrom().toBareJID());
        }
        outQueue.put(forwarder);
    }

    @Override
    public boolean accept(Element elm) {
        return XMLConstants.SUBSCRIPTIONS_ELEM.equals(elm.getName());
    }
}

@@ -6185,30 +6185,8 @@
 By()
- && isOwnerModerator()
 ) %7B%0A

Include invitedBy for users' own subscriptions