"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const Immutable = require("immutable");
const i18next = require("i18next");
const core_1 = require("./core");
const html_1 = require("./html");
const primitives_1 = require("./primitives");
class Repeat extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.stopped = false;
        this.state = { current_value: props.value, frame_index: 1 };
    }
    componentWillUnmount() {
        this.stopped = true;
    }
    componentWillMount() {
        this.stopped = false;
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info(), this.state.current_value);
        return this.props.p(this.state.current_value).comp(this.props.context)(callback => new_value => {
            if (this.stopped)
                return;
            return this.setState(Object.assign({}, this.state, { frame_index: this.state.frame_index + 1, current_value: new_value }), () => this.props.cont(callback)(new_value));
        });
    }
}
exports.repeat = function (key, dbg) {
    return p => initial_value => core_1.make_C(ctxt => cont => React.createElement(Repeat, ({ kind: "repeat", debug_info: dbg, p: p, value: initial_value, context: ctxt, cont: cont, key: key })));
};
class Any extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { ps: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.setState(Object.assign({}, this.state, { ps: new_props.ps.map(p => p(new_props.value).comp(new_props.context)(callback => new_value => new_props.cont(callback)(new_value))) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { ps: this.props.ps.map(p => p(this.props.value).comp(this.props.context)(callback => new_value => this.props.cont(callback)(new_value))) }));
    }
    render() {
        return React.createElement("div", { className: this.props.className },
            " ",
            this.state.ps != "creating" ? this.state.ps : null,
            " ");
    }
}
exports.any = function (key, className, dbg) {
    return ps => initial_value => core_1.make_C(ctxt => cont => React.createElement(Any, { kind: "any", debug_info: dbg, ps: ps, value: initial_value, context: ctxt, cont: cont, key: key, className: className }));
};
class Never extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { p: "loading" };
    }
    componentWillReceiveProps(new_props) {
        this.setState(Object.assign({}, this.state, { p: new_props.p.comp(new_props.context)(callback => new_value => { }) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { p: this.props.p.comp(this.props.context)(callback => new_value => { }) }));
    }
    render() {
        return this.state.p != "loading" ? this.state.p : [];
    }
}
exports.never = function (p, key) {
    return core_1.make_C(ctxt => cont => React.createElement(Never, { kind: "never", p: p, context: ctxt, cont: cont, key: key, debug_info: undefined }));
};
class All extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { results: Immutable.Map(), ps: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.setState(Object.assign({}, this.state, { ps: new_props.ps.map((p, p_i) => p.comp(new_props.context)(callback => result => this.setState(Object.assign({}, this.state, { results: this.state.results.set(p_i, result) }), () => {
                if (this.state.results.keySeq().toSet().equals(Immutable.Range(0, new_props.ps.length).toSet())) {
                    let results = this.state.results.sortBy((r, r_i) => r_i).toArray();
                    this.setState(Object.assign({}, this.state, { results: Immutable.Map() }), () => new_props.cont(callback)(results));
                }
            }))) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { ps: this.props.ps.map((p, p_i) => p.comp(this.props.context)(callback => result => this.setState(Object.assign({}, this.state, { results: this.state.results.set(p_i, result) }), () => {
                if (this.state.results.keySeq().toSet().equals(Immutable.Range(0, this.props.ps.length).toSet())) {
                    let results = this.state.results.sortBy((r, r_i) => r_i).toArray();
                    this.setState(Object.assign({}, this.state, { results: Immutable.Map() }), () => this.props.cont(callback)(results));
                }
            }))) }));
    }
    render() {
        return React.createElement("div", null,
            " ",
            this.state.ps != "creating" ? this.state.ps : null,
            " ");
    }
}
exports.all = function (ps, key, dbg) {
    return core_1.make_C(ctxt => cont => React.createElement(All, { kind: "all", debug_info: dbg, ps: ps, context: ctxt, cont: cont, key: key }));
};
class Retract extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { p: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.setState(Object.assign({}, this.state, { p: new_props.p(new_props.inb(new_props.value)).comp(new_props.context)(callback => new_value => new_props.cont(callback)(new_props.out(new_props.value)(new_value))) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { p: this.props.p(this.props.inb(this.props.value)).comp(this.props.context)(callback => new_value => this.props.cont(callback)(this.props.out(this.props.value)(new_value))) }));
    }
    render() {
        return this.state.p != "creating" ? this.state.p : [];
    }
}
exports.retract = function (key, dbg) {
    return (inb, out, p) => (initial_value) => core_1.make_C(ctxt => (cont) => React.createElement(Retract, { kind: "retract", debug_info: dbg, inb: inb, out: out, p: p, value: initial_value, context: ctxt, cont: cont, key: key }));
};
class LiftPromise extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.wait_time = 500;
        this.stopped = false;
        this.state = { result: "busy", input: props.value, retry_count: 0 };
    }
    componentWillReceiveProps(new_props) {
        // if (this.state.result != "busy" && this.state.result != "error") {
        //   this.props.debug_info && console.log("New props (ignored):", this.props.debug_info(), this.state.input, new_props.value)
        //   return
        // }
        this.props.debug_info && console.log("New props:", this.props.debug_info(), this.state.input, new_props.value);
        this.setState(Object.assign({}, this.state, { input: new_props.value }), () => this.load(new_props));
    }
    load(props) {
        if (this.stopped)
            return;
        this.setState(Object.assign({}, this.state, { result: "busy" }), () => props.p(this.state.input).then(x => {
            this.wait_time = 500;
            if (this.props.debug_info)
                console.log("Promise done:", this.props.debug_info());
            if (this.stopped)
                return;
            this.setState(Object.assign({}, this.state, { result: x }), () => props.cont(() => null)(x));
        })
            .catch(() => {
            if (props.retry_strategy == "never") {
                if (this.stopped)
                    return;
                this.setState(Object.assign({}, this.state, { result: "error" }));
            }
            else if (props.retry_strategy == "semi exponential") {
                this.wait_time = Math.floor(Math.max(this.wait_time * 1.5, 2500));
                setTimeout(() => this.load(props), this.wait_time);
            }
            else if (props.retry_strategy.kind == "retry then show failure") {
                if (this.stopped)
                    return;
                if (this.state.retry_count < props.retry_strategy.times) {
                    this.setState(Object.assign({}, this.state, { retry_count: this.state.retry_count + 1 }));
                    setTimeout(() => this.load(props), this.wait_time);
                }
                else {
                    let failedJSX = props.retry_strategy.on_failure.comp(props.context)(props.cont);
                    this.setState(Object.assign({}, this.state, { retry_count: 0, result: { kind: "failing", failure_renderer: failedJSX } }));
                }
            }
            else if (props.retry_strategy.kind == "never") {
                if (this.stopped)
                    return;
                let failedJSX = props.retry_strategy.on_failure.comp(props.context)(props.cont);
                this.setState(Object.assign({}, this.state, { result: { kind: "failing", failure_renderer: failedJSX } }));
            }
        }));
    }
    componentWillUnmount() {
        this.stopped = true;
    }
    componentWillMount() {
        this.stopped = false;
        this.props.debug_info && console.log("Mount:", this.props.debug_info());
        this.load(this.props);
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        return this.state.result == "busy" ? React.createElement("div", { className: "busy" }, i18next.t("busy"))
            : this.state.result == "error" ? React.createElement("div", { className: "error" }, i18next.t("error"))
                : this.state.result != undefined && this.state.result.hasOwnProperty('kind') && this.state.result.kind === "failing" ? this.state.result.failure_renderer
                    : [];
    }
}
exports.lift_promise = function (p, retry_strategy, key, dbg) {
    return x => core_1.make_C(ctxt => cont => React.createElement(LiftPromise, { kind: "lift promise", debug_info: dbg, value: x, retry_strategy: retry_strategy, p: p, context: ctxt, cont: cont, key: key }));
};
class Delay extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.running = false;
        this.state = { status: "dirty", value: props.value, last_command: props.p(props.value).comp(props.context)(props.cont) };
    }
    componentWillMount() {
        // console.log("starting delay thread")
        if (this.running)
            return;
        this.running = true;
        var self = this;
        let process = () => setTimeout(() => {
            // console.log("delay is ticking", self.state.status, self.state.value)
            if (self.state.status == "dirty") {
                // console.log("delay is submitting the data to save")
                if (!this.running)
                    return;
                self.setState(Object.assign({}, self.state, { status: "waiting", last_command: self.props.p(self.state.value).comp(this.props.context)(callback => new_value => {
                        // console.log("calling the continuation of dirty", self.state.value)
                        self.props.cont(callback)(new_value);
                    }) }));
                process();
            }
            else {
                if (self.running)
                    process();
            }
        }, self.props.dt);
        process();
    }
    componentWillUnmount() {
        // console.log("stopping delay thread")
        this.running = false;
    }
    componentWillReceiveProps(new_props) {
        // console.log("Delay received new props and is going back to dirty")
        this.setState(Object.assign({}, this.state, { value: new_props.value, status: "dirty" }));
    }
    render() {
        return this.state.last_command;
    }
}
exports.delay = function (dt, key, dbg) {
    return p => initial_value => core_1.make_C(ctxt => cont => React.createElement(Delay, { kind: "delay", debug_info: dbg, dt: dt, p: p, value: initial_value, context: ctxt, cont: cont, key: key }));
};
exports.mk_submenu_entry = function (label, children) { return { kind: "sub menu", label: label, children: children }; };
exports.mk_menu_entry = function (v) { return { kind: "item", value: v }; };
exports.simple_menu = function (type, to_string, key, dbg) {
    let content_menu_class, content_class, menu_class, entries_class, entry_class, sub_entry_class;
    if (type == "side menu") {
        content_menu_class = "monadic-content-with-menu";
        content_class = "monadic-content";
        menu_class = "monadic-content-menu";
        entries_class = "monadic-content-menu__entries";
        entry_class = "monadic-content-menu__entry";
        sub_entry_class = "monadic-content-menu__sub-entry";
    }
    else {
        content_menu_class = "monadic-content-with-tabs";
        content_class = "monadic-content";
        menu_class = "monadic-tabs";
        entries_class = "monadic-tabs__entries";
        entry_class = "monadic-tabs__entry";
        sub_entry_class = "monadic-tabs__sub-entry";
    }
    return (items_array, p, selected_item, selected_sub_menu) => {
        let items = Immutable.List(items_array);
        let entries = (s) => (type != "side menu" && s.shown_range.first > 0 ?
            [s => html_1.div(`${entry_class} monadic-prev-tab`)(html_1.a("<"))(Object.assign({}, s, { shown_range: Object.assign({}, s.shown_range, { first: s.shown_range.first - 1 }) }))]
            :
                []).concat(items.map((item, i) => {
            return (s) => item.kind == "item" ?
                html_1.div(`${entry_class} ${s.selected.kind == "item" && item.value == s.selected.value ? ` ${entry_class}--active` : ""}`, to_string(item.value))(html_1.a(to_string(item.value), undefined, undefined, false, undefined))(Object.assign({}, s, { sub_selected: { kind: "nothing" }, selected: item, last_action: { kind: "selection" } }))
                :
                    exports.any(item.label)([
                        (s) => html_1.div(`${entry_class} `, item.label)(html_1.a(item.label, undefined, undefined, false, undefined))(Object.assign({}, s, { sub_selected: item, last_action: { kind: "selection" } }))
                    ].concat((s.sub_selected.kind == "sub menu" && item.label == s.sub_selected.label) ||
                        (s.selected.kind == "item" && item.children.some(c => s.selected.kind == "item" && c.value == s.selected.value)) ?
                        item.children.map(c => (s) => html_1.div(`${sub_entry_class} ${s.selected.kind == "item" && c.value == s.selected.value ? ` ${sub_entry_class}--active` : ""}`, to_string(c.value))(html_1.a(to_string(c.value), undefined, undefined, false, undefined))(Object.assign({}, s, { sub_selected: item, selected: c, last_action: { kind: "selection" } })))
                        :
                            []))(s);
        }).filter((i, i_i) => type == "side menu" || i_i >= s.shown_range.first && (i_i - s.shown_range.first) < s.shown_range.amount)
            .concat(type != "side menu" && s.shown_range.first + s.shown_range.amount < items.count() ?
            [s => html_1.div(`${entry_class} monadic-next-tab`)(html_1.a(">"))(Object.assign({}, s, { shown_range: Object.assign({}, s.shown_range, { first: s.shown_range.first + 1 }) }))]
            :
                [])
            .toArray());
        return exports.repeat()(html_1.div()(exports.any(undefined, content_menu_class)([
            html_1.div(menu_class, menu_class)(s => exports.any(undefined, entries_class)(entries(s))(s)),
            html_1.div(content_class, content_class)((s) => s.selected.kind == "item" ?
                p(s.selected.value).then(undefined, (p_res) => core_1.unit(Object.assign({}, s, { last_action: { kind: "p", p_res: p_res } })))
                :
                    core_1.unit(s).never())
        ])))({ selected: selected_item == undefined ? { kind: "nothing" } : { kind: "item", value: selected_item },
            sub_selected: selected_sub_menu == undefined ? { kind: "nothing" } : { kind: "sub menu", label: selected_sub_menu },
            last_action: { kind: "init" },
            shown_range: type == "side menu" ? undefined : { first: 0, amount: type.max_tabs } })
            .filter(s => s.last_action.kind != "p")
            .map(s => s.last_action.kind == "p" && s.last_action.p_res);
    };
};
exports.custom = function (key, dbg) {
    return (render) => core_1.make_C(ctxt => cont => render(ctxt)(cont));
};
exports.hide = (f_name, f) => exports.repeat()(visible => primitives_1.bool("edit", "plus/minus")(visible))(false).then(`${f_name} toggle`, visible => !visible ?
    core_1.unit(null)
    :
        f.then(`visible ${f_name}`, _ => core_1.unit(null)));
//# sourceMappingURL=combinators.js.map