package ru.yandex.wmconsole.data;

import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

import wmc.stubs.Stubs;

import ru.yandex.wmconsole.util.XmlUtil;

/**
 * Результат анализа файла sitemap
 *
 * User: azakharov
 * Date: 22.02.12
 * Time: 13:27
 */
@XmlRootElement(name = "sitemap-analysis-result")
public class SitemapAnalysisResult {
    private Long urlCount;

    private List<SitemapErrorInfo> errorInfos;

    private Integer shownErrorsCount;
    private Integer totalErrorsCount;
    private Integer totalWarningsCount;

    private static final int MAX_SHOWN_ERRORS_COUNT = 100;

    public SitemapAnalysisResult() {
        // for JAXB
    }

    public SitemapAnalysisResult(Stubs.stm_result_msg resultMessage) {
        urlCount = resultMessage.hasUrlsCount() ? resultMessage.getUrlsCount() : null;
        errorInfos = new LinkedList<SitemapErrorInfo>();
        totalErrorsCount = resultMessage.getErrorsList().size();
        shownErrorsCount = totalErrorsCount;
        if (totalErrorsCount > 0) {
            totalWarningsCount = 0;
            for (Stubs.stm_error_info info : resultMessage.getErrorsList()) {
                if (info.getCode().getNumber() < 100) {
                    totalWarningsCount++;
                }
            }

            final List<Stubs.stm_error_info> shownErrors = resultMessage.getErrorsList().subList(
                    0, Math.min(resultMessage.getErrorsList().size(), MAX_SHOWN_ERRORS_COUNT));
            shownErrorsCount = shownErrors.size();
            for (Stubs.stm_error_info info : shownErrors) {
                Long lineNumber = info.hasLineNum() && info.getLineNum() > 0 ? info.getLineNum() : null;
                errorInfos.add(
                        new SitemapErrorInfo(
                                SitemapErrorCode.valueOf(info.getCode().getNumber()),
                                lineNumber,
                                info.hasMsg() ? filterBinaryMessage(info.getMsg()) : ""));
            }
        }
    }

    public String filterBinaryMessage(String data) {
        return XmlUtil.isValidXmlString(data) ? data : "";
    }

    @XmlElement(name = "url-count")
    public Long getUrlCount() {
        return urlCount;
    }

    @XmlElementWrapper(name = "errors")
    @XmlElement(name = "error")
    public List<SitemapErrorInfo> getErrorInfos() {
        return errorInfos;
    }

    public void addWrnRedirected() {
        // Если ошибок много, то заменяем одну из показываемых на
        if (shownErrorsCount == MAX_SHOWN_ERRORS_COUNT) {
            errorInfos.remove(errorInfos.size() - 1);
        } else {
            shownErrorsCount++;
        }
        errorInfos.add(new SitemapErrorInfo(SitemapErrorCode.WRN_SITEMAP_URL_REDIRECTED, null, ""));
        totalErrorsCount++;
        totalWarningsCount++;
    }

    @XmlElement(name = "show-errors")
    public Integer getShownErrorsCount() {
        return shownErrorsCount;
    }

    @XmlElement(name = "total-errors")
    public Integer getTotalErrorsCount() {
        return totalErrorsCount;
    }

    public Integer getTotalWarningsCount() {
        return totalWarningsCount;
    }

    @XmlRootElement(name = "error")
    public static class SitemapErrorInfo {
        SitemapErrorCode code;
        Long lineNumber;
        String message;


        public SitemapErrorInfo() {
            // for JAXB
        }

        public SitemapErrorInfo(SitemapErrorCode code, Long lineNumber, String message) {
            this.code = code;
            this.lineNumber = lineNumber;
            this.message = message;
        }

        @XmlElement(name = "code")
        public SitemapErrorCode getCode() {
            return code;
        }

        @XmlElement(name = "line")
        public Long getLineNumber() {
            return lineNumber;
        }

        @XmlElement(name = "message")
        public String getMessage() {
            return message;
        }
    }

    public static enum SitemapErrorCode {
        WRN_UNKNOWN_TAG(1),
        WRN_TAG_CANNOT_BE_EMPTY(2),
        WRN_TAG_CANNOT_BE_COMPLEX(3),
        WRN_TAG_DATA_TOO_LONG(4),
        WRN_TAG_EMPTY_DATA(5),
        WRN_PRIORITY_INVALID(6),
        WRN_LASTMOD_INVALID(7),
        WRN_TOO_MANY_WARNINGS(8),
        WRN_CHANGEFREQ_INVALID(9),
        WRN_SITEMAP_URL_REDIRECTED(1000),      // особый код для предупреждения о редиректе

        ERR_URL_TOO_LONG(100),
        ERR_URL_INVALID(101),

        ERR_UNKNOWN_TAG(102),
        ERR_TAG_CANNOT_BE_EMPTY(103),
        ERR_TAG_APPEARS_TWICE(104),
        ERR_TAG_CANNOT_BE_COMPLEX(105),
        ERR_TAG_DATA_TOO_LONG(106),
        ERR_TAG_EMPTY_DATA(107),
        ERR_INCOMPLETE_URL_DATA(108),

        ERR_HOST_NOT_CONFIRM(109),
        ERR_PREFIX_DONT_MATCH(110),

        ERR_INVALID_TEXT_FORMAT(200),
        ERR_INVALID_XML(201),
        ERR_INVALID_MAIN_TAG(202),
        ERR_TOO_MANY_URLS(203),
        ERR_ILLEGAL_BOM(204);

        private int code;

        private SitemapErrorCode(int code) {
            this.code = code;
        }

        @XmlAttribute(name = "id")
        public int getCode() {
            return code;
        }

        public static SitemapErrorCode valueOf(final int code) {
            for (SitemapErrorCode ei: values()) {
                 if (ei.getCode() == code) {
                     return ei;
                 }
            }
            return null;
        }

        public boolean isError() {
            return code >= 100 && code != 1000;
        }
    }
}
