package de.visone.crawl.texter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;

import de.visone.crawl.rules.HtmlQuery;

public class XmlTexterImpl extends FullTexterImpl {

    private static String ENC = "UTF-8";

    private static String ROOT = "html";

    private XMLStreamWriter out;

    private ByteArrayOutputStream baos;

    private static final Logger logger = Logger.getLogger(XmlTexterImpl.class);

    public XmlTexterImpl(final URL url, final HtmlQuery links,
            final HtmlQuery text) {
        super(url, links, text);
        baos = new ByteArrayOutputStream();

        // out =
        // XMLStreamWriterFactory.create(baos);
        try {
            out = XMLOutputFactory.newInstance().createXMLStreamWriter(baos);
            out.writeStartDocument(ENC, "1.0");
            out.writeStartElement(ROOT);
        } catch (final XMLStreamException e) {
            // should never happen
        }
    }

    @Override
    public void startTag(final String tag, final Attributes a) {
        ensureOpen();
        // super before processing
        super.startTag(tag, a);
        if (acceptString()) {
            if (ROOT.equals(tag)) {
                return;
            }
            try {
                out.writeStartElement(tag);
                final int len = a.getLength();
                for (int i = 0; i < len; ++i) {
                    out.writeAttribute(a.getLocalName(i), a.getValue(i));
                }
            } catch (final XMLStreamException e) {
                // should never happen
            }
        }
    }

    @Override
    public void endTag(final String tag) {
        ensureOpen();
        // super after processing
        if (acceptString()) {
            if (ROOT.equals(tag)) {
                return;
            }
            try {
                out.writeEndElement();
            } catch (final XMLStreamException e) {
                // should never happen
            }
        }
        super.endTag(tag);
    }

    @Override
    public void string(final String str) {
        ensureOpen();
        try {
            out.writeCData(str);
        } catch (final XMLStreamException e) {
            // should never happen
        }
    }

    private void ensureOpen() {
        if (out == null) {
            throw new IllegalStateException("stream already closed");
        }
    }

    @Override
    public String getText() {
        if (out != null) {
            try {
                out.writeEndDocument();
                out.close();
            } catch (final XMLStreamException e) {
                // ignore
            }
            out = null;
        }
        try {
            return baos.toString(ENC);
        } catch (final UnsupportedEncodingException e) {
            logger.error("encoding " + ENC + " is not supported", e);
            return null;
        }
    }

    @Override
    public void dispose() {
        super.dispose();
        if (out != null) {
            try {
                out.close();
            } catch (final XMLStreamException e) {
                // nothing to do
            }
            out = null;
        }
        if (baos != null) {
            try {
                baos.close();
            } catch (final IOException e) {
                // ignore
            }
            baos = null;
        }
    }

}
