/**
 * Contains `Just` constructor and associated methods.
 */
import Nothing, {isNothing} from './Nothing';
import Monad from '../monad/Monad';
import {isset} from 'fjl';

export const

    /**
     * Checks for `Just`.
     * @function module:maybe.isJust
     * @param x {*}
     * @returns {boolean}
     */
    isJust = x => x instanceof Just,

    /**
     * Functional constructor (function that returns an instance) for `Just` -
     * Same as `new Just(...)` (just shorter and can be used as a function).
     * @function module:maybe.just
     * @param x {Just|*}
     * @returns {Just}
     */
    just = x => new Just(x),

    /**
     * Ensures `Just`
     * @function module:maybe.toJust
     * @param x {Just|*}
     * @returns {Just}
     */
    toJust = x => isJust(x) ? x : just(x)

;

/**
 * @class maybe.Just
 * @param x {*}
 * @property value {*}
 * @extends module:monad.Monad
 */
export default class Just extends Monad {
    /**
     * Maps incoming function over contained value and
     * returns result wrapped in `Just`.
     * @method module:maybe.Just#map
     * @param fn {Function} - Unary operation.
     * @returns {Just|Nothing}
     */
    map (fn) {
        const {constructor} = this,
            value = this.valueOf();
        return isset(value) && !isNothing(value) ? constructor.of(fn(value)) :
            constructor.counterConstructor.of(value);
    }

    /**
     * Applicative pure - Same as `new Just(...)`.
     * @method module:maybe.Just.of
     * @static
     * @param x {*}
     * @returns {Just}
     */
    static of (x) { return just(x); }
}

/**
 * @static
 * @member {Functor} module:maybe.Just.counterConstructor
 */
Just.counterConstructor = Nothing;