

if (typeof(Control.Tabs) == "undefined") {
    throw "profile.js requires Control.Tabs to be loaded.";
}

function MTabbedPickerContext(tabsManager, staticTabPrefixes, searchDiv, selectedDiv, selectedItemContainers, searchContext, maxBrands)
{
    this.tabsManager = tabsManager;
    this.tabsManager.observe('beforeChange', this.willChangeTabs.bind(this));

    this.staticTabPrefixes = staticTabPrefixes;
    this.searchDiv = searchDiv;
    this.selectedDiv = selectedDiv;
    this.searchContext = searchContext;
    this.maxBrands = maxBrands;
    this.searchContext.observe('didSearch', this.didSearch.bind(this));
    this.selectedItemContainers = selectedItemContainers;

    // collect the initially selected items from the selected tab
    var nodes = selectedDiv.getElementsByTagName('P');
    this.selectedItems = MTabbedPickerContext.nodeListToArray(nodes);
    this.selectedItems.sort(MTabbedPickerContext.itemComparator);
    this.selectedTabNeedsLayout = false;
    this.changingTabsAutomatically = false;

    // initial update, the HTML renders it hidden for starters
    $('tooManyBrandsError').style.visibility = this.selectedItems.length > this.maxBrands ? 'visible' : 'hidden';
}

// Returns the raw ids (no prefixes) of the selected items.  Used by clients to post the forms.
MTabbedPickerContext.prototype.getSelectedIds = function(checkbox)
{
    var results = new Array();
    for (var i = 0; i < this.selectedItems.length; i++) {
        var item = this.selectedItems[i];
        var id = item.id.substr(1);
        results.push(id);
    }
    return results;
}

MTabbedPickerContext.prototype.willChangeTabs = function(oldTab, newTab)
{
    if (newTab == this.selectedDiv && this.selectedTabNeedsLayout) {
        this.layoutSelectedItems();
    }

    // When clicking on a tab, select the search field, but don't do it if we're auto-selecting
    // the search tab in response to some typing that is already in progress.
    if (!this.changingTabsAutomatically) {
        this.searchContext.getSearchField().focus();
        this.searchContext.getSearchField().select();
    }
}

MTabbedPickerContext.prototype.didSearch = function()
{
    // When the user starts typing, make sure the Search tab is visible
    if (this.tabsManager.activeContainer != this.searchDiv) {
        this.changingTabsAutomatically = true;
        this.tabsManager.setActiveTab(this.searchDiv.id);
        this.changingTabsAutomatically = false;
    }
}

MTabbedPickerContext.prototype.itemClicked = function(checkbox)
{
    // I had weird unreliable results until I captured this state before any DOM changes
    var turningOn = checkbox.checked;

    var itemId;
    var itemTabPrefix;

    // the checkbox's parent P is the one holding the item's id
    var item = checkbox.parentNode;
    while (item != null && item.tagName != "P") {
        item = item.parentNode;
    }
    if (item == null) {
        throw Error("Could not find P parent for checkbox:" + checkbox);
    }

    var item = checkbox.parentNode.parentNode;
    // break up the id of the clicked item into the tab's prefix and raw id
    if (item.id.search(/^[A-Z_]/) == -1) {
        // no prefix, the item clicked is on the search tab
        itemId = item.id;
        itemTabPrefix = "";
    } else {
        itemId = item.id.substr(1);
        itemTabPrefix = item.id.substr(0, 1);
    }

    // get sibling items in other static tabs in sync
    for (var i = 0; i < this.staticTabPrefixes.length; i++) {
        var prefix = this.staticTabPrefixes[i];
        if (prefix != itemTabPrefix) {
            var otherId = prefix+itemId;
            var otherItem = $(otherId);
            if (otherItem) {
                var otherCheckbox = otherItem.getElementsByTagName('INPUT')[0];
                otherCheckbox.checked = turningOn;
            }
        }
    }

    // get sibling item on search tab in sync
    if (itemTabPrefix != "") {
        var searchTabItem = $(itemId);
        if (searchTabItem) {
            var otherCheckbox = searchTabItem.getElementsByTagName('INPUT')[0];
            otherCheckbox.checked = turningOn;
        }
    }
    
    // add/remove from the list of selected items
    if (turningOn) {
        var newLabel = MTabbedPickerContext.getLabel(item);
        var newItem = this.makeNewItem(itemId, newLabel);
        this.selectedItems.push(newItem);
        this.selectedItems.sort(MTabbedPickerContext.itemComparator);
    } else {
        // items on the selected tab always have an id prefix of '_'
        var selectedItemId = '_' + itemId;
        for (var i = this.selectedItems.length-1; i >= 0; i--) {
            if (this.selectedItems[i].id == selectedItemId) {
                this.selectedItems.splice(i, 1);
                break;
            }
        }
    }

    $('pickerSelectedCount').innerHTML = '(' + this.selectedItems.length + ')';
    $('tooManyBrandsError').style.visibility = this.selectedItems.length > this.maxBrands ? 'visible' : 'hidden';
    this.selectedTabNeedsLayout = true;
}

MTabbedPickerContext.newItemTemplate = new Template('<p id="#{id}">' +
                                                '<label><input type="checkbox" onclick="BPCl(this)" checked="1"/>' +
                                                '#{label}</label></p>');

MTabbedPickerContext.prototype.makeNewItem = function(itemId, label)
{
    // Note the underbar we add to the ID, since this will be an item in the selected tab
    var itemCode = MTabbedPickerContext.newItemTemplate.evaluate({id: '_' + itemId,
                                                                  label: label });
    // HACK - we want to get the itemCode eval'ed into a real DOM node.  It seems like this requires it
    // to be at least momentarily inserted somewhere in the DOM.  We toss it momentarily into the container
    // of selected items, which turns out to never be visible when we're adding a new item.
    new Insertion.Bottom(this.selectedItemContainers[0], itemCode);
    var item = this.selectedItemContainers[0].lastChild;
    item.parentNode.removeChild(item);
    return item;
}

MTabbedPickerContext.ColumnLengthCutoffs = [0, 7, 12*2, 1000*1000*1000];

// Lays out the selected items into balanced columns within the selected items tab
MTabbedPickerContext.prototype.layoutSelectedItems = function()
{
    this.selectedTabNeedsLayout = false;

    // first empty the containers
    for (var i = this.selectedItemContainers.length-1; i >= 0; i--) {
        var container = this.selectedItemContainers[i];
        while (container.hasChildNodes()) {
            container.removeChild(container.firstChild);
        }
    }

    // Column filling code is a copy of the algorithm in FilterDialog.java
    var numColumns = 1;
    while (this.selectedItems.length > MTabbedPickerContext.ColumnLengthCutoffs[numColumns]) {
        numColumns++;
    }
    var columnLength = Math.floor((this.selectedItems.length-1) / numColumns) + 1;

    // put new nodes into columns
    var filterIndex = 0;
    for (var i = 0; i < numColumns; i++) {
        var container = this.selectedItemContainers[i];
        for (var j = 0; j < columnLength && filterIndex < this.selectedItems.length; j++) {
            var item = this.selectedItems[filterIndex++];
            container.appendChild(item);
        }
    }
}

// Dig the label out of an item, by hunting through its contained nodes
MTabbedPickerContext.getLabel = function(item)
{
    var label = item.label;       // try cached value
    if (label == null) {
        for (var node = item.firstChild.firstChild; node != null; node = node.nextSibling) {
            if (node.nodeName == "#text") {       // find the first text node
                label = node.nodeValue;
                break;
            }
        }
        item.label = label;
    }
    return label;
}

MTabbedPickerContext.itemComparator = function(i1, i2)
{
    var label1 = MTabbedPickerContext.getLabel(i1);
    var label2 = MTabbedPickerContext.getLabel(i2);
    if (label1 < label2) {
        return -1;
    } else if (label1 > label2) {
        return 1;
    } else {
        return 0;
    }
}

MTabbedPickerContext.nodeListToArray = function(nodeList)
{
    var length = nodeList.length;
    var results = new Array(length);
    for (var i = length-1; i >= 0; i--) {
        results[i] = nodeList.item(i);
    }
    return results;
}
