/** @module logic **/
const { curry } = require('../fp');

/** Will evaluate the first function, if it throws any exception will evaluate the second one.
 * @argument {Function} mainFn  function to be executed.
 * @argument {Function|*} failOver function or value to fail over if first one fails.
 * @returns {Function} excepting to receive fn arguments.
 * @example
 * const jsonOr = either(parse.JSON,value => `Cannot parse ${value} as json`)
 * jsonOr(null) // -> "Cannot parse null as json"
 * @example
 * const jsonOr = either(parse.JSON,33)
 * jsonOr(null) // -> 33
 * jsonOr('{"a":1}') // -> {a:1}
 * @method
 */
const either = (mainFn, failOver) => (...arg) => {
  try {
    return mainFn(...arg);
  } catch (e) {
    return fnOrValue(failOver, e);
  }
};
/** Given a value that can be a function if it's a function we call it passing the data to it
 * if not we just return it
 * @argument {Function|*} fnOrVal a function or any value
 * @arugment {*} data any kind of data
 * @example
 * fnOrValue(3,4) // -> 3
 * fnOrValue(4,null) // -> 4
 * fnOrValue(x => x+1,4) // -> 5
 * fnOrValue(x => x*2,4) // -> 8
 * @returns {*}
 * @method
 */
const fnOrValue = curry((fnOrVal, data) => (typeof fnOrVal === 'function' ? fnOrVal(data) : fnOrVal));
/** Function that returns the passed value
 * @argument {*} x any value
 * @returns {*} any value
 * @example
 * [1,2].map(identity) // -> [1,2]
 * @method
 */
const identity = x => x;
/** Function that negates any passed function value
 * @argument {Function} fn function to be negated
 * @returns {Boolean} negated boolean value
 * @example
 * const isNumber = not(isNaN)
 * isNumber(33) // -> true
 * @method
 */
const not = fn => (...args) => !fn(...args);

module.exports = { either, fnOrValue, identity, not };