"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const combinators_1 = require("./combinators");
function make_C(comp) {
    return {
        comp: comp,
        then: function (key, k, className, dbg) {
            return exports.bind(key, this, k, className, dbg);
        },
        map: function (f, key, dbg) {
            return exports.map(key, dbg)(f)(this);
        },
        filter: function (f, key, dbg) {
            return exports.filter(key, dbg)(f)(this);
        },
        // bind_once:function<B>(this:C<A>, key:string, k:(_:A)=>C<B>, dbg?:()=>string) : C<B> {
        //         return bind_once<A,B>(key, this, k, dbg)
        //       },
        never: function (key) {
            return combinators_1.never(this, key);
        },
        ignore_with: function (x) {
            return this.then(``, _ => exports.unit(x));
        },
        ignore: function (key) {
            return this.then(key, _ => exports.unit(null));
        }
    };
}
exports.make_C = make_C;
class Unit extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = {};
    }
    componentWillReceiveProps(new_props) {
        new_props.debug_info && console.log("New props:", new_props.debug_info(), new_props.value);
        new_props.cont(() => { })(new_props.value);
    }
    componentWillMount() {
        this.props.debug_info && console.log("Component will mount:", this.props.debug_info(), this.props.value);
        this.props.cont(() => { })(this.props.value);
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        return [];
    }
}
exports.unit = function (x, key, dbg) {
    return make_C(ctxt => cont => (React.createElement(Unit, { kind: "unit", debug_info: dbg, value: x, context: ctxt, cont: cont, key: key })));
};
class Join extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { p_inner: "waiting", p_outer: props.p.comp(props.context)(cont => p_inner => this.setState(Object.assign({}, this.state, { p_inner: p_inner.comp(this.props.context)(cb => x => this.props.cont(cb)(x)) }))) };
    }
    componentWillReceiveProps(new_props) {
        new_props.debug_info && console.log("New join props:", new_props.debug_info());
        this.setState({ p_outer: new_props.p.comp(new_props.context)(cont => p_inner => this.setState(Object.assign({}, this.state, { p_inner: p_inner.comp(new_props.context)(cb => x => new_props.cont(cb)(x)) }))) });
    }
    render() {
        return React.createElement("div", null,
            this.state.p_outer,
            this.state.p_inner == "waiting"
                ? []
                : this.state.p_inner);
    }
}
let join = function (p, key, dbg) {
    return make_C(ctxt => cont => React.createElement(Join, { p: p, context: ctxt, cont: cont, debug_info: dbg, key: key }));
};
class Bind extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { k: "waiting for p", p: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.props.debug_info && console.log("New props:", this.props.debug_info());
        if (this.props.once)
            this.setState(Object.assign({}, this.state, { p: "creating" }));
        else
            this.setState(Object.assign({}, this.state, { p: new_props.p.comp(new_props.context)(callback => x => this.setState(Object.assign({}, this.state, { k: new_props.k(x).comp(new_props.context)(callback => x => new_props.cont(callback)(x)) }), callback)) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { p: this.props.p.comp(this.props.context)(callback => x => this.setState(Object.assign({}, this.state, { k: this.props.k(x).comp(this.props.context)(callback => x => this.props.cont(callback)(x)) }), callback)) }));
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        return React.createElement("div", { className: `bind ${this.props.className || ""}` },
            (this.state.k == "waiting for p" || !this.props.once) && this.state.p != "creating"
                ? this.state.p
                : [],
            this.state.k != "waiting for p"
                ? this.state.k
                : []);
    }
}
exports.bind = function (key, p, k, className, dbg) {
    let q = p.map(k, `${key}_map`, dbg);
    return join(q, `${key}_join`, dbg);
    // return make_C<B>(ctxt => cont =>
    //   (React.createElement<BindProps<A,B>>(Bind,
    //     { kind:"bind", debug_info:dbg, p:p, k:k, once:false, cont:cont, context:ctxt, key:key, className:className })))
};
class Map extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { p: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.props.debug_info && console.log("New props:", this.props.debug_info());
        this.setState(Object.assign({}, this.state, { p: new_props.p.comp(new_props.context)(callback => x => new_props.cont(callback)(new_props.f(x))) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { p: this.props.p.comp(this.props.context)(callback => x => this.props.cont(callback)(this.props.f(x))) }));
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        return this.state.p != "creating"
            ? this.state.p
            : [];
    }
}
exports.map = function (key, dbg) {
    return f => p => make_C(ctxt => cont => React.createElement(Map, { kind: "map", debug_info: dbg, p: p, f: f, context: ctxt, cont: cont, key: key }));
};
class Filter extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { p: "creating" };
    }
    componentWillReceiveProps(new_props) {
        this.props.debug_info && console.log("New props:", this.props.debug_info());
        this.setState(Object.assign({}, this.state, { p: new_props.p.comp(new_props.context)(callback => x => { if (new_props.f(x)) {
                new_props.cont(callback)(x);
            } }) }));
    }
    componentWillMount() {
        this.setState(Object.assign({}, this.state, { p: this.props.p.comp(this.props.context)(callback => x => { if (this.props.f(x)) {
                this.props.cont(callback)(x);
            } }) }));
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        return this.state.p != "creating"
            ? this.state.p
            : [];
    }
}
exports.filter = function (key, dbg) {
    return f => p => make_C(ctxt => cont => React.createElement(Filter, { kind: "filter", debug_info: dbg, p: p, f: f, context: ctxt, cont: cont, key: key }));
};
class ShouldComponentUpdate extends React.Component {
    constructor(props, context) {
        super(props, context);
    }
    shouldComponentUpdate(next_props) {
        return next_props.f(next_props.v);
    }
    componentWillReceiveProps(next_props) {
        this.props.debug_info && console.log("New props:", this.props.debug_info());
    }
    render() {
        this.props.debug_info && console.log("Render:", this.props.debug_info());
        // let s = this.props.v as any
        // console.log(`rendering should component update with props ${this.props.debug_info()}`, JSON.stringify(s))
        return this.props.p(this.props.v).comp(this.props.context)(cbk => y => this.props.cont(cbk)(y));
    }
}
exports.should_component_update = function (key, dbg) {
    return f => p => v => make_C(ctxt => cont => React.createElement(ShouldComponentUpdate, { kind: "should component update", debug_info: dbg, p: p, f: f, context: ctxt, cont: cont, key: key, v: v }));
};
class SimpleApplication extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = { context: this.context_from_props(this.props, exports.unit(null)) };
    }
    context_from_props(props, p) {
        let self = this;
        return {
            current_page: p,
            logic_frame: 0,
            force_reload: (callback) => make_C(ctxt => inner_callback => this.setState(Object.assign({}, this.state, { context: Object.assign({}, this.state.context, { logic_frame: this.state.context.logic_frame + 1 }) }), () => inner_callback(callback)(null)) || null),
            set_page: function (x, new_page, callback) {
                return exports.unit(null);
            },
            set_url: function (x, new_url, callback) {
                return exports.unit(null);
            },
            push_route: function (route, callback) {
                return exports.unit(null);
            },
            set_routes: function (routes, callback) {
                return exports.unit(null);
            }
        };
    }
    render() {
        return React.createElement("div", { className: "monadic-application", key: `application@${this.state.context.logic_frame}` }, this.props.p.comp(() => this.state.context)(callback => x => this.props.cont(x)));
    }
}
exports.SimpleApplication = SimpleApplication;
exports.simple_application = function (p, cont) {
    return React.createElement(SimpleApplication, { p: p, cont: cont });
};
//# sourceMappingURL=core.js.map