"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const Immutable = require("immutable");
const Option = require("./option");
const Slugify = require("slugify");
const core_1 = require("./core");
const html_1 = require("./html");
exports.parse_url = function (template) {
    return url => {
        let res = {};
        let url_items = url.split("/");
        if (url_items.length != template.length)
            return Option.none();
        for (var i = 0; i < url_items.length; i++) {
            let x = Slugify(url_items[i]);
            let y = Slugify(template[i]);
            if (typeof y === "string") {
                if (x != y)
                    return Option.none();
            }
            else {
                let n = parseInt(x);
                if (isNaN(n))
                    return Option.none();
                res[y.name] = n;
            }
        }
        return Option.some(res);
    };
};
exports.instantiate_url = function (template) {
    return t => {
        let url = "";
        for (var i = 0; i < template.length; i++) {
            let el = Slugify(template[i]);
            if (typeof el === "string") {
                url = i == 0 ? el : `${url}/${el}`;
            }
            else {
                url = i == 0 ? `${t[el.name]}` : `${url}/${t[el.name]}`;
            }
        }
        return url;
    };
};
exports.make_url = function (template) {
    return { in: exports.parse_url(template), out: exports.instantiate_url(template) };
};
exports.fallback_url = function () {
    return { in: _ => Option.some({}), out: _ => "" };
};
class Application extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.history = Immutable.Stack();
        this.state = { kind: "loading routes" };
    }
    load() {
        this.props.routes().then(raw_routes => {
            let routes = Immutable.List(raw_routes);
            let initial_page = undefined;
            routes.forEach(r => {
                let p = r.url.in(this.props.slug).map(r.page);
                if (p.kind == "some") {
                    initial_page = p.value;
                    return false;
                }
                return true;
            });
            let newState = Object.assign({}, this.state, { kind: "running", context: this.context_from_props(this.props, initial_page), routes: routes });
            this.setState(newState);
        }).catch(() => setTimeout(() => this.load(), 250));
    }
    componentDidMount() {
        this.load();
        let self = this;
        let load = () => {
            if (self.state.kind != "running")
                return;
            if (self.history.count() == 1) {
                let slug = self.history.peek();
                window.history.pushState(`${self.props.base_url}${slug}`, `${self.props.base_url}${slug}`, `${self.props.base_url}${slug}`);
                return;
            }
            self.history = self.history.pop();
            let slug = self.history.peek();
            // console.log("back to", slug, old_history.toArray(), self.history.toArray())
            let routes = self.state.routes;
            let new_page = undefined;
            routes.forEach(r => {
                let p = r.url.in(slug).map(r.page);
                if (p.kind == "some") {
                    new_page = p.value;
                    return false;
                }
                return true;
            });
            window.history.pushState(`${self.props.base_url}${slug}`, `${self.props.base_url}${slug}`, `${self.props.base_url}${slug}`);
            let new_context = Object.assign({}, self.state.context, { current_page: new_page, logic_frame: self.state.context.logic_frame + 1 });
            let new_state = Object.assign({}, self.state, { context: new_context });
            self.setState(new_state);
        };
        window.onpopstate = function (e) {
            load();
        };
    }
    context_from_props(props, p) {
        let self = this;
        return {
            current_page: p,
            logic_frame: 0,
            force_reload: (callback) => core_1.make_C(ctxt => inner_callback => {
                if (this.state.kind == "loading routes")
                    return null;
                let old_context = this.state.context;
                let new_state = Object.assign({}, this.state, { context: Object.assign({}, old_context, { logic_frame: this.state.context.logic_frame + 1 }) });
                this.setState(new_state, () => inner_callback(callback)(null));
                return null;
            }),
            set_page: function (x, new_page, callback) {
                let out = new_page.url.out(x);
                window.history.pushState(`${self.props.base_url}${out}`, `${self.props.base_url}${out}`, `${self.props.base_url}${out}`);
                if (self.history.isEmpty() || self.history.peek() != out)
                    self.history = self.history.push(out);
                // console.log("set page", self.history.toArray())
                return core_1.make_C(ctxt => inner_callback => {
                    if (self.state.kind == "loading routes")
                        return undefined;
                    let new_context = Object.assign({}, self.state.context, { current_page: new_page.page(x) });
                    let new_state = Object.assign({}, this.state, { context: new_context });
                    self.setState(new_state, () => inner_callback(callback)(null));
                    return null;
                });
            },
            set_url: function (x, new_url, callback) {
                let out = new_url.out(x);
                // console.log("set page", self.props.base_url, out, new_url)
                window.history.pushState(`${self.props.base_url}${out}`, `${self.props.base_url}${out}`, `${self.props.base_url}${out}`);
                if (self.history.isEmpty() || self.history.peek() != out)
                    self.history = self.history.push(out);
                // console.log("set url", self.history.toArray())
                return core_1.unit(null);
            },
            push_route: (new_route, callback) => core_1.make_C(ctxt => inner_callback => {
                if (this.state.kind == "loading routes")
                    return null;
                let old_context = this.state.context;
                let new_state = Object.assign({}, this.state, { routes: this.state.routes.push(new_route) });
                this.setState(new_state, () => inner_callback(callback)(null));
                return null;
            }),
            set_routes: (routes, callback) => core_1.make_C(ctxt => inner_callback => {
                if (this.state.kind == "loading routes")
                    return null;
                let old_context = this.state.context;
                let new_state = Object.assign({}, this.state, { routes: Immutable.List(routes) });
                this.setState(new_state, () => inner_callback(callback)(null));
                return null;
            }),
        };
    }
    render() {
        if (this.state.kind == "loading routes")
            return React.createElement("div", { className: "loading" }, "Loading...");
        return React.createElement("div", { className: "monadic-application", key: `application@${this.state.context.logic_frame}` }, this.state.context.current_page.comp(() => this.state.kind != "loading routes" && this.state.context)(callback => _ => callback && callback()));
    }
}
exports.Application = Application;
exports.application = (mode, base_url, slug, routes) => {
    console.log("Calling application with", window.location.href, slug, base_url);
    return React.createElement(Application, { mode: mode, base_url: base_url, slug: slug, routes: routes });
};
exports.get_context = function (key, dbg) {
    return core_1.make_C(ctxt => cont => (core_1.unit(ctxt()).comp(ctxt)(cont)));
};
exports.link_to_route = function (label, x, r, key, className) {
    return html_1.button(label)(null).then(key, _ => exports.get_context().then(undefined, c => c.set_page(x, r), className).ignore());
};
//# sourceMappingURL=router.js.map