package org.apache.slide.projector.value;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.i18n.LocalizedError;
import org.apache.slide.projector.repository.RepositoryException;
import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public class DocumentValue extends AbstractStreamable implements XMLValue {
    private static Logger logger = Logger.getLogger(DocumentValue.class.getName());

    private final static XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
    private byte[] bytes = null;
    private Document document;
    private boolean documentCreated = false;
    private InputStream inputStream = null;

    public DocumentValue(Streamable streamableResource) throws RepositoryException {
        this(streamableResource.getInputStream());
    }

    public DocumentValue(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    public DocumentValue(Document document) {
        this.document = document;
        documentCreated = true;
    }

    public InputStream getInputStream() throws RepositoryException {
        if ( inputStream != null ) return inputStream;
        if ( bytes == null ) {
            try {
            bytes = getBytes();
            } catch ( IOException exception ) {
                throw new RepositoryException(new LocalizedError("IOException"), exception);
            }
        }
        InputStream inputStream = new ByteArrayInputStream(bytes);
        return inputStream;
    }

    private byte[] getBytes() throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        xmlOutputter.output(getDocument(), outputStream);
        return outputStream.toByteArray();
    }

    public void enableMultipleStreaming() {}

    public DocType getDocumentType() {
        DocType docType = getDocument().getDocType();
        if (docType == null) {
            docType = new DocType(getDocument().getRootElement().getName());
            getDocument().setDocType(docType);
        }
        return docType;
    }

    public Document getDocument() {
        if ( !documentCreated ) {
            documentCreated = true;
            SAXBuilder saxBuilder = new SAXBuilder();
            try {
                document = saxBuilder.build(inputStream);
            } catch (JDOMException e) {
            	logger.log(Level.SEVERE, "Could not create XML document from given input stream", e);
                // FIXME: Exception handling!
                document = null;
            } catch (IOException e) {
            	logger.log(Level.SEVERE, "Could not create XML document from given input stream", e);
                document = null;
            }
        }
        return document;
    }

    public Element getRootElement() {
        return getDocument().getRootElement();
    }

    public String getCharacterEncoding() {
        return "UTF-8";
    }

    public int getContentLength() {
        if ( bytes == null ) {
            try {
                bytes = getBytes();
            } catch ( IOException e ) {
                // could not calculate content length
                return -1;
            }
        }
        return bytes.length;
    }

    public boolean isDocument() {
        return true;
    }
}