package ru.yandex.wmconsole.servantlet.xmlsearch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.util.URLUtil;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import java.net.IDN;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * Replaces absolute urls to relative one if isAbsolute option is switched off
 * Appends punycode-host to url element if needPunycode is switched on
 * Appends punycode-domain to domain element if needPunycode option is switched on
 * Replaces documents count if docsCount != null
 *
 * User: azakharov
 * Date: 25.09.13
 * Time: 17:58
 */
public class PunycodeRewritingHandler extends RewritingHandler {

    private static final String TAG_DOCS = "docs";
    private static final String TAG_URL = "url";
    private static final String TAG_DOMAIN = "domain";
    private static final String TAG_WRAPPER = "wrapper";

    private static final String ATTR_COUNT = "count";

    private static final String TAG_PUNYCODE_HOST = "punycode-host";
    private static final String TAG_PUNYCODE_DOMAIN = "punycode-domain";

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

    private final Options options;

    public PunycodeRewritingHandler(XMLStreamWriter writer, Options options) {
        super(writer);
        this.options = options;
    }

    @Override
    protected String replaceElementContent(String namespaceUri, String localName, String qName, String content) {
        if (TAG_URL.equals(localName) && !options.isAbsolute()) {
            try {
                URL url = SupportedProtocols.getURL(content);
                return URLUtil.getRelativeUrl(url);
            } catch (MalformedURLException e) {
                logger.error("MalformedURLException " + content, e);
                return content;
            } catch (URISyntaxException e) {
                logger.error("URISyntaxException " + content, e);
                return content;
            } catch (SupportedProtocols.UnsupportedProtocolException e) {
                logger.error("UnsupportedProtocolException " + content, e);
                return content;
            }
        } else if (TAG_DOMAIN.equals(localName)) {
            try {
                URL url = SupportedProtocols.getURL(content);
                String decodedHost = IDN.toUnicode(url.getHost());
                URL decodedUrl = new URL(url.getProtocol(), decodedHost, url.getPort(), url.getFile());
                return URLUtil.getHostName(decodedUrl, false);
            } catch (MalformedURLException e) {
                logger.error("MalformedURLException " + content, e);
                return content;
            } catch (URISyntaxException e) {
                logger.error("URISyntaxException " + content, e);
                return content;
            } catch (SupportedProtocols.UnsupportedProtocolException e) {
                logger.error("UnsupportedProtocolException " + content, e);
                return content;
            }
        }

        return content;
    }

    @Override
    protected void appendElement(String namespaceUri, String localName, String qName, String content, XMLStreamWriter writer) {
        if (TAG_URL.equals(localName) && options.isNeedPunycodeHost()) {
            try {
                URL url = SupportedProtocols.getURL(content);
                String originalHost = url.getHost();
                String punycodeHost = IDN.toASCII(originalHost);
                URL punycodeUrl = new URL(url.getProtocol(), punycodeHost, url.getPort(), url.getFile());
                String fullHostName =  URLUtil.getHostName(punycodeUrl, true);
                writer.writeStartElement(namespaceUri, TAG_PUNYCODE_HOST);
                writer.writeCharacters(fullHostName);
                writer.writeEndElement();
            } catch (MalformedURLException e) {
                logger.error("MalformedURLException " + content, e);
                return;
            } catch (URISyntaxException e) {
                logger.error("URISyntaxException " + content, e);
                return;
            } catch (SupportedProtocols.UnsupportedProtocolException e) {
                logger.error("UnsupportedProtocolException " + content, e);
                return;
            } catch (XMLStreamException e) {
                logger.error("XMLStreamException " + content, e);
                return;
            }
        } else if (TAG_DOMAIN.equals(localName) && options.isNeedPunycodeDomain()) {
            try {
                URL url = SupportedProtocols.getURL(content);
                String punycodeHost = IDN.toASCII(url.getHost());
                URL punycodeUrl = new URL(url.getProtocol(), punycodeHost, url.getPort(), url.getFile());
                String fullDomain = URLUtil.getHostName(punycodeUrl, false);
                writer.writeStartElement(namespaceUri, TAG_PUNYCODE_DOMAIN);
                writer.writeCharacters(fullDomain);
                writer.writeEndElement();
            } catch (MalformedURLException e) {
                logger.error("MalformedURLException " + content, e);
                return;
            } catch (URISyntaxException e) {
                logger.error("URISyntaxException " + content, e);
                return;
            } catch (SupportedProtocols.UnsupportedProtocolException e) {
                logger.error("UnsupportedProtocolException " + content, e);
                return;
            } catch (XMLStreamException e) {
                logger.error("XMLStreamException " + content, e);
                return;
            }
        }
    }

    @Override
    protected String replaceAttributeValue(String namespaceUri, String localName, String qName, String attribute, String value) {
        if (options.getDocsCount() != null && TAG_DOCS.equals(localName) && ATTR_COUNT.equals(attribute)) {
            return options.getDocsCount().toString();
        } else {
            return value;
        }
    }

    @Override
    protected boolean skipElement(String namespaceUri, String localName, String qName) {
        return TAG_WRAPPER.equals(localName);
    }

    public static class Options {
        private boolean isAbsolute = true;
        private boolean needPunycodeHost = false;
        private boolean needPunycodeDomain = false;
        private Long docsCount = null;

        public boolean isAbsolute() {
            return isAbsolute;
        }

        public void setAbsolute(boolean absolute) {
            isAbsolute = absolute;
        }

        public boolean isNeedPunycodeHost() {
            return needPunycodeHost;
        }

        public void setNeedPunycodeHost(boolean needPunycodeHost) {
            this.needPunycodeHost = needPunycodeHost;
        }

        public boolean isNeedPunycodeDomain() {
            return needPunycodeDomain;
        }

        public void setNeedPunycodeDomain(boolean needPunycodeDomain) {
            this.needPunycodeDomain = needPunycodeDomain;
        }

        public Long getDocsCount() {
            return docsCount;
        }

        public void setDocsCount(Long docsCount) {
            this.docsCount = docsCount;
        }
    }
}
