/**
 * Copyright (C) 1999-2008 Jive Software. All rights reserved.
 *
 * Licensed 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.jivesoftware.openfire.fastpath.providers;

import org.jivesoftware.xmpp.workgroup.AgentNotFoundException;
import org.jivesoftware.xmpp.workgroup.AgentSession;
import org.jivesoftware.xmpp.workgroup.Workgroup;
import org.jivesoftware.xmpp.workgroup.WorkgroupProvider;
import org.dom4j.Element;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;

import java.util.*;

public class SiteTracker implements WorkgroupProvider {
    private Map<String, SiteUser> siteUsers = new HashMap<String, SiteUser>();

    public boolean handleGet(IQ packet) {
        Element iq = packet.getChildElement();
        String name = iq.getName();

        return "site-user-history".equals(name);
    }

    /**
     * Check to see if the packet is a tracker packet.
     *
     * @param packet the packet to check.
     * @return true if we should handle this packet.
     */
    public boolean handleSet(IQ packet) {
        Element iq = packet.getChildElement();
        String name = iq.getName();

        if ("site-user".equals(name)) {
            return true;
        }
        else if ("site-invite".equals(name)) {
            return true;
        }
        return false;
    }

    public void executeGet(IQ packet, Workgroup workgroup) {
        Element iq = packet.getChildElement();
        String name = iq.getName();

        if ("site-user-history".equals(name)) {
            handleUserHistoryRequest(packet, workgroup);
        }
    }

    private void handleUserHistoryRequest(IQ packet, Workgroup workgroup) {
        IQ reply;
        Element iq = packet.getChildElement();

        try {
            AgentSession agentSession = workgroup.getAgentManager().getAgentSession(packet.getFrom());
            if (agentSession == null) {
                reply = IQ.createResultIQ(packet);
                reply.setChildElement(packet.getChildElement().createCopy());
                reply.setError(new PacketError(PacketError.Condition.not_authorized));
                workgroup.send(reply);
                return;
            }
        }
        catch (AgentNotFoundException e) {
            reply = IQ.createResultIQ(packet);
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(new PacketError(PacketError.Condition.not_authorized));
            workgroup.send(reply);
            return;
        }

        // Define default values
        String sessionID = iq.attribute("sessionID").getText();

        reply = IQ.createResultIQ(packet);

        Element views = reply.setChildElement("site-user-history", "http://jivesoftware.com/protocol/workgroup");
        views.addAttribute("sessionID", sessionID);

        SiteUser siteUser = siteUsers.get(sessionID);
        if (siteUser != null) {
            for (PageView view : siteUser.getViews()) {
                Element pageView = views.addElement("page-view");
                pageView.addElement("title").setText(view.getTitle());
                pageView.addElement("url").setText(view.getUrl());
                pageView.addElement("time").setText(Long.toString(view.getTimeViewed()));
            }

            workgroup.send(reply);
        }

    }

    public void executeSet(IQ packet, Workgroup workgroup) {
        Element iq = packet.getChildElement();
        String name = iq.getName();


        if ("site-user".equals(name)) {
            handleSiteUser(packet, workgroup);
        }
        else if ("site-invite".equals(name)) {
            handleInvitation(packet, workgroup);
        }

    }

    private void handleSiteUser(IQ packet, Workgroup workgroup) {
        IQ reply;
        Element iq = packet.getChildElement();

        // Define default values
        IQ update = new IQ();
        Element elem = update.setChildElement("site-user", "http://jivesoftware.com/protocol/workgroup");
        String sessionID = iq.attribute("sessionID").getText();
        elem.addAttribute("sessionID", sessionID);

        // Check if a users session has expired
        Element sessionExpired = iq.element("expired");
        if (sessionExpired != null) {
            siteUsers.remove(sessionID);
            elem.addElement("sessionExpired").setText("true");
            for (AgentSession session : workgroup.getAgentSessions()) {
                update.setTo(session.getJID());
                update.setType(IQ.Type.set);
                workgroup.send(update);
            }
            reply = IQ.createResultIQ(packet);
            workgroup.send(reply);
            return;
        }

        Element chatting = iq.element("chatting");
        if (chatting != null) {
            elem.addElement("chatting").setText("true");
            for (AgentSession session : workgroup.getAgentSessions()) {
                update.setTo(session.getJID());
                update.setType(IQ.Type.set);
                workgroup.send(update);
            }
            reply = IQ.createResultIQ(packet);
            workgroup.send(reply);
            return;
        }

        // Check if user left a page
        Element leaving = iq.element("leftPage");
        if (leaving != null) {
            elem.addElement("leftPage").setText("true");
            for (AgentSession session : workgroup.getAgentSessions()) {
                update.setTo(session.getJID());
                update.setType(IQ.Type.set);
                workgroup.send(update);
            }
            reply = IQ.createResultIQ(packet);
            workgroup.send(reply);

            // Handle pageView
            SiteUser siteUser = siteUsers.get(sessionID);
            if (siteUser != null) {
                PageView lastView = siteUser.getLastView();
                if (lastView != null) {
                    lastView.setEndTime(new Date());
                }
            }
            return;
        }

        // If user has not left a page and the session has not expired. This user
        // has entered a new page.

        String url = iq.element("url").getTextTrim();
        String title = iq.element("title").getTextTrim();
        String referrer = iq.element("referrer").getTextTrim();
        String ipAddress = iq.element("ipAddress").getTextTrim();
        String userID = iq.element("userID").getTextTrim();

        // Send back reply
        reply = IQ.createResultIQ(packet);
        workgroup.send(reply);


        elem.addElement("url").setText(url);
        elem.addElement("title").setText(title);
        elem.addElement("referrer").setText(referrer);
        elem.addElement("userID").setText(userID);
        elem.addElement("ipAddress").setText(ipAddress);

        // Add to tracking information
        SiteUser siteUser = siteUsers.get(sessionID);
        if (siteUser == null) {
            siteUser = new SiteUser();
        }
        PageView pageView = new PageView();
        pageView.setStartTime(new Date());
        pageView.setTitle(title);
        pageView.setUrl(url);

        siteUser.addView(pageView);
        siteUser.setJID(reply.getTo());

        siteUsers.put(sessionID, siteUser);


        for (AgentSession session : workgroup.getAgentSessions()) {
            update.setTo(session.getJID());
            update.setType(IQ.Type.set);
            workgroup.send(update);
        }
    }

    private void handleInvitation(IQ packet, Workgroup workgroup) {
        Element iq = packet.getChildElement();

        // Define default values
        IQ update = new IQ();
        Element elem = update.setChildElement("site-invite", "http://jivesoftware.com/protocol/workgroup");
        String sessionID = iq.attribute("sessionID").getText();
        elem.addAttribute("sessionID", sessionID);

        SiteUser siteUser = siteUsers.get(sessionID);
        
        IQ reply = IQ.createResultIQ(packet);
        if (siteUser == null) {
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(new PacketError(PacketError.Condition.item_not_found));
            workgroup.send(reply);
            return;
        }
        else {
            // Send back reply
            workgroup.send(reply);
        }

        String agent = iq.element("agent").getText();
        String message = iq.element("message").getText();

        elem.addElement("agent").setText(agent);
        elem.addElement("message").setText(message);
        update.setTo(siteUser.getJID());
        update.setType(IQ.Type.set);
        workgroup.send(update);
    }


    private class SiteUser {
        private List<PageView> views = new ArrayList<PageView>();
        private JID jid = null;

        public void addView(PageView view) {
            views.add(view);
        }

        public void setJID(JID jid) {
            this.jid = jid;
        }

        public JID getJID() {
            return jid;
        }

        public Collection<PageView> getViews() {
            return views;
        }

        public PageView getLastView() {
            if (views.size() > 0) {
                return views.get(views.size() - 1);
            }
            return null;
        }
    }

    private class PageView {
        private String url;
        private String title;
        private Date startTime;
        private Date endTime;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public long getTimeViewed() {
            if (startTime != null) {
                long start = startTime.getTime();
                long end = endTime != null ? endTime.getTime() : 0;
                if (end == 0) {
                    return new Date().getTime() - start;
                }
                else {
                    return end - start;
                }
            }
            return 0;
        }


        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public Date getStartTime() {
            return startTime;
        }

        public void setStartTime(Date startTime) {
            this.startTime = startTime;
        }

        public Date getEndTime() {
            return endTime;
        }

        public void setEndTime(Date endTime) {
            this.endTime = endTime;
        }
    }

}
