/*jslint browser: true, for: true, fudge: true, maxlen: 80, multivar: true,
         this: true */

/*global cfBrowserGetName, CFBROWSER_EXPLORER, cfBrowserGetVersion,
    cfBrowserGetOS, CFBROWSER_OS_MAC, CFPopup, cfEventHandlerCreate,
    CFWIDGET_EVENT_MOUSE_OUT, CFWIDGET_EVENT_MOUSE_OVER, CFWidget,
    cfElementGet, cfElementDiscardClass, cfElementAddClass,
    CFWIDGET_EVENT_SHOW, cfErrorTrigger */

/*property
    __addSubMenuEventHandlers, __decrementMouseOverCount, __hackApplied,
    __handleChildSubMenuMouseOutEvent, __handleChildSubMenuMouseOverEvent,
    __handleMouseOutEvent, __handleMouseOverEvent, __handleSubMenuShowEvent,
    __hoverClass, __incrementMouseOverCount, __isVertical, __items,
    __linkElement, __mouseOverCount, __refreshMenuPosition, __rootMenu,
    __sections, __show, __showIEHack, __showRight, __subMenu, __triggerEvent,
    addEventHandler, bind, call, extendClasses, getDimensions,
    getDocumentPosition, getElement, getHideTime, getItems, getLinkElement,
    getSections, getShowTime, getSubMenu, height, hide, hoverClass, id,
    isVertical, itemIds, length, linkId, max, onmouseout, onmouseover,
    prototype, push, refresh, rootMenuId, sectionIds, setTopLeftPosition, show,
    showRight, style, subMenuId, width, x, y
*/

////////////////////////////////////////////////////////////////////////////////
// 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 cfMenuSectionGet(id) {
    "use strict";
    var section = __cfMenuSectionMap[id];
    if (!section) {
        return cfErrorTrigger(
            "cfMenuSectionGet: '" + id + "': " + __CFMENU_ERROR_SECTION_ID
        );
    }
    return section;
}
function cfMenuItemGet(id) {
    "use strict";
    var item = __cfMenuItemMap[id];
    if (!item) {
        return cfErrorTrigger(
            "cfMenuItemGet: '" + id + "': " + __CFMENU_ERROR_ITEM_ID
        );
    }
    return item;
}

function CFMenu(id, sectionIds, itemIds, animate) {
    "use strict";
    CFPopup.call(this, id, animate);
    var items = [],
        i;
    for (i = 0; i < itemIds.length; i += 1) {
        items.push(cfMenuItemGet(itemIds[i]));
    }
    var sections = [];
    for (i = 0; i < sectionIds.length; i += 1) {
        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 () {
    "use strict";
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OUT);
    return false;
};

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

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

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

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

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

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

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

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

// CFMenuContainer

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

CFMenuContainer.extendClasses(CFWidget);

// CFMenuItem

function CFMenuItem(id, linkId, hoverClass) {
    "use strict";
    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 () {
    "use strict";
    cfElementDiscardClass(this.getElement(), this.__hoverClass);
    this.__triggerEvent(CFWIDGET_EVENT_MOUSE_OUT);
    return false;
};

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

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

// CFMenuSection

function CFMenuSection(
    id,
    subMenuId,
    linkId,
    isVertical,
    hoverClass,
    showRight
) {
    "use strict";
    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) {
        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
) {
    "use strict";
    var i, subMenu;
    menu.addEventHandler(CFWIDGET_EVENT_MOUSE_OUT, outFunc);
    menu.addEventHandler(CFWIDGET_EVENT_MOUSE_OVER, overFunc);
    var sections = menu.getSections();
    for (i = 0; i < sections.length; i += 1) {
        subMenu = sections[i].getSubMenu();
        if (subMenu) {
            this.__addSubMenuEventHandlers(subMenu, overFunc, outFunc);
        }
    }
};

CFMenuSection.prototype.__decrementMouseOverCount = function () {
    "use strict";
    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 () {
    "use strict";
    this.__decrementMouseOverCount();
    return true;
};

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

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

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

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

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

CFMenuSection.prototype.__refreshMenuPosition = function () {
    "use strict";
    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 () {
    "use strict";
    return this.__linkElement;
};

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

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

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

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

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

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

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

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