import Rule from "./Rule";

export default abstract class ContextLessMapper {
    protected _context : string;
    protected _cssSelector2Rules : Map<string, Rule[]>;
    protected _xpathSelector2Rules : Map<string, Rule[]>;

    constructor(context : string) {
        this._context = context;
        this._cssSelector2Rules = new Map();
        this._xpathSelector2Rules = new Map();
    }

    add(rule : Rule): void {
        if (rule.contextCSS !== this._context && rule.contextURL !== this._context && rule.contextXPath !== this._context) {
            throw new Error('cannot add rule with a different context ');
        }
        if (rule.css) {
            if (!this._cssSelector2Rules.has(rule.css)) {
                this._cssSelector2Rules.set(rule.css, []);
            }
            this._cssSelector2Rules.get(rule.css).push(rule);
        }
        if (rule.xpath) {
            if (!this._xpathSelector2Rules.has(rule.xpath)) {
                this._xpathSelector2Rules.set(rule.xpath, []);
            }
            this._xpathSelector2Rules.get(rule.xpath).push(rule);
        }
    }

    abstract buildElementToRuleMap() : Map<Element, Rule[]>;

    protected buildElementToRuleMapForSelectors(context? : Element) : Map<Element, Rule[]> {
        const elementToRules = new Map();
        for (const [selector, rules] of this._cssSelector2Rules) {
            let querySelector : NodeListOf<Element>;
            if (context) {
                querySelector = context.querySelectorAll(selector);
            } else {
                querySelector = document.querySelectorAll(selector);
            }
            if (querySelector.length > 0) {
                querySelector.forEach(element => {
                    if (!elementToRules.has(element)) {
                        elementToRules.set(element, []);
                    }
                    elementToRules.get(element).push(...rules);
                })
            }
        }
        for (const [selector, rules] of this._xpathSelector2Rules) {
            let queryXpath;
            let queryXpathResult;

            try {
                if (context) {
                    queryXpath = document.evaluate(selector, context, null, XPathResult.ANY_TYPE, null);
                } else {
                    queryXpath = document.evaluate(selector, document, null, XPathResult.ANY_TYPE, null);
                }
            } catch (e) {
                console.log('XPath Error:',e);
            }

            queryXpathResult = queryXpath.iterateNext();

            while (queryXpathResult) {
                if (!elementToRules.has(queryXpathResult)) {
                    elementToRules.set(queryXpathResult, []);
                }
                elementToRules.get(queryXpathResult).push(...rules);
                queryXpathResult = queryXpath.iterateNext();
            }
        }
        return elementToRules;
    }
}