package haskell.prelude;

import haskell.lang.Data;
import haskell.lang.Exp;
import haskell.prelude.text.Show;

/**
 * data Bool = False | True
 *     deriving (Eq, Ord, Enum, Bounded, Read, Show)
 */
public class Bool
        extends AbstractEnum<Bool>
        implements Data<Bool>,
                Eq<Bool>,
                Ord<Bool>,
                Enum<Bool>,
                Bounded<Bool>,
                //Read<Bool>,
                Show<Bool> {

    final Boolean value;

    Bool(final java.lang.String name, final boolean value) {
        super(name);
        this.value = value;
    }

    Bool(final Exp<Bool> expression) {
        super(expression);
        this.value = null;
    }

    public static Bool valueOf(final boolean value) {
        return (value ? True() : False());
    }

    boolean value() {
        return eval().value;
    }

    // ---- java.lang.Object

    @Override
    public int hashCode() {
        return Boolean.valueOf(value()).hashCode();
    }

    @Override
    public boolean equals(final Bool other) {
        return (value() == other.value());
    }

    // ---- java.lang.Comparable

    public int compareTo(final Bool other) {
        final boolean x = value();
        final boolean y = other.value();
        if (x == y) {
            return 0;
        } else if (!x) {
            return -1;
        } else {
            return 1;
        }
    }

    // ---- data Bool

    /**
     * False
     */
    public static Bool False() {
        return False;
    }

    static final Bool False = new Bool("False", false);

    public boolean isFalse() {
        return !value();
    }

    /**
     * True
     */
    public static Bool True() {
        return True;
    }

    static final Bool True = new Bool("True", true);

    public boolean isTrue() {
        return value();
    }

    /**
     * (&&) :: Bool -> Bool -> Bool
     */
    public static Operator<Bool, Bool, Bool> and() {
        return new Operator<Bool, Bool, Bool>(
                "&&", Bool.class, Bool.class, "and", Bool.class, Bool.class);
    }

    /**
     * True  && x = x
     * False && _ = False
     */
    public static Bool and(final Bool x, final Bool y) {
        if (x.isTrue()) {
            return y;
        } else {
            return False();
        }
    }

    /**
     * this && x
     */
    public final Bool _and_(final Bool x) {
        return and(this, x);
    }

    /**
     * (||) :: Bool -> Bool -> Bool
     */
    public static Operator<Bool, Bool, Bool> or() {
        return new Operator<Bool, Bool, Bool>(
                "||", Bool.class, Bool.class, "or", Bool.class, Bool.class);
    }

    /**
     * True  || _ = True
     * False || x = x
     */
    public static Bool or(final Bool x, final Bool y) {
        if (x.isTrue()) {
            return True();
        } else {
            return y;
        }
    }

    /**
     * this || x
     */
    public final Bool _or_(final Bool x) {
        return or(this, x);
    }

    /**
     * not :: Bool -> Bool
     */
    public static Function<Bool, Bool> not() {
        return new Function<Bool, Bool>(
                Bool.class, Bool.class, "not", Bool.class);
    }

    /**
     * not True  = False
     * not False = True
     */
    public static Bool not(final Bool x) {
        if (x.isTrue()) {
            return False();
        } else {
            return True();
        }
    }

    /**
     * otherwise :: Bool
     * otherwise = True
     */
    public static Bool otherwise() {
        return True();
    }

    // ---- instance Eq Bool

    protected Eq.Support<Bool> _Eq_() {
        return Eq();
    }

    public static Eq.Support<Bool> Eq() {
        return Ord;
    }

    // ---- instance Ord Bool

    protected Ord.Support<Bool> _Ord_() {
        return Ord();
    }

    public static Ord.Support<Bool> Ord() {
        return Ord;
    }

    static final Ord.Support<Bool> Ord
            = new AbstractOrd.Support<Bool>(Bool.class);

    // ---- instance Enum Bool

    protected Enum.Support<Bool> _Enum_() {
        return Enum();
    }

    public static Enum.Support<Bool> Enum() {
        return Enum;
    }

    static final Enum.Support<Bool> Enum
            = new Enum.Support<Bool>(Bool.class) {

        /**
         * toEnum n
         */
        public Bool toEnum(final Int n) {
            return Bool.valueOf(n.value() != 0);
        }

        /**
         * fromEnum x
         */
        public Int fromEnum(final Bool x) {
            return Int.valueOf(x.isTrue() ? 1 : 0);
        }

    };

    // ---- instance Bounded Bool

    public static Bounded.Support<Bool> Bounded() {
        return Bounded;
    }

    static final Bounded.Support<Bool> Bounded
            = new Bounded.Support<Bool>() {

        /**
         * minBound
         */
        public Bool minBound() {
            return False();
        }

        /**
         * maxBound
         */
        public Bool maxBound() {
            return True();
        }

    };

}
