// Removes leading whitespaces
function LTrim( value ) {
    var re = /\s*((\S+\s*)*)/;
    return value.replace(re, "$1");
}

// Removes ending whitespaces
function RTrim( value ) {
    var re = /((\s*\S+)*)\s*/;
    return value.replace(re, "$1");
}

// Removes leading and ending whitespaces
function trim( value ) { return LTrim(RTrim(value)); }

/*
   This is a custom function/object, that implements the
   basic functionalities of a Hash.
*/
function Hash() {
    this.length = 0;
    this.items = new Array();
    for (var i = 0; i < arguments.length; i += 2) {
        if (typeof(arguments[i + 1]) != 'undefined') {
            this.items[arguments[i]] = arguments[i + 1];
            this.length++;
        }
    }

    this.removeItem = function(in_key) {
        var tmp_value;
        if (typeof(this.items[in_key]) != 'undefined') {
            this.length--;
            var tmp_value = this.items[in_key];
            delete this.items[in_key];
        }
        return tmp_value;
    }

    this.getItem = function(in_key) {
        return this.items[in_key];
    }

    this.setItem = function(in_key, in_value) {
        if (typeof(in_value) != 'undefined') {
            if (typeof(this.items[in_key]) == 'undefined') {
                this.length++;
            }
            this.items[in_key] = in_value;
        }
        return in_value;
    }

    this.hasItem = function(in_key) {
        return typeof(this.items[in_key]) != 'undefined';
    }
}

var siPrefixHash = new Hash();
siPrefixHash.setItem('M', Math.pow(10,6));    // Mega  (input * 10 ^  6 )
siPrefixHash.setItem('k', Math.pow(10,3));    // Kilo  (input * 10 ^  3 )
siPrefixHash.setItem('h', Math.pow(10,2));    // Hecto (input * 10 ^  2 )
siPrefixHash.setItem('da', Math.pow(10,1));   // Deca  (input * 10 ^  1 )
siPrefixHash.setItem('d', Math.pow(10,-1));   // Deci  (input * 10 ^ -1 )
siPrefixHash.setItem('c', Math.pow(10,-2));   // Centi (input * 10 ^ -2 )
siPrefixHash.setItem('m', Math.pow(10,-3));   // Milli (input * 10 ^ -3 )
siPrefixHash.setItem('u', Math.pow(10,-6));   // Micro (input * 10 ^ -6 )
siPrefixHash.setItem('n', Math.pow(10,-9));   // Nano  (input * 10 ^ -9 )
siPrefixHash.setItem('p', Math.pow(10,-12));  // Pico  (input * 10 ^ -12)
siPrefixHash.setItem('f', Math.pow(10,-15));  // Femto (input * 10 ^ -15)

/*
   Custom object to hold the numeric, prefix and unit of an input.
*/
function paramDetail (numericValue, siPrefix, siUnit) {
    this.numericValue = numericValue;
    this.siPrefix = siPrefix;
    this.siUnit = siUnit;
}

/*
   This function parses the given inValue to check for the
   actual value and units entered. The expectedEnd is used as a
   marker to check for voltage or current.
   returns null, if the inValue is invalid.
   else returns a paramDetail object with the values parsed.
*/
function parseInput(inValue, expectedEnd) {
    var outValue = null;

    if (null != inValue && null != expectedEnd) {
        inValue = trim(inValue);
        var lcInValue = inValue.toLowerCase();
        var lcExcpEnd = expectedEnd.toLowerCase();
        var inputLength = lcInValue.length;

        if (lcInValue.lastIndexOf(lcExcpEnd) == (inputLength - expectedEnd.length)) {
            inValue = inValue.slice(0, inputLength - expectedEnd.length);
        }

        if (!isNaN(parseFloat(inValue))) {
            if (!isNaN(inValue)) {
                outValue = new paramDetail(parseFloat(inValue), null, expectedEnd);
            } else {
                var numericValue = parseFloat(inValue);
                var siPrefix = null;
                var sliceLength = 0;

                if (inValue.indexOf('.') == 0) {
                    sliceLength = numericValue.toString().length - 1;
                } else {
                    sliceLength = numericValue.toString().length;
                }

                siPrefix = trim(inValue.slice(sliceLength , inputLength));

                if (null != siPrefix && siPrefixHash.hasItem(siPrefix)) {
                    outValue = new paramDetail(numericValue, siPrefix, expectedEnd);
                } else {
                    sliceLength = lcInValue.lastIndexOf('0') + 1;
                    siPrefix = trim(inValue.slice(sliceLength , inputLength));

                    if (null != siPrefix && siPrefixHash.hasItem(siPrefix)) {
                        outValue = new paramDetail(numericValue, siPrefix, expectedEnd);
                    } else {
                        outValue = null;
                    }
                }
            }
        }
    }
    return outValue;
}

/*
   This function returns the standard value for a given number and prefix.
   returns a null if the prefix is invalid.
*/
function getStandardValue(inValue, inPrefix) {
    var outValue = null;

    if (null != inValue && null != inPrefix && siPrefixHash.hasItem(inPrefix)) {
        outValue = inValue * siPrefixHash.getItem(inPrefix);
    } else if (null != inValue && null == inPrefix) {
        outValue = inValue;
    }
    return outValue;
}

/*
   This function checks if the given input is within the min and max values
*/
function isValidEntry(inValue, exMin, exMax, exEnd) {
    var isValid = false;
    var parsedInput = parseInput(inValue, exEnd);
    if (null != parsedInput) {
        var siValue = getStandardValue(parsedInput.numericValue, parsedInput.siPrefix);
        if (null != siValue && siValue >= exMin && siValue <= exMax) {
            isValid = true;
        }
    }
    return isValid;
}
