////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////

var __CFMENU_APPLY_IE_HACK = (cfBrowserGetName() == CFBROWSER_EXPLORER) &&
                             (cfBrowserGetVersion() < 7.0) &&
                             (cfBrowserGetOS() != CFBROWSER_OS_MAC);

var __CFMENU_ERROR_CONTAINER_ID = "invalid CFMenuContainer id";
var __CFMENU_ERROR_ID = "invalid CFMenu id";
var __CFMENU_ERROR_ITEM_ID = "invalid CFMenuItem id";
var __CFMENU_ERROR_SECTION_ID = "invalid CFMenuSection id";

var __CFMENU_HIDE_TIME = 300;
var __CFMENU_SHOW_TIME = 300;

var __CFMENU_SECTION_SUBMENU_OFFSET = -2;

////////////////////////////////////////////////////////////////////////////////
// Static Variables
////////////////////////////////////////////////////////////////////////////////

var __cfMenuContainerMap = {};
var __cfMenuItemMap = {};
var __cfMenuMap = {};
var __cfMenuSectionMap = {};

////////////////////////////////////////////////////////////////////////////////
// Classes
////////////////////////////////////////////////////////////////////////////////

// CFMenu

function CFMenu(id, sectionIds, itemIds)
{
    CFPopup.call(this, id);
    var items = new Array();
    for (var i = 0; i < itemIds.length; i++) {
        items.push(cfMenuItemGet(itemIds[i]));
    }
    var sections = new Array();
    for (var i = 0; i < sectionIds.length; i++) {
        sections.push(cfMenuSectionGet(sectionIds[i]));
    }
    this.__items = items;
    this.__sections = sections;
    if (__CFMENU_APPLY_IE_HACK) {
        this.__hackApplied = false;
    }
    __cfMenuMap[id] = this;
    var element = this.getElement();
    var f = cfEventHandlerCreate(this.__handleMouseOutEvent.bind(this));
    element.onmouseout = f;
    f = cfEventHandlerCreate(this.__handleMouseOverEvent.bind(this));
    element.onmouseover = f;
}

CFMenu.extendClasses(CFPopup);

CFMenu.prototype.__handleMouseOutEvent = function()
{
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OUT);
    return false;
}

CFMenu.prototype.__handleMouseOverEvent = function()
{
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OVER);
    return false;
}

CFMenu.prototype.__showIEHack = function()
{
    var result = CFPopup.prototype.__show.call(this);
    if (! this.__hackApplied) {
        var width = 0;
        var items = this.__items;
        for (var i = 0; i < items.length; i++) {
            width = Math.max(width, items[i].getDimensions().width);
        }
        var sections = this.__sections;
        for (var i = 0; i < sections.length; i++) {
            width = Math.max(width, sections[i].getDimensions().width);
        }
        width = width + "px";
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            item.getElement().style.width = width;
            var linkElement = item.getLinkElement();
            if (linkElement) {
                linkElement.style.width = width;
            }
        }
        for (var i = 0; i < sections.length; i++) {
            var section = sections[i];
            section.getElement().style.width = width;
            var linkElement = section.getLinkElement();
            if (linkElement) {
                linkElement.style.width = width;
            }
        }
        this.__hackApplied = true;
    }
    return result;
}

CFMenu.prototype.getHideTime = function()
{
    return __CFMENU_HIDE_TIME;
}

CFMenu.prototype.getItems = function()
{
    return this.__items;
}

CFMenu.prototype.getSections = function()
{
    return this.__sections;
}

CFMenu.prototype.getShowTime = function()
{
    return __CFMENU_SHOW_TIME;
}

CFMenu.prototype.refresh = function()
{
    CFPopup.prototype.refresh.call(this);
    var items = this.__items;
    for (var i = 0; i < items.length; i++) {
        items[i].refresh();
    }
    var sections = this.__sections;
    for (var i = 0; i < sections.length; i++) {
        sections[i].refresh();
    }
}

if (__CFMENU_APPLY_IE_HACK) {
    CFMenu.prototype.__show = CFMenu.prototype.__showIEHack;
}

// CFMenuContainer

function CFMenuContainer(id, rootMenuId)
{
    CFWidget.call(this, id);
    this.__rootMenu = cfElementGet(rootMenuId);
    __cfMenuContainerMap[id] = this;
}

CFMenuContainer.extendClasses(CFWidget);

// CFMenuItem

function CFMenuItem(id, linkId, hoverClass)
{
    CFWidget.call(this, id);
    this.__hoverClass = hoverClass;
    this.__linkElement = linkId.length ? cfElementGet(linkId) : undefined;
    __cfMenuItemMap[id] = this;
    var element = this.getElement();
    var f = cfEventHandlerCreate(this.__handleMouseOutEvent.bind(this));
    element.onmouseout = f;
    f = cfEventHandlerCreate(this.__handleMouseOverEvent.bind(this));
    element.onmouseover = f;
}

CFMenuItem.extendClasses(CFWidget);

CFMenuItem.prototype.__handleMouseOutEvent = function()
{
    cfElementDiscardClass(this.getElement(), this.__hoverClass);
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OUT);
    return false;
}

CFMenuItem.prototype.__handleMouseOverEvent = function()
{
    cfElementAddClass(this.getElement(), this.__hoverClass);
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OVER);
    return false;
}

CFMenuItem.prototype.getLinkElement = function()
{
    return this.__linkElement;
}

// CFMenuSection

function CFMenuSection(id, subMenuId, linkId, isVertical, hoverClass, showRight)
{
    CFWidget.call(this, id);
    var subMenu = subMenuId.length ? cfMenuGet(subMenuId) : undefined;
    this.__hoverClass = hoverClass;
    this.__isVertical = isVertical;
    this.__linkElement = linkId.length ? cfElementGet(linkId) : undefined;
    this.__mouseOverCount = 0;
    this.__showRight = showRight;
    this.__subMenu = subMenu;
    __cfMenuSectionMap[id] = this;
    var element = this.getElement();
    var f = cfEventHandlerCreate(this.__handleMouseOutEvent.bind(this));
    element.onmouseout = f;
    f = cfEventHandlerCreate(this.__handleMouseOverEvent.bind(this));
    element.onmouseover = f;
    if (subMenu) {
        var f = this.__handleChildSubMenuMouseOutEvent.bind(this);
        var outFunc = cfEventHandlerCreate(f);
        f = this.__handleChildSubMenuMouseOverEvent.bind(this);
        var overFunc = cfEventHandlerCreate(f);
        this.__addSubMenuEventHandlers(subMenu, overFunc, outFunc);
        f = cfEventHandlerCreate(this.__handleSubMenuShowEvent.bind(this));
        subMenu.addEventHandler(CFWIDGET_EVENT_SHOW, f);
    }
}

CFMenuSection.extendClasses(CFWidget);

CFMenuSection.prototype.__addSubMenuEventHandlers =
function(menu, overFunc, outFunc)
{
    menu.addEventHandler(CFWIDGET_EVENT_MOUSE_OUT, outFunc);
    menu.addEventHandler(CFWIDGET_EVENT_MOUSE_OVER, overFunc);
    var sections = menu.getSections();
    for (var i = 0; i < sections.length; i++) {
        var subMenu = sections[i].getSubMenu();
        if (subMenu) {
            this.__addSubMenuEventHandlers(subMenu, overFunc, outFunc);
        }
    }
}

CFMenuSection.prototype.__decrementMouseOverCount = function()
{
    var subMenu = this.__subMenu;
    if (subMenu) {
        var count = this.__mouseOverCount;
        if (count <= 1) {
            count = 1;
            subMenu.hide();
        }
        this.__mouseOverCount = count - 1;
    }
}

CFMenuSection.prototype.__handleChildSubMenuMouseOutEvent = function()
{
    this.__decrementMouseOverCount();
    return true;
}

CFMenuSection.prototype.__handleChildSubMenuMouseOverEvent = function()
{
    this.__incrementMouseOverCount();
    return true;
}

CFMenuSection.prototype.__handleMouseOutEvent = function()
{
    cfElementDiscardClass(this.getElement(), this.__hoverClass);
    this.__decrementMouseOverCount();
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OUT);
    return false;
}

CFMenuSection.prototype.__handleMouseOverEvent = function()
{
    cfElementAddClass(this.getElement(), this.__hoverClass);
    this.__incrementMouseOverCount();
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OVER);
    return false;
}

CFMenuSection.prototype.__handleSubMenuShowEvent = function()
{
    this.__refreshMenuPosition();
}

CFMenuSection.prototype.__incrementMouseOverCount = function()
{
    var subMenu = this.__subMenu;
    if (subMenu) {
        var count = this.__mouseOverCount;
        if (! count) {
            this.__refreshMenuPosition();
            subMenu.show();
        }
        this.__mouseOverCount = count + 1;
    }
}

CFMenuSection.prototype.__refreshMenuPosition = function()
{
    var dimensions = this.getDimensions();
    var position = this.getDocumentPosition();
    var left = position.x;
    var subMenu = this.__subMenu;
    var top = position.y;
    if (this.__showRight) {
        var menuDimensions = subMenu.getDimensions();
        if (this.__isVertical) {
            left = (left - menuDimensions.width) -
                   __CFMENU_SECTION_SUBMENU_OFFSET;
        } else {
            left = (left - menuDimensions.width) + dimensions.width;
            top = dimensions.height + __CFMENU_SECTION_SUBMENU_OFFSET;
        }
    } else {
        if (this.__isVertical) {
            left += dimensions.width + __CFMENU_SECTION_SUBMENU_OFFSET;
        } else {
            top += dimensions.height + __CFMENU_SECTION_SUBMENU_OFFSET;
        }
    }
    subMenu.setTopLeftPosition(top, left);
}

CFMenuSection.prototype.getLinkElement = function()
{
    return this.__linkElement;
}

CFMenuSection.prototype.getSubMenu = function()
{
    return this.__subMenu;
}

////////////////////////////////////////////////////////////////////////////////
// Public API
////////////////////////////////////////////////////////////////////////////////

function cfMenuContainerCreate(arg)
{
    return new CFMenuContainer(arg.id, arg.rootMenuId);
}

function cfMenuContainerGet(id)
{
    var container = __cfMenuContainerMap[id];
    if (! container) {
        return cfErrorTrigger("cfMenuContainerGet: '" + id + "': " +
                              __CFMENU_ERROR_CONTAINER_ID);
    }
    return container;
}

function cfMenuCreate(arg)
{
    return new CFMenu(arg.id, arg.sectionIds, arg.itemIds);
}

function cfMenuGet(id)
{
    var menu = __cfMenuMap[id];
    if (! menu) {
        return cfErrorTrigger("cfMenuGet: '" + id + "': " + __CFMENU_ERROR_ID);
    }
    return menu;
}

function cfMenuItemCreate(arg)
{
    return new CFMenuItem(arg.id, arg.linkId, arg.hoverClass);
}

function cfMenuItemGet(id)
{
    var item = __cfMenuItemMap[id];
    if (! item) {
        return cfErrorTrigger("cfMenuItemGet: '" + id + "': " +
                              __CFMENU_ERROR_ITEM_ID);
    }
    return item;
}

function cfMenuSectionCreate(arg)
{
    return new CFMenuSection(arg.id, arg.subMenuId, arg.linkId, arg.isVertical,
                             arg.hoverClass, arg.showRight);
}

function cfMenuSectionGet(id)
{
    var section = __cfMenuSectionMap[id];
    if (! section) {
        return cfErrorTrigger("cfMenuSectionGet: '" + id + "': " +
                              __CFMENU_ERROR_SECTION_ID);
    }
    return section;
}

