
// import fetchJsonp from 'fetch-jsonp';
import { json } from 'd3-fetch';
import { values, map } from 'd3-collection';

var NS = {
    main: 0,
    discussion: 1,
    template: 10,
    category: 14,
    news: 3106,
    web: 3116
}

function strip_fragment (href) {
    return href.replace(/#.?$/, '');
}

export class Wiki {
    constructor (apiurl) {
        this.apiurl = apiurl;
        this.pages_by_title = {};
        this.ns_names = {};
        for (var key in NS) {
            var nsid = NS[key];
            this.ns_names[nsid] = key;
        }
        this.siteinfo = null;
    }

    get_nodes () {
        var ret = values(this.pages_by_title);
        ret = ret.filter(p => (!p.redirect && p.ns === 0));
        return ret;
    }

    get_links () {
        return values(this.links);
    }

    async init () {
        await this.get_site_info();
    }

    async get_site_info () {
        // https://en.wikipedia.org/w/api.php
        var url = this.apiurl + "?action=query&meta=siteinfo&siprop=general&format=json&formatversion=2";
        var data = await json(url);
        // this.siteinfo = data.query.general;

        this.server = data.query.general.server; // e.g. "http://activearchives.org"
        this.articlepath = data.query.general.articlepath; // e.g. "/wiki/$1"
        this.base = data.query.general.base; // e.g. "http://activearchives.org/wiki/Main_Page"
        // this.sitename = data.query.general.sitename;
        this.mainpage = data.query.general.mainpage;
        this.wikibasepat = new RegExp(this.server+this.articlepath.replace(/\$1$/, "(.+)"));

        url = this.apiurl + "?action=query&meta=siteinfo&siprop=namespaces&format=json&formatversion=2";
        data = await json(url);
        this.namespaces_by_id = data.query.namespaces;
        this.namespaces_by_name = {};
        values(this.namespaces_by_id).forEach(n => {
            this.namespaces_by_name[n.name] = n;
        });
        // create special special entry
        var special = { name: "Special", id: -17 };
        this.namespaces_by_name["Special"] = special;
        this.namespaces_by_id[-17] = special;

    }

    escapeTitle (title) {
        return encodeURI(title.replace(/ /g, "_"));
    }

    unescapeTitle (title) {
        return decodeURI(title.replace(/_/g, " "));
    }

    wiki_title_to_url (title) {
        return this.server + this.articlepath.replace(/\$1$/, this.escapeTitle(title));
    }

    url_to_wiki_title (href) {
        var m = this.wikibasepat.exec(strip_fragment(href));
        if (m !== null) {
            return this.unescapeTitle(m[1]);
        }
    }

    /* sample siteinfo, see: http://activearchives.org/mw/api.php?action=query&meta=siteinfo&formatversion=2&format=json */

    get_page (url) {
        var title = this.url_to_wiki_title(url);
        if (title) {
            return this.get_page_by_title(title);
        }
    }

    get_page_by_title (title) {
        var p = this.pages_by_title[title];
        if (p) {
            return p;
        } else {
            var cpos = title.indexOf(":"),
                name = title,
                namespace = "";
            if (cpos >= 0) {
                namespace = title.substring(0, cpos);
                name = title.substring(cpos+1);
            }
            p = new Page(this, {title: title, name: name, ns: this.namespaces_by_name[namespace].id});
            this.pages_by_title[title] = p;
            return p; 
        }
    }
    page_for_object (n, merge_data) {
        if (merge_data === undefined) { merge_data = true; }
        var title = n.title,
            p = this.pages_by_title[title];
        if (p) {
            if (merge_data) { p.merge_data(n); }
            return p;
        } else {
            p = new Page(this, n);
            this.pages_by_title[title] = p;
            return p; 
        }
    }
    get_ns_classname (nsid) {
        // console.log("get_ns_classname", nsid, this.ns_names[nsid]);
        var ret = this.ns_names[nsid];
        console.log("classname", ret);
        if (ret === undefined) { console.log("warning classname undefined for ns", nsid); }
        return ret;
    }
    union (p1, p2) {
        var union = map(p1, d=>d.title);
        for (var i=0, l=p2.length; i<l; i++) {
            var x = p2[i];
            union.set(x.title, x);
        }
        return union.values();
    }
}

export class Page {
    constructor (wiki, node) {
        this.wiki = wiki;
        this.merge_data(node);
    }
    url () {
        // return this.wiki.apiurl.replace("api.php", "index.php")+"/"+encodeURIComponent(this.title);
        // return this.wiki.apiurl.replace("api.php", "index.php")+"/"+encodeURIComponent(this.title);
        return this.wiki.wiki_title_to_url(this.title);
    }
    merge_data (node) {
        for (var key in node) {
            if (node.hasOwnProperty(key)) {
                this[key] = node[key];
            }
        }        
    }

    // api.php?action=query&prop=categories&titles=Albert%20Einstein
    async get_prop (pname, prefix) {
        var ret = [];
        var url = this.wiki.apiurl+"?action=query&format=json&formatversion=2&prop="+pname+"&titles="+encodeURIComponent(this.title);
        while (true) {
            var data = await json(url);
            // var json = await data.json();
            if (data.query.pages[0]) {
                var p = data.query.pages[0];
                // extract any missing page info
                if (p.ns && !this.ns) { this.ns = p.ns; }
                if (p.pageid && !this.pageid) { this.pageid = p.pageid; }
            }
            if (data.query.pages[0][pname]) {
                ret.push.apply(ret, data.query.pages[0][pname]);
            }
            if (!data.continue) { break; }
            url = this.wiki.apiurl+"?action=query&format=json&formatversion=2&prop="+pname+"&"+prefix+"continue="+data.continue[prefix+"continue"]+"&titles="+encodeURIComponent(this.title);
        }
        ret = ret.map(x => this.wiki.page_for_object(x));
        // console.log("get_prop", pname, prefix, ret);
        return ret;
    }

    async get_list (lname, prefix, appendstr) {
        var ret = [];
        var baseurl = this.wiki.apiurl+"?action=query&list="+lname+"&format=json&formatversion=2&"+prefix+"title="+encodeURIComponent(this.title)+(appendstr || "");
        var url = baseurl;
        while (true) {
            var data = await json(url);
            if (data.query[lname]) {
                ret.push.apply(ret, data.query[lname]);
            }
            if (!data.continue) { break; }
            url = baseurl+"&"+prefix+"continue="+data.continue[prefix+"continue"];
        }
        ret = ret.map(x => this.wiki.page_for_object(x));
        // console.log("get_prop", pname, prefix, ret);
        return ret;
    }

    async get_links () {
        return await this.get_prop("links", "pl");
    }

    async get_linkshere () {
        return await this.get_prop("linkshere", "lh");
    }

    async get_categorymembers () {
        return await this.get_list("categorymembers", "cm", "&cmtype=page");
    }
}
