// Two's complement 4 bit integer can represent values -8 to 7

var BINARY_BASE = 2; // Binary is base 2
var NUM_OF_BITS = 4; // 4 bit integer
var RANGE = Math.pow(BINARY_BASE, NUM_OF_BITS); // 2^4 = 16
var MID_RANGE = Math.floor(RANGE / 2); // 16/2 = 8
var MAX_INT = MID_RANGE-1;
var MIN_INT = -MID_RANGE; // -8


var currentStep = 0;
var steps = new Array("step1", "step2", "step3", "step4", "step5", "step6", "step7", "step8");

function Reset(){
    document.forms[0].next.disabled = false;
    document.forms[0].reset();
    currentStep = 0;
	
	for(var i=0; i<steps.length; i++) {
		ClearTextNode(document.getElementById(steps[i]));
	}
}


function NextStep(a, b){
    currentStep++;
    switch (currentStep) {
        case 1:
            Step1(a, b);
            break;
        case 2:
            Step2(a, b);
            break;
        case 3:
            Step3(a, b);
            break;
        case 4:
            Step4(a, b);
            break;
        case 5:
            Step5(a, b);
            break;
        case 6:
            Step6(a, b);
            break;
        case 7:
            Step7(a, b);
            break;
        case 8:
            Step8(a, b);
            document.forms[0].next.disabled = true;
            break;
    }
}


function Calculate(aType){
    var formRef = document.forms[0];
    
    var aIndex = formRef.a.selectedIndex;
    var bIndex = formRef.b.selectedIndex;
    
    // Always a - b
    var a = formRef.a[aIndex].value;
    var b = formRef.b[bIndex].value;
    
    var isValid = CheckValues(a, b);
    
    if (!isValid) {
        alert("A 4 bit integer cannot represent the result.");
    }
    else {
        if (aType == "ALL") {
            Step1(a, b);
            Step2(a, b);
            Step3(a, b);
            Step4(a, b);
            Step5(a, b);
            Step6(a, b);
            Step7(a, b);
            Step8(a, b);
            document.forms[0].next.disabled = true;
        }
		else if (aType == "NEXT") {
			NextStep(a, b);
        }
    }
    return;
}

function CheckValues(a, b){
    var result = parseInt(a) - parseInt(b);
    
    return !((result < MIN_INT) || (result > MAX_INT));
}

function TwosComplementRepresentation(aDecimal){
    aDecimal = parseInt(aDecimal);
    
    var twosComp;
    
    if (aDecimal < 0) {
        // Because aDecimal is negative, this is actually subtraction.
        twosComp = RANGE + aDecimal;
    }
    else {
        twosComp = aDecimal;
    }
    return twosComp;
}

function ToNegative(aDecimal){
    return -(parseInt(aDecimal));
}

function DecimalToBinaryString(aDecimal){
    var binaryString = "";
    var tmp;
    
    while ((tmp = Math.floor(aDecimal / 2)) >= 0) {
        var remainder = (aDecimal % 2);
        
        if (remainder > 0) {
            binaryString += "1";
        }
        else {
            binaryString += "0";
        }
        
        if (tmp <= 0) {
            break;
        }
        aDecimal = tmp;
    }
    return ReverseString(binaryString);
}

function BinaryStringToDecimal(aBinary){
    aBinary = aBinary.replace(' ', '');
    
    var numBits = aBinary.length;
    var decimal = 0;
    
    for (var i = 0; i < numBits; i++) {
        var bit = parseInt(aBinary[i]);
        if (bit == 1) {
            decimal += Math.pow(BINARY_BASE, i);
        }
    }
    return decimal;
}

function ReverseString(aString){
    // This rather nice string reverse code wasn't written by me.
    // I just need to find the source before I can credit the author.
    return aString.split('').reverse().join('');
}

function CorrectBitLength(aBinary){
    aBinary = aBinary.replace(' ', '');
    
    var overflow = "";
    var padding = "";
    var diff;
    
    if (aBinary.length > NUM_OF_BITS) {
        overflow = aBinary.substring(aBinary.length - NUM_OF_BITS);
    }
    else if (aBinary.length < NUM_OF_BITS) {
            overflow = aBinary;
            diff = NUM_OF_BITS - aBinary.length;
            for (var i = 0; i < diff; i++) {
                padding += "0";
            }
    }
	else {
		overflow = aBinary;
	}
    return padding + overflow;
}

function AddChildTextNode(aTargetRef, aString){
    ClearTextNode(aTargetRef);
    var childDiv = document.createTextNode(aString);
    aTargetRef.appendChild(childDiv);
}

function ClearTextNode(aTargetRef){
    if (aTargetRef.childNodes.length > 0) {
        aTargetRef.removeChild(aTargetRef.childNodes.item(0));
    }
}

function Step1(a, b){
    var str = a + " - " + b;
    targetRef = document.getElementById(steps[0]);
    AddChildTextNode(targetRef, str);
}

function Step2(a, b){
    var str = a + " + (" + ToNegative(b) + ")";
    targetRef = document.getElementById(steps[1]);
    AddChildTextNode(targetRef, "Equivalent to " + str);
}

function Step3(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
    
	var str = "Two's complement of " + a + " is " + aBin + ", " +
	"Two's complement of " + ToNegative(b) + " is " + bBin;
    
    targetRef = document.getElementById(steps[2]);
    AddChildTextNode(targetRef, str);
}

function Step4(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
    
    var result = parseInt(aBin) + parseInt(bBin);
    var str = aBin + " + " + bBin + " = " + result;
    
    targetRef = document.getElementById(steps[3]);
    AddChildTextNode(targetRef, str);
}

function Step5(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
	
    var result = parseInt(aBin) + parseInt(bBin);
    var binaryResult = DecimalToBinaryString(result);
    
    targetRef = document.getElementById(steps[4]);
    AddChildTextNode(targetRef, result + " in binary is " + binaryResult);
}

function Step6(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
   
    var result = parseInt(aBin) + parseInt(bBin);
    var binaryResult = DecimalToBinaryString(result);
    var overflow = CorrectBitLength(binaryResult);
    
    targetRef = document.getElementById(steps[5]);
    AddChildTextNode(targetRef, NUM_OF_BITS + " bit representation is " + overflow);
}

function Step7(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
    
    var result = parseInt(aBin) + parseInt(bBin);
    var binaryResult = DecimalToBinaryString(result);
    var overflow = CorrectBitLength(binaryResult);
	

	var decimalValue = BinaryStringToDecimal(ReverseString(overflow));
    var str = overflow + " in decimal is " + decimalValue;
    
    targetRef = document.getElementById(steps[6]);
    AddChildTextNode(targetRef, str);
}

function Step8(a, b){
    var aBin = TwosComplementRepresentation(a);
    var bBin = TwosComplementRepresentation(ToNegative(b));
    
    var result = parseInt(aBin) + parseInt(bBin);
    var binaryResult = DecimalToBinaryString(result);
    var overflow = CorrectBitLength(binaryResult);
    var decimalValue = BinaryStringToDecimal(ReverseString(overflow));
    
    var correctedValue = 0;
    
    if (decimalValue >= MID_RANGE) {
        correctedValue = decimalValue - RANGE;
    }
    else {
        correctedValue = decimalValue;
    }
    
    var str = decimalValue + " is the Two's Complement of " + correctedValue;
    
    targetRef = document.getElementById(steps[7]);
    AddChildTextNode(targetRef, str);
    return overflow;
}

