var _ = require('lodash');
var Q = require('q');
var colors = require('colors');

var definitions = require('./definitions');

var Situation = module.exports = function(values) {
    this._values = _.clone(values);
};

Situation.prototype.get = function(key) {
    if (!(key in this._values)) this.compute(key);
    return Q(this._values[key]);
};

Situation.prototype.respond = function(value) {
    return Q(value);
};

Situation.prototype.claim = function(key) {
    return Q.reject({ claimedValues: [key] });
};

Situation.prototype.compute = function(key) {
    if (key in this._values) return;

    if (!definitions[key]) {
        this._values[key] = Q.reject({ claimedValues: [key] });
        return;
    }

    var self = this;
    var def = definitions[key];

    var requiredValues = def.required ? def.required.map(function(key) { return self.get(key); }) : [];
    var optionalValues = def.optional ? def.optional.map(function(key) { return self.get(key); }) : [];

    var requiredValuesProcessed = Q.allSettled(requiredValues).then(function(promises) {
        var values = [];
        var claimedValues = [];
        promises.forEach(function(promise) {
            values.push(promise.value);
            if (promise.state === 'rejected') {
                claimedValues.push(promise.reason.claimedValues);
            }
        });

        if (claimedValues.length > 0) return Q.reject({ claimedValues: _.uniq(_.flatten(claimedValues)) });
        return values;
    });

    var optionalValuesProcessed = Q.allSettled(optionalValues).then(function(promises) {
        return _.pluck(promises, 'value');
    });

    this._values[key] = Q.all([requiredValuesProcessed, optionalValuesProcessed]).then(function(values) {
        return Q.fapply(_.bind(def.getValue, self), _.flatten(values)).fail(function(reason) {
            if (!reason.claimedValues) throw reason;
            // TODO: When claimedValues.length > 1
            return Q.reject(self._values[reason.claimedValues].inspect().reason);
        });
    });
};

Situation.prototype.toJSON = function() {
    var response = {};
    _(this._values).forEach(function(promise, key) {
        var snapshot = Q(promise).inspect();
        // Important!
        console.log(key, 'is', snapshot.state.green, snapshot.value, snapshot.reason);
        if (snapshot.state === 'fulfilled') response[key] = snapshot.value;
    });
    return response;
};

@@ -492,29 +492,876 @@
 imedValues: 
-%5Bkey%5D
+_.isArray(key) ? key : %5Bkey%5D %7D);%0A%7D;%0A%0ASituation.prototype.assumeFilter = function(keys, filter) %7B%0A    if (!_.isArray(keys)) keys = %5Bkeys%5D;%0A    var self = this;%0A    var notMatchingKeys = _.filter(keys, function(key) %7B%0A        return !filter(self.get(key).inspect().value);%0A    %7D);%0A    if (notMatchingKeys.length %3E 0) throw %7B claimedValues: notMatchingKeys %7D;%0A%7D;%0A%0ASituation.prototype.assumeNumber = function(keys) %7B%0A    return this.assumeFilter(keys, function(value) %7B%0A        return _.isNumber(value);%0A    %7D);%0A%7D;%0A%0ASituation.prototype.assumeDefined  = function(keys) %7B%0A    return this.assumeFilter(keys, function(value) %7B%0A        return !_.isUndefined(value);%0A    %7D);%0A%7D;%0A%0ASituation.prototype.assumeValue = function(keys, targetValue) %7B%0A    return this.assumeFilter(keys, function(value) %7B%0A        return !_.isUndefined(value) && value === targetValue;%0A   
  %7D);%0A%7D;%0A%0ASit

Add assume functions