// The utility for processing mutually exclusive elements.

// Elements can belong to groups. Utillity supports 3 types of exclusivity:
// 1. Element to element exclusivity - when elements from different groups are mutually exclusive.
// 2. Group to group exclusivity - when different groups are mutually exclusive.
// 3. Group exlusivity - when all elements from one group are mutually exclusive.

// To use this utility need to:
// 1. Setup ids and values for document elements in following format: <groupId>-<elementId>. Note that for first option in the selecet control id must be equal 'selectOption' and value must be empty  but for other id and value equal to <groupId>-<elementId>.
// 2. Initialize exlusivity using functions "addLockedElementByGroup (groupId, documentElemetId)" and "addLockedElementByElement (elementId, documentElemetId)".
// 3. Attach functions "processElementToElementLocks(elementId, element)" and  "processGroupToGroupLocks(groupId, element)" on change events of document elements.

// Note.Utility was tested for options and checkboxes.

var groupIdToExclusiveElementMap = new Object();
var productIdToExclusiveElementMap = new Object();

var previouslySelectedOption = null;	// Option that selected before the current.

function putToMap(map, key, value) {
    if ((map[key] == undefined) || (map[key] == null)) {
        map[key] = new Array();
    }
    map[key].push(value);
}

function addElementLockedByGroup (groupId, documentElemetId) {
    var element = document.getElementById(documentElemetId);
    if ((element != null) && (element != undefined)) {
       putToMap(groupIdToExclusiveElementMap, groupId, element);
    }
}

function addElementLockedByElement (elementId, documentElemetId) {
    var element = document.getElementById(documentElemetId);
    if ((element != null) && (element != undefined)) {
        putToMap(productIdToExclusiveElementMap, elementId, element);
    }
}

function getGroupToElementsLocks(groupId) {
    if (groupId == null || groupId == undefined) {
        return null;
    }

    return groupIdToExclusiveElementMap[groupId];
}

function getElementToElemetLocks(elementId) {
    if (elementId == null || elementId == undefined) {
        return null;
    }

    return productIdToExclusiveElementMap[elementId];
}

/**
 * Removes previously made locks.
 */
function removePreviousLocks() {
	removeLocks(groupIdToExclusiveElementMap);
	removeLocks(productIdToExclusiveElementMap);
}

/**
 * Removes locks from the map specified if no other locks are set.
 *
 * @param locksMap A map of locks.
 */
function removeLocks(locksMap) {
	for (var lockId in locksMap) {
		for (var i = 0; i < locksMap[lockId].length; i++) {
			unLockElement(locksMap[lockId][i]);
		}
	}
}

function processExclusivity(element) {
    if (element != null && element != undefined) {
        if (!element.disabled) {
			// Remove locks
			removePreviousLocks();

			if (element.value && element.value.length > 0) {
				var productId = getProductId(element);
				var groupId = getGroupId(element);

				if (productId != null) {
					processElementToElementLocks(productId, element);
				}

				if (groupId != null) {
					processGroupToGroupLocks(groupId, element);
				}
			}
		}
    }
}

function processElementToElementLocks(productId, element) {
    processLocks(getElementToElemetLocks(productId), element);
}

function processGroupToGroupLocks(groupId, element) {
    processLocks(getGroupToElementsLocks(groupId), element);
}

function processLocks(locks, element) {
    if ((locks != null) && (locks != undefined)) {
        for (var i = 0; i < locks.length; i ++) {

            if (locks[i] == element) continue;

            if (isElementTicked(element)) {
                lockElement(locks[i]);
            } else {
                unLockElement(locks[i]);
            }
        }
    }
}

function lockElement (element) {
    if (element.tagName == 'OPTION') {
        disableOption(element);
    } else {
        element.disabled = true;
    }
}

function unLockElement (element) {
    if (!areThereOtherLocks(element)) {
        if (element.tagName == 'OPTION') {
            enableOption(element);
        } else {
            element.disabled = false;
        }
    }
}

function areThereOtherLocks(element) {
    if (element != null && element != undefined) {
        var grouplocks = getGroupToElementsLocks(getGroupId(element));

        if (areThereTickedLocks(grouplocks)) {
            return true;
        }

        var elementlocks = getElementToElemetLocks(getProductId(element));

        if (areThereTickedLocks(elementlocks)) {
            return true;
        }
    }

    return false;
}

function areThereTickedLocks(locks) {
    if (locks != null && locks != undefined) {
        for (var i = 0; i < locks.length; i++) {
            if (isElementTicked(locks[i])) {
                return true;
            }
        }
    }
    return false;
}

function isElementTicked(element) {
    var result = false;

    if (element.type == 'checkbox') {
        result = element.checked;
    }

    if (element.tagName == 'OPTION' ) {
        if (element.id == 'selectOption') {
            result = false;
        } else {
            result = element.selected;
        }
    }

    return result;
}

// format of element id: groupId-productId. See comments on the top of file.
function getGroupAndProductIds(element) {
    var ids = null;
    var elementId = element.value;

    if (elementId != null && elementId != undefined && elementId.match(/(\d)+-(\d)+/) != null) {
        ids = elementId.split('-');
    }

    return ids;
}

function getGroupId(element) {
    var groupId = null;
    var ids = getGroupAndProductIds(element);

    if (ids != null) {
        groupId = ids[0];
    }

    return groupId;
}

function getProductId(element) {
    var productId = null
    var ids = getGroupAndProductIds(element);

    if (ids != null) {
        productId = ids[1];
    }

    return productId;
}