/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
 * Copyright (C) 2004 Jive Software. 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 org.jivesoftware.messenger.spi;

import org.jivesoftware.util.XPPReader;
import org.jivesoftware.messenger.*;
import java.util.Iterator;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;

public class IQImpl extends AbstractPacket implements IQ {

    String childNamespace = "";
    String childName = "";
    XMPPFragment childFragment;

    public IQImpl() {
    }

    public String getChildNamespace() {
        return childNamespace;
    }

    public IQ createResult(Element body) {
        IQ result = createResult();
        XMPPFragment fragment = new XMPPDOMFragment(body);
        result.setChildFragment(fragment);
        result.addFragment(fragment);
        return result;
    }

    public IQ createResult() {
        IQ result = (IQ)createDeepCopy();
        result.setType(RESULT);
        result.setChildFragment(null);
        result.clearFragments();
        result.setSender(recipient);
        result.setRecipient(sender);
        return result;
    }

    public XMPPPacket.Type typeFromString(String type) {
        XMPPPacket.Type typeResult = super.typeFromString(type);
        if (typeResult == null) {
            if ("set".equals(type)) {
                return SET;
            }
            else if ("get".equals(type)) {
                return GET;
            }
            else if ("result".equals(type)) {
                return RESULT;
            }
        }
        return typeResult;
    }

    /**
     * Quick hack to get around parsing for the short term
     *
     * @param doc The document holding the entire iq packet
     */
    public void parse(Document doc) {
        Element root = doc.getRootElement();
        setRecipient(XMPPAddress.parseJID(root.attributeValue("to")));
        setType(typeFromString(root.attributeValue("type")));
        setID(root.attributeValue("id"));

        Iterator elements = root.elements().iterator();
        while (elements.hasNext()) {
            Element element = (Element)elements.next();
            setChildNamespace(element.getNamespaceURI());
            setChildName(element.getName());
            XMPPFragment fragment = new XMPPDOMFragment(element);
            addFragment(fragment);
            setChildFragment(fragment);
        }
    }

    /**
     * <p>The parsing occurs one past the root element start tag event.</p>
     *
     * @param xpp The parser to pull from (set to one past the root iq element).
     * @throws XMLStreamException If there is trouble parsing
     */
    public void parse(XMLStreamReader xpp) throws XMLStreamException {
        // we're already one past the root iq-element
        int event = xpp.getEventType();
        Document doc = null;
        if (event == XMLStreamConstants.START_ELEMENT) {
            try {
                doc = XPPReader.parseDocument(xpp);
                setChildNamespace(doc.getRootElement().getNamespaceURI());
                setChildName(doc.getRootElement().getName());
                XMPPFragment fragment = new XMPPDOMFragment(doc.getRootElement());
                addFragment(fragment);
                setChildFragment(fragment);
            }
            catch (DocumentException e) {
                throw new XMLStreamException();
            }
        }
        while (event != XMLStreamConstants.END_ELEMENT) {
            event = xpp.next();
        }
    }

    public void setChildNamespace(String namespace) {
        if (namespace == null) {
            childNamespace = "";
        }
        else {
            childNamespace = namespace;
        }
    }

    public String getChildName() {
        return childName;
    }

    public void setChildName(String name) {
        if (name == null) {
            childName = "";
        }
        else {
            childName = name;
        }
    }

    public XMPPFragment getChildFragment() {
        return childFragment;
    }

    public void setChildFragment(XMPPFragment fragment) {
        childFragment = fragment;
        if (childFragment != null) {
            childNamespace = childFragment.getNamespace();
            childName = childFragment.getName();
        }
    }

    public void send(XMLStreamWriter xmlSerializer, int version) throws
            XMLStreamException {
        super.sendRoot(xmlSerializer, version, "iq", null);
        if (childFragment != null) {
            childFragment.send(xmlSerializer, version);
        }
        xmlSerializer.writeEndElement();
    }

    public XMPPFragment createDeepCopy() {
        IQImpl iq = new IQImpl();
        copyAttributes(iq);
        iq.childFragment = childFragment;
        iq.childName = childName;
        iq.childNamespace = childNamespace;
        return iq;
    }

    public Iterator getFragments() {
        Iterator frags;
        if (childFragment == null) {
            frags = Collections.EMPTY_LIST.iterator();
        }
        else {
            List list = new ArrayList(1);
            list.add(childFragment);
            frags = list.iterator();
        }
        return frags;
    }

    public void clearFragments() {
        childFragment = null;
    }

    public void addFragment(XMPPFragment fragment) {
        if (fragment.equals(this)) {
            throw new IllegalArgumentException("Circular parent-child relationship");
        }
        setChildFragment(fragment);
    }

    /* send
    if (getSender() == null){
        Attribute fromAttribute = document.getRootElement().attribute("from");
        if (fromAttribute != null){
            document.getRootElement().remove(fromAttribute);
        }
    } else {
        document.getRootElement().addAttribute("from",getSender().toString());
    }
    XPPWriter.write(getDocument(),writer);


    public XMPPDocumentPacket createResult(Element body) {
        XMPPDocumentPacket resultPacket = createResult();
        Element root = resultPacket.getDocument().getRootElement();
        body.detach();
        if ("query".equals(body.getName())){
            root.add(body);
        } else {
            root.addElement("query",getIQNamespace()).add(body);
        }
        return resultPacket;
    }

    public XMPPDocumentPacket createQueryResult() {
        XMPPDocumentPacket resultPacket = createResult();
        resultPacket.getDocument().getRootElement().addElement("query",getIQNamespace());
        return resultPacket;
    }

    public XMPPDocumentPacket createResult() {
        XMPPDocumentPacket resultPacket = new XMPPDocumentPacket(DocumentHelper.createDocument(),localServerName);
        tagType = TYPE_IQ;
        Element root = DocumentHelper.createElement("iq");
        resultPacket.getDocument().setRootElement(root);

        if (getRecipient() != null){
            root.addAttribute("from",getRecipient().toString());
            resultPacket.setSender(getRecipient());
        }
        if (getSender() != null){
            root.addAttribute("to",getSender().toString());
            resultPacket.setRecipient(getSender());
        }
        root.addAttribute("type","result");

        if (getID() != null){
            root.addAttribute("id",getID());
        }
        resultPacket.setSender(null);
        resultPacket.setRecipient(getSender());
        resultPacket.setOriginatingSession(getOriginatingSession());
        return resultPacket;
    }

    */
    public String toString() {
        return "IQ " + Integer.toHexString(hashCode()) + " " + getChildName() + ">" + getChildNamespace() + " t: " + (type == null ? "no type" : type.toString()) + " S: " + sender + " R: " + recipient;
    }

}
