/*
 * Copyright 2006-2010 Daniel Henninger.  All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */
package net.sf.kraken.protocols.simple;

import java.io.ByteArrayInputStream;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * This class performs conversions between presence packets of XMPP and SIMPLE formats.
 * <br>
 * By now, SIP presence comforms with <a href="http://www.ietf.org/rfc/rfc4480.txt">RFC4480</a>.
 * @author Patrick Siu
 * @author Daniel Henninger
 */
public class SimplePresence {

    static Logger Log = Logger.getLogger(SimplePresence.class);

    /**
     * Tuple status of the presence packet.
     * @author Patrick Siu
     * @version 0.0.1
     */
    public enum TupleStatus {
        OPEN  ("open"),
        CLOSED("closed");
        
        private String status;
        
        public static TupleStatus getTupleStatus(String tupleStatusString) throws IllegalArgumentException {
            for (TupleStatus t : values()) {
                if (t.toString().equalsIgnoreCase(tupleStatusString)) return t;
            }
            throw new IllegalArgumentException("There is no matching TupleStatus for this String.");
        }
        
        private TupleStatus(String status) {
            this.status = status;
        }
        
        public boolean isOpen() {
            return status.equals("open");
        }
        
        @Override
        public String toString() {
            return status;
        }
    }
    
    /**
     * Represents the rpid of the status packet.
     * <br><br>
     * Refer to <a href="http://www.ietf.org/rfc/rfc4480.txt">RFC4480</a> for details of these statuses.
     * @author Patrick Siu
     * @version 0.0.1
     */
    public enum Rpid {
        APPOINTMENT      ("appointment"),
        AWAY             ("away"),
        BREAKFAST        ("breakfast"),
        BUSY             ("busy"),
        DINNER           ("dinner"),
        HOLIDAY          ("holiday"),
        IN_TRANSIT       ("in-transit"),
        LOOKING_FOR_WORK ("looking-for-work"),
        LUNCH            ("lunch"),
        MEAL             ("meal"),
        MEETING          ("meeting"),
        ON_THE_PHONE     ("on-the-phone"),
        OTHER            ("other"),
        PERFORMANCE      ("performance"),
        PERMANENT_ABSENCE("permanent-absence"),
        PLAYING          ("playing"),
        PRESENTATION     ("presentation"),
        SHOPPING         ("shopping"),
        SLEEPING         ("sleeping"),
        SPECTATOR        ("spectator"),
        STEERING         ("steering"),
        TRAVEL           ("travel"),
        TV               ("tv"),
        UNKNOWN          ("unknown"),
        VACATION         ("vacation"),
        WORKING          ("working"),
        WORSHIP          ("worship");
        
        private String desc;
        
        public static Rpid getRpid(String rpidString) throws IllegalArgumentException {
            for (Rpid r : values()) {
                if (r.toString().equalsIgnoreCase(rpidString)) return r;
            }
            throw new IllegalArgumentException("There is no matching Rpid for the String.");
        }
        
        private Rpid(String desc) {
            this.desc = desc;
        }
        
        /**
         * Overridden to return the string description of the constant.
         */
        @Override
        public String toString() {
            return desc;
        }
    }
    
    private TupleStatus tupleStatus;
    private Rpid        rpid;
    private String      dmNote;
    private String      entity;
    
    /**
     * Constructor.
     */
    public SimplePresence() {
        this.tupleStatus = TupleStatus.OPEN;
        this.rpid        = Rpid.UNKNOWN;
        this.dmNote      = "";
        this.entity      = "";
    }
    
    public SimplePresence(TupleStatus tupleStatus) {
        this.tupleStatus = tupleStatus;
        this.rpid        = Rpid.UNKNOWN;
        this.dmNote      = "";
        this.entity      = "";
    }
    
    
    public void setRpid(Rpid rpid) {
        this.rpid = rpid;
    }
    
    public void setDmNote(String dmNote) {
        this.dmNote = dmNote;
    }
    
    public void setEntity(String entity) {
        this.entity = entity;
    }
    
    public void setTupleStatus(TupleStatus tupleStatus) {
        this.tupleStatus = tupleStatus;
    }
    
    
    public Rpid getRpid() {
        return this.rpid;
    }
    
    public String getDmNote() {
        return this.dmNote;
    }
    
    public TupleStatus getTupleStatus() {
        return this.tupleStatus;
    }
    
    private String getEightLength(int hash) {
        StringBuffer buffer = new StringBuffer(Integer.toHexString(hash));
        
        while (buffer.length() < 8) {
            buffer.insert(0, "0");
        }
        
        return new String(buffer);
    }
    
    public String toXML() {
        return
                "<?xml version='1.0' encoding='UTF-8'?>" +
                "<presence xmlns='urn:ietf:params:xml:ns:pidf'" +
                         " xmlns:dm='urn:ietf:params:xml:ns:pidf:data-model'" +
                         " xmlns:rpid='urn:ietf:params:xml:ns:pidf:rpid'" +
                         " xmlns:c='urn:ietf:params:xml:ns:pidf:cipid'" +
                         " entity='" + entity + "'>" +
                "<tuple id='t" + getEightLength(tupleStatus.hashCode()) + "'><status><basic>" + tupleStatus.toString() + "</basic></status></tuple>" +
                "<dm:person id='p" + getEightLength(this.hashCode()) + "'><rpid:activities><rpid:" + rpid.toString() + "/></rpid:activities>" +
//				"<tuple><status><basic>" + tupleStatus.toString() + "</basic></status></tuple>" +
//				"<dm:person><rpid:activities><rpid:" + rpid.toString() + "/></rpid:activities>" +
                ((dmNote != null && !dmNote.equals("")) ? "<dm:note>" + dmNote + "</dm:note>" : "") + 
                "</dm:person>" +
                "</presence>";

//		DocumentFactory docFactory = DocumentFactory.getInstance();
//
//		Document xmlDocument = docFactory.createDocument();
//		Element  rootElement = docFactory.createDocument().addElement("presence");
//
//		rootElement.addAttribute("xmlns",      "urn:ietf:params:xml:ns:pidf");
//		rootElement.addAttribute(new QName("xmlns", "dm"),   "urn:ietf:params:xml:ns:pidf:data-model");
//		rootElement.addAttribute(new QName("xmlns", "rpid"), "urn:ietf:params:xml:ns:pidf:rpid");
//		rootElement.addAttribute(new QName("xmlns", "c"),    "urn:ietf:params:xml:ns:pidf:cipid");
//		rootElement.addAttribute("entity",     entity);
//
//		Element tupleElement = rootElement.addElement("tuple");
//		tupleElement.addAttribute("id", "t" + Integer.toHexString(this.hashCode()));
//		tupleElement.addElement("status").addElement("basic").setText(this.tupleStatus.toString());
//
//		Element personElement = rootElement.addElement(new QName("dm", "person"));
//		personElement.addAttribute("id", "p" + Integer.toHexString(this.hashCode()));
//		personElement.addElement(new QName("rpid", "activities")).addElement("rpid:" + this.rpid.toString());
//
//		if (this.dmNote != null && !this.dmNote.trim().equals("")) {
//			personElement.addElement("dm:note").setText(this.dmNote);
//		}
//
//		return xmlDocument.asXML();

//		return result;
    }
    
//	public Presence convertSIPPresenceToXMPP(String sipPresence) {
//		Presence  xmppPresence = new Presence();
//		
//		SAXParser saxParser;
//		try {
//			SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
//			saxParser = saxParserFactory.newSAXParser();
//			
//		} catch (Exception e) {
//			Log.debug("Unable to load parser to parse SIP presence to XMPP Presence.", e);
//			return xmppPresence;
//		}
//		
//		return xmppPresence;
//	}
    
//	public String convertXMPPPresenceToSIP(Presence xmppPresence) {
//		String sipPresence = "";
//		String basic       = "open";
//		String rpid        = "unknown";
//		String dmNote      = "";
//		
//		if (!xmppPresence.isAvailable()) {
//			// Prepare "closed" basic presence.
//			basic = "closed";
//		} else {
//			Presence.Show xmppPresenceShow = xmppPresence.getShow();
//			if (xmppPresenceShow.equals(Presence.Show.away)) {
//				rpid = "away";
//			} else if (xmppPresenceShow.equals(Presence.Show.chat)) {
//				rpid = "away";
//			} else if (xmppPresenceShow.equals(Presence.Show.dnd)) {
//				rpid = "busy";
//			} else if (xmppPresenceShow.equals(Presence.Show.xa)) {
//				rpid = "away";
//			} else {
//				rpid = "";
//			}
//		}
//		
//		sipPresence = "<?xml version='1.0' encoding='UTF-8'?>"
//				+ "<presence xmlns='urn:ietf:params:xml:ns:pidf' xmlns:dm='urn:ietf:params:xml:ns:pidf:data-model' "
//				+ "xmlns:rpid='urn:ietf:params:xml:ns:pidf:rpid' xmlns:c='urn:ietf:params:xml:ns:pidf:cipid' "
//				+ "entity='pres:sip:sipdemo1@192.168.1.199'>"
//				+ "<tuple><status><basic>" + basic + "</basic></status></tuple>"
//				+ "<dm:person id='p3e32d940'><rpid:activities><rpid:" + rpid + "/></rpid:activities></dm:person></presence>";
//		
//		return sipPresence;
//	}
    
    public void parse(String simplePresenceXml) throws Exception {
        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
        SAXParser        saxParser        = saxParserFactory.newSAXParser();
        
        ByteArrayInputStream bais = new ByteArrayInputStream(simplePresenceXml.getBytes());
        
        saxParser.parse(bais, new SimplePresenceParser());
        bais.close();
    }
    
    
    public static SimplePresence parseSimplePresence(String simplePresenceXml) throws Exception {
        SimplePresence simplePresenceObject = new SimplePresence();
        simplePresenceObject.parse(simplePresenceXml);
        
        return simplePresenceObject;
    }
    
    class SimplePresenceParser extends DefaultHandler {
//		private boolean isStartTag   = false;
        private boolean isPresence   = false;
        private boolean isStatus     = false;
        private boolean isStatusType = false;
        private boolean isStatusName = false;
        
        String elementName = null;
        String paramName   = null;
        String userName    = null;
        String statusType  = null;
        String statusName  = null;
        
        public SimplePresenceParser() {
        }
        
        @Override
        public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException {
//			isStartTag  = true;
            elementName = (!sName.equals(""))? sName : qName;
            
            if(elementName.equals("presence")) {
                isPresence = true;
                if (attrs.getIndex("entity") >= 0) {
                    entity = attrs.getValue("entity");
                }
            }
            else if(elementName.equals("basic")) {
                isStatus = true;
            }
            else if(elementName.equals("rpid:user-input")) {
                isStatusType = true;
            }
            else if(elementName.startsWith("rpid:")) {
                try {
                    String temp = elementName.substring(elementName.indexOf("rpid:") + 5);
                    
                    if(!temp.equals("activities")) {
                        try {
                            rpid = Rpid.getRpid(temp);
                        }
                        catch (IllegalArgumentException ex) {
                            Log.debug(ex);
                            // Ignore the exception.  Leave it as "unknown".
                        }
                    }
                }
                catch (Exception ex) {
                    Log.debug(ex);
                }
            }
            else if(elementName.equals("dm:note")) {
                isStatusName = true;
            }
            
            if (isPresence) {
//				for(int i = 0; i < attrs.getLength(); i++) {
//					if(attrs.getQName(i).equals("entity")) {
//						userName = attrs.getValue(i).substring(attrs.getValue(i).indexOf("sip:"));
//					}
//				}
            }
        }
        
        @Override
        public void characters(char buf[], int offset, int len) throws SAXException {
            String data = new String(buf, offset, len);
            
            if (isStatus) {
                try {
                    tupleStatus = TupleStatus.getTupleStatus(data);
                }
                catch (IllegalArgumentException ex) {
                    // Ignore
                }
            }
            else if (isStatusType) {
//				statusType = data;
            }
            else if (isStatusName) {
                dmNote = data;
                
//				if (rpid.compareTo(Rpid.UNKNOWN) == 0) {
//					try {
//						rpid = Rpid.getRpid(data);
//					}
//					catch (IllegalArgumentException ex) {
//						// Ignore the exception.  Leave it as "unknown".
//					}
//				}
//				statusName = data;
            }
        }
        
        @Override
        public void endElement(String namespaceURI, String sName, String qName) throws SAXException {
//			isStartTag = false;
            elementName = (!sName.equals(""))? sName : qName;
            
            if(elementName.equals("presence")) {
                isPresence   = false;
            }
            else if(elementName.equals("basic")) {
                isStatus     = false;
            }
            else if(elementName.equals("rpid:user-input")) {
                isStatusType = false;
            }
            else if(elementName.equals("dm:note")) {
                isStatusName = false;
            }
        }
        
        @Override
        public void endDocument() throws SAXException {
//			obj.setUser(userName);
//			obj.setType(statusType);
//			obj.setStatus(statusName);
        }
    }
}
