package usda.weru.mcrew;

/**
 *
 * @author maxerdwien
 */
public class InputLimits {

    private final Double absoluteMinimum;
    private final Boolean absoluteMinimumIncluded;

    private final Double absoluteMaximum;
    private final Boolean absoluteMaximumIncluded;

    private final Double recommendedMinimum;
    private final Boolean recommendedMinimumIncluded;

    private final Double recommendedMaximum;
    private final Boolean recommendedMaximumIncluded;
    
    private final DataType dataType;
    
    private final Boolean DEFAULT_INCLUSION = true;

    /**
     * It's important that these be in order of increasing severity
     */
    public enum TableStatus {

        //The OKAY state specifies that the data is within expected ranges.
        OKAY(-1), 
        //The INIT case is an escape case so we can properly color the initialization
        //operations on the table.
        INIT(0),
        //The WARNSAVE state specifies that the data is outside of the
        //expected range, but should not crash the code.
        WARNSAVE(1), 
        //The NOSAVE state specifies that the data is outside of a range that
        //WEPS is designed to deal with and will probably crash the code.
        NOSAVE(2),
        //The CHOICE state specifies that the data within the field should not
        //be checked, as it would be impossible for it to be incorrect unless 
        //hand edited.
        CHOICE(3);
        
        private final int code;
        
        TableStatus(int input)
        {
            code = input;
        }
        
        public boolean lessThan(TableStatus other)
        {
            return this.code < other.code;
        }
    }
    
    public static DataType checkType(String value)
    {
        try
        {
            Float.parseFloat(value);
            return DataType.FLOAT;
        }
        catch(NumberFormatException wrongType)
        {}
        try
        {
            Integer.parseInt(value);
            return DataType.INTEGER;
        }
        catch(NumberFormatException wrongType)
        {}
        return DataType.STRING;
    }
    
    private enum DataType
    {
        INTEGER,
        FLOAT,
        STRING,
        NCON
    }

    public InputLimits(String aMin, String aMax, String rMin, String rMax, 
            Boolean aMinInc, Boolean aMaxInc, Boolean rMinInc, Boolean rMaxInc, 
            String dType) {
        if(!aMin.equals("")) absoluteMinimum = Double.parseDouble(aMin);
        else absoluteMinimum = null;
        if(aMinInc != null) absoluteMinimumIncluded = aMinInc;
        else absoluteMinimumIncluded = DEFAULT_INCLUSION;

        if(!aMax.equals("")) absoluteMaximum = Double.parseDouble(aMax);
        else absoluteMaximum = null;
        if(aMaxInc != null) absoluteMaximumIncluded = aMaxInc;
        else absoluteMaximumIncluded = DEFAULT_INCLUSION;

        if(!rMin.equals("")) recommendedMinimum = Double.parseDouble(rMin);
        else recommendedMinimum = null;
        if(rMinInc != null) recommendedMinimumIncluded = rMinInc;
        else recommendedMinimumIncluded = DEFAULT_INCLUSION;

        if(!rMax.equals("")) recommendedMaximum = Double.parseDouble(rMax);
        else recommendedMaximum = null;
        if(rMinInc != null) recommendedMaximumIncluded = rMaxInc;
        else recommendedMaximumIncluded = DEFAULT_INCLUSION;

        dataType = processType(dType);
    }
    
    private DataType processType(String input)
    {
        if(input.equalsIgnoreCase("int")) return DataType.INTEGER;
        else if(input.equalsIgnoreCase("float")) return DataType.FLOAT;
        else return DataType.STRING;
    }

    private Boolean belowAbsolute(double d) {
        if (absoluteMinimum == null) {
            return false;
        }
        if (absoluteMinimumIncluded) {
            return d < absoluteMinimum;
        } else {
            return d <= absoluteMinimum;
        }
    }

    private Boolean aboveAbsolute(double d) {
        if (absoluteMaximum == null) {
            return false;
        }
        if (absoluteMaximumIncluded) {
            return d > absoluteMaximum;
        } else {
            return d >= absoluteMaximum;
        }
    }

    private Boolean belowRecommended(double d) {
        if (recommendedMinimum == null) {
            return false;
        }
        if (recommendedMinimumIncluded) {
            return d < recommendedMinimum;
        } else {
            return d <= recommendedMinimum;
        }
    }

    private Boolean aboveRecommended(double d) {
        if (recommendedMaximum == null) {
            return false;
        }
        if (recommendedMaximumIncluded) {
            return d > recommendedMaximum;
        } else {
            return d >= recommendedMaximum;
        }
    }
    
    private boolean correctType(String value)
    {
        switch(dataType)
        {
            case FLOAT:
                try
                {
                    Float.parseFloat(value);
                    return true;
                }
                catch(NumberFormatException wrongType)
                {
                    return false;
                }
            case INTEGER:
                try
                {
                    Integer.parseInt(value);
                    return true;
                }
                catch(NumberFormatException wrongType)
                {
                    return false;
                }
            case STRING:
                return true;
            case NCON:
                return false;
            default:
                return true;
        }
    }

    /**
     * 
     * @param d number to evaluate
     * @return whether and how d breaks the bounds
     */
    public TableStatus evaluateInput(double d) {
        if (aboveAbsolute(d) || belowAbsolute(d)) {
            return TableStatus.NOSAVE;
        } else if (belowRecommended(d) && !belowAbsolute(d)) {
            return TableStatus.WARNSAVE;
        } else if (aboveRecommended(d) && !aboveAbsolute(d)) {
            return TableStatus.WARNSAVE;
        } else {
            return TableStatus.OKAY;
        }
    }
    
    public TableStatus evaluateInput(String value)
    {
        if(correctType(value))
        {
            switch(dataType)
            {
                case FLOAT:/*FALLTHROUGH*/
                case INTEGER:
                    try{return evaluateInput(Double.parseDouble(value));}
                    catch(NumberFormatException fail) { return TableStatus.NOSAVE; }
                case STRING:
                    return TableStatus.OKAY;
                default:
                    return TableStatus.NOSAVE;
            }
        }
        return TableStatus.NOSAVE;
    }
}
