// React immutable utilities.
define('RIU', [ 'exports', 'immutable' ], function (exports, Immutable) {

var STATE_KEY = 'data';

function isImmutable(o) {
    // TODO: Only checks for more common Immutable objects.
    return Immutable.List.isList(o) || Immutable.Map.isMap(o);
}

function initialState(d) {
    var dd = { };
    dd[STATE_KEY] = d;
    return dd;
}

exports.getStateData = function (d) {
    return d.data;
}

exports.getInitialMapState = function (d) {
    if (Immutable.Map.isMap(d))
        return initialState(d);
    else if (typeof(d) != 'object')
        throw new Error("Bad argument to RIU.getInitialMapState");
    else
        return initialState(Immutable.fromJS(d));
};

exports.mapStateUpdate = function (component, d) {
    component.setState((s) => { 
        let dd = { };
        dd[STATE_KEY] = s[STATE_KEY].merge(d);
        return dd;
    });
};

exports.mapStateSet = function (component, key, v) {
    component.setState((s) => {
        let d = { };
        d[STATE_KEY] = s[STATE_KEY].set(key, v);
        return d;
    });
};

exports.mapStateGet = function (component, key) {
    return component.state[STATE_KEY].get(key);
};

exports.mapStateToDict = function (component) {
    return component.state[STATE_KEY].toObject();
};

exports.mapStateShouldComponentUpdate = function (nextProps, nextState) {
    var component = this;

    // If we're on some crazy old browser without a built in JSON object, then too bad,
    // we won't try to be clever.
    if (typeof(JSON) !== 'object')
        return true;

    // Compare props. First look at function props. Then then do a special check for Immutable
    // objects. Finally, use JSON.stringify to perform a deep equality check
    // that will work almost all of the time for everything else.
    // In the case of unJSONifiable properties, we conservatively return true.
    for (var k in component.props) {
        if (! ({ }).hasOwnProperty.call(component.props, k))
            continue;

        var v = component.props[k];

        if (typeof(v) == 'function') {
            if (component.props[k] !== nextProps[k])
                return true;
        }
        else if (isImmutable(v)) {
            // If one of the properties is an Immutable object, we can trivially
            // check if it or one of its contained objects has changed.
            if (v !== nextProps[k])
                return true;
        }
        else if (typeof(v) == 'string') {
            if (v !== nextProps[k])
                return true;
        }
        else if (typeof(v) == 'number') {
            if (v !== nextProps[k])
                return true;
        }
        else if (v === true || v === false || v === null || v === undefined) {
            if (v !== nextProps[k])
                return true;
        }
        else {
            // Ugly but fast way of doing deep equality comparison on objects.
            var j1 = JSON.stringify(v);
            if (v !== undefined && j1 === undefined)
                return true;
            var j2 = JSON.stringify(nextProps[k]);
            if (nextProps[k] !== undefined && j2 === undefined)
                return true;
            if (j1 != j2)
                return true;
        }
    }

    return component.state[STATE_KEY] !== nextState[STATE_KEY];
}

exports.link = function (key, propDefault) {
    let v = this.S.get(key);
    if (v == null)
        v = (this.props[propDefault] ? this.props[propDefault] : null);

    return {
        value: v,
        requestChange: (newValue) => this.S.set(key, newValue)
    };
};

//
// By convention, React components set this.S = getMapStateWrapper(this)
//
exports.getMapStateWrapper = function (component) {
    function Wrapper() {
        this.set = function (key, v) {
            return exports.mapStateSet(component, key, v);
        };
        this.get = function (key) {
            return exports.mapStateGet(component, key);
        };
        this.update = function (d) {
            return exports.mapStateUpdate(component, d);
        };
    }

    return new Wrapper();
};

});
