package com.limegroup.gnutella.xml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import com.limegroup.gnutella.Response;


public final class LimeXMLDocumentHelper{

    private static final Log LOG = LogFactory.getLog(LimeXMLDocumentHelper.class);
    
    public static final String XML_HEADER = "<?xml version=\"1.0\"?>";
    public static final String XML_NAMESPACE =
        "xsi:noNamespaceSchemaLocation=\"";

	/**
	 * Private constructor to ensure that this class can never be
	 * instantiated.
	 */
	private LimeXMLDocumentHelper() {}

    /**
     * TO be used when a Query Reply comes with a chunk of meta-data
     * we want to get LimeXMLDocuments out of it
     */
    public static List getDocuments(String aggregatedXML, int totalResponseCount) {
        if(aggregatedXML==null || aggregatedXML.equals("") || totalResponseCount <= 0)
            return Collections.EMPTY_LIST;
        
        List results = new ArrayList();
        
        for(Iterator i = XMLParsingUtils.split(aggregatedXML).iterator(); i.hasNext(); ) {
            String xmlDocument = (String)i.next();
            XMLParsingUtils.ParseResult parsingResult;
            try {
                parsingResult = XMLParsingUtils.parse(xmlDocument,totalResponseCount);
            } catch (SAXException sax) {
                LOG.warn("SAX while parsing: " + xmlDocument, sax);
                continue;// bad xml, ignore
            } catch (IOException bad) {
                LOG.warn("IOX while parsing: " + aggregatedXML, bad);
                return Collections.EMPTY_LIST; // abort
            }
            
            final String indexKey = parsingResult.canonicalKeyPrefix +
                                    LimeXMLDocument.XML_INDEX_ATTRIBUTE;
            LimeXMLDocument[] documents = new LimeXMLDocument[totalResponseCount];
            for(Iterator j = parsingResult.iterator(); j.hasNext(); ) {
                Map attributes = (Map)j.next();
                String sindex = (String)attributes.remove(indexKey);
                if (sindex == null)
                    return Collections.EMPTY_LIST;
                
                int index = -1;
                try {
                    index = Integer.parseInt(sindex);
                } catch(NumberFormatException bad) { //invalid document
                    LOG.warn("NFE while parsing", bad);
                    return Collections.EMPTY_LIST;
                }
                
                if (index >= documents.length || index < 0)
                    return Collections.EMPTY_LIST; // malicious document, can't trust it.
                
                if(!attributes.isEmpty()) {
                    try {
                        documents[index] = new LimeXMLDocument(attributes,
                                parsingResult.schemaURI,
                                parsingResult.canonicalKeyPrefix);
                    } catch(IOException ignored) {
                        LOG.debug("",ignored);
                    }
                }
            }
            results.add(documents);
        }
        return results;
    }
    
    /**
     * Builds an XML string out of all the responses.
     * If no responses have XML, an empty string is returned.
     */
    public static String getAggregateString(Response[] responses) {
        HashMap /* LimeXMLSchema -> StringBuffer */ allXML = new HashMap();
        for(int i = 0; i < responses.length; i++) {
            LimeXMLDocument doc = responses[i].getDocument();
            if(doc != null) {
                LimeXMLSchema schema = doc.getSchema();
                StringBuffer built = (StringBuffer)allXML.get(schema);
                if(built == null) {
                    built = new StringBuffer();
                    allXML.put(schema, built);
                }
                built.append(doc.getAttributeStringWithIndex(i));
            }
        }
     
        // Iterate through each schema and build a string containing
        // a bunch of XML docs, each beginning with XML_HEADER.   
        StringBuffer fullXML = new StringBuffer();
        for(Iterator i = allXML.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry entry = (Map.Entry)i.next();
            LimeXMLSchema schema = (LimeXMLSchema)entry.getKey();
            StringBuffer buffer = (StringBuffer)entry.getValue();
            buildXML(fullXML, schema, buffer.toString());
        }
        return fullXML.toString();
    }
    
    /**
     * Wraps the inner element around the root tags, with the correct
     * XML headers.
     */
    public static void buildXML(StringBuffer buffer, LimeXMLSchema schema, String inner) {
        buffer.append(XML_HEADER);
        buffer.append("<");
        buffer.append(schema.getRootXMLName());
        buffer.append(" ");
        buffer.append(XML_NAMESPACE);
        buffer.append(schema.getSchemaURI());
        buffer.append("\">");
        buffer.append(inner);
        buffer.append("</");
        buffer.append(schema.getRootXMLName());
        buffer.append(">");
    }
}
