
#include "PlayerMenuServiceWin.h"

PLAYER_NS_BEGIN

PlayerMenuItemWin *PlayerMenuItemWin::create(const std::string &menuId, const std::string &title)
{
    PlayerMenuItemWin *item = new PlayerMenuItemWin();
    item->_menuId = menuId;
    item->_title = title;
    item->autorelease();
    return item;
}

PlayerMenuItemWin::PlayerMenuItemWin()
    : _parent(nullptr)
    , _commandId(0)
    , _hmenu(NULL)
    , _menubarEnabled(true)
{
}

PlayerMenuItemWin::~PlayerMenuItemWin()
{
    CC_SAFE_RELEASE(_parent);
    if (_hmenu)
    {
        CCLOG("PlayerMenuItemWin::~PlayerMenuItemWin() - %s (HMENU)", _menuId.c_str());
        DestroyMenu(_hmenu);
    }
    else
    {
        CCLOG("PlayerMenuItemWin::~PlayerMenuItemWin() - %s", _menuId.c_str());
    }
}

void PlayerMenuItemWin::setTitle(const std::string &title)
{
    if (title.length() == 0)
    {
        CCLOG("MenuServiceWin::setTitle() - can not set menu title to empty, menu id (%s)", _menuId.c_str());
        return;
    }

    MENUITEMINFO menuitem;
    menuitem.cbSize = sizeof(menuitem);
    menuitem.fMask = MIIM_FTYPE | MIIM_STRING;
    menuitem.fType = (title.compare("-") == 0) ? MFT_SEPARATOR : MFT_STRING;
    std::u16string u16title;
    cocos2d::StringUtils::UTF8ToUTF16(title, u16title);
    menuitem.dwTypeData = (LPTSTR)u16title.c_str();
    if (SetMenuItemInfo(_parent->_hmenu, _commandId, MF_BYCOMMAND, &menuitem))
    {
        _title = title;
    }
    else
    {
        DWORD err = GetLastError();
        CCLOG("MenuServiceWin::setTitle() - set menu title failed, menu id (%s). error code = %u", _menuId.c_str(), err);
    }
}

void PlayerMenuItemWin::setEnabled(bool enabled)
{
    MENUITEMINFO menuitem = {0};
    menuitem.cbSize = sizeof(menuitem);
    menuitem.fMask = MIIM_STATE;
    menuitem.fState = (enabled && _menubarEnabled) ? MFS_ENABLED : MFS_DISABLED;
    if (SetMenuItemInfo(_parent->_hmenu, _commandId, MF_BYCOMMAND, &menuitem))
    {
        _isEnabled = enabled;
    }
    else
    {
        DWORD err = GetLastError();
        CCLOG("MenuServiceWin::setEnabled() - set menu enabled failed, menu id (%s). error code = %u", _menuId.c_str(), err);
    }
}

void PlayerMenuItemWin::setChecked(bool checked)
{
    MENUITEMINFO menuitem;
    menuitem.cbSize = sizeof(menuitem);
    menuitem.fMask = MIIM_STATE;
    menuitem.fState = (checked) ? MFS_CHECKED : MFS_UNCHECKED;
    if (SetMenuItemInfo(_parent->_hmenu, _commandId, MF_BYCOMMAND, &menuitem))
    {
        _isChecked = checked;
    }
    else
    {
        DWORD err = GetLastError();
        CCLOG("MenuServiceWin::setChecked() - set menu checked failed, menu id (%s). error code = %u", _menuId.c_str(), err);
    }
}

void PlayerMenuItemWin::setShortcut(const std::string &shortcut)
{

}


// MenuServiceWin

WORD PlayerMenuServiceWin::_newCommandId = 0x1000;

PlayerMenuServiceWin::PlayerMenuServiceWin(HWND hwnd)
    : _hwnd(hwnd)
    , _menubarEnabled(true)
{
    // create menu
    _root._menuId = "__ROOT__";
    _root._commandId = 0;

    // hwnd has menu 
    HMENU menu = GetSystemMenu(hwnd, FALSE);// GetMenu(hwnd);
    if (menu)
    {
        _root._hmenu = menu;
    }
    else
    {
        _root._hmenu = CreateMenu();
        SetMenu(hwnd, _root._hmenu);
    }
}

PlayerMenuServiceWin::~PlayerMenuServiceWin()
{
}

PlayerMenuItem *PlayerMenuServiceWin::addItem(const std::string &menuId,
                                              const std::string &title,
                                              const std::string &parentId,
                                              int order /* = MAX_ORDER */)
{
    if (menuId.length() == 0 || title.length() == 0)
    {
        CCLOG("MenuServiceWin::addItem() - menuId and title must is non-empty");
        return nullptr;
    }

    // check menu id is exists
    if (_items.find(menuId) != _items.end())
    {
        CCLOG("MenuServiceWin::addItem() - menu id (%s) is exists", menuId.c_str());
        return false;
    }

    // set parent
    PlayerMenuItemWin *parent = &_root;
    if (parentId.length())
    {
        // query parent menu
        auto it = _items.find(parentId);
        if (it != _items.end())
        {
            parent = it->second;
        }
    }

    if (!parent->_hmenu)
    {
        // create menu handle for parent (convert parent to submenu)
        parent->_hmenu = CreateMenu();
        parent->_isGroup = true;
        MENUITEMINFO menuitem;
        menuitem.cbSize = sizeof(menuitem);
        menuitem.fMask = MIIM_SUBMENU;
        menuitem.hSubMenu = parent->_hmenu;
        if (!SetMenuItemInfo(parent->_parent->_hmenu, parent->_commandId, MF_BYCOMMAND, &menuitem))
        {
            DWORD err = GetLastError();
            CCLOG("MenuServiceWin::addItem() - set menu handle failed, menu id (%s). error code = %u", parent->_menuId.c_str(), err);
            return nullptr;
        }
    }

    // create new menu item
    _newCommandId++;
    PlayerMenuItemWin *item = PlayerMenuItemWin::create(menuId, title);
    item->_commandId = _newCommandId;
    item->_parent = parent;
    item->_parent->retain();

    // add menu item to menu bar
    MENUITEMINFO menuitem;
    menuitem.cbSize = sizeof(menuitem);
    menuitem.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
    menuitem.fType = (item->_title.compare("-") == 0) ? MFT_SEPARATOR : MFT_STRING;
    menuitem.fState = (item->_isEnabled) ? MFS_ENABLED : MFS_DISABLED;
    menuitem.fState |= (item->_isChecked) ? MFS_CHECKED : MFS_UNCHECKED;
    std::u16string u16title;
    cocos2d::StringUtils::UTF8ToUTF16(item->_title, u16title);
    menuitem.dwTypeData = (LPTSTR)u16title.c_str();
    menuitem.wID = _newCommandId;

    // check new menu item position
    if (order > parent->_children.size())
    {
        order = parent->_children.size();
    }
    else if (order < 0)
    {
        order = 0;
    }

    // create new menu item
    if (!InsertMenuItem(parent->_hmenu, order, TRUE, &menuitem))
    {
        DWORD err = GetLastError();
        CCLOG("MenuServiceWin::addItem() - insert new menu item failed, menu id (%s). error code = %u", item->_menuId.c_str(), err);
        item->release();
        return nullptr;
    }

    // update menu state
    parent->_children.insert(order, item);
    _items[item->_menuId] = item;
    _commandId2menuId[item->_commandId] = item->_menuId;
    updateChildrenOrder(parent);

    return item;
}

PlayerMenuItem *PlayerMenuServiceWin::addItem(const std::string &menuId,
                                              const std::string &title)
{
    return addItem(menuId, title, "");
}

PlayerMenuItem *PlayerMenuServiceWin::getItem(const std::string &menuId)
{
    auto it = _items.find(menuId);
    if (it == _items.end())
    {
        CCLOG("MenuServiceWin::getItem() - Invalid menu id (%s)", menuId.c_str());
        return nullptr;
    }

    return it->second;
}

bool PlayerMenuServiceWin::removeItem(const std::string &menuId)
{
    return removeItemInternal(menuId, true);
}

void PlayerMenuServiceWin::setMenuBarEnabled(bool enabled)
{
    _menubarEnabled = enabled;

    UINT state = enabled ? MFS_ENABLED : MFS_DISABLED;
    for (auto it = _root._children.begin(); it != _root._children.end(); ++it)
    {
        PlayerMenuItemWin *item = *it;
        MENUITEMINFO menuitem = {0};
        menuitem.cbSize = sizeof(menuitem);
        menuitem.fMask = MIIM_STATE;
        menuitem.fState = state;
        SetMenuItemInfo(item->_parent->_hmenu, item->_commandId, MF_BYCOMMAND, &menuitem);
        item->_menubarEnabled = enabled;
    }
}

PlayerMenuItemWin *PlayerMenuServiceWin::getItemByCommandId(WORD commandId)
{
    auto it = _commandId2menuId.find(commandId);
    if (it == _commandId2menuId.end()) return nullptr;
    return _items[it->second];
}

bool PlayerMenuServiceWin::removeItemInternal(const std::string &menuId, bool isUpdateChildrenOrder)
{
    auto it = _items.find(menuId);
    if (it == _items.end())
    {
        CCLOG("MenuServiceWin::removeItem() - Invalid menu id (%s)", menuId.c_str());
        return false;
    }

    PlayerMenuItemWin *item = it->second;
    if (item->_children.size() == 0)
    {
        if (!DeleteMenu(item->_parent->_hmenu, item->_commandId, MF_BYCOMMAND))
        {
            DWORD err = GetLastError();
            CCLOG("MenuServiceWin::removeItem() - remove menu item failed, menu id (%s). error code = %u", item->_menuId.c_str(), err);
            return false;
        }

        // remove item from parent
        bool removed = false;
        auto *children = &item->_parent->_children;
        for (auto it = children->begin(); it != children->end(); ++it)
        {
            if ((*it)->_commandId == item->_commandId)
            {
                CCLOG("MenuServiceWin::removeItem() - remove menu item (%s)", item->_menuId.c_str());
                children->erase(it);
                removed = true;
                break;
            }
        }

        if (!removed)
        {
            CCLOG("MenuServiceWin::removeItem() - remove menu item (%s) failed, not found command id from parent->children", item->_menuId.c_str());
        }

        // remove menu id mapping
        _items.erase(menuId);
        _commandId2menuId.erase(item->_commandId);
        if (isUpdateChildrenOrder)
        {
            updateChildrenOrder(item->_parent);
        }
        DrawMenuBar(_hwnd);
        return true;
    }
    else
    {
        // remove all children
        while (item->_children.size() != 0)
        {
            PlayerMenuItemWin *child = *item->_children.begin();
            if (!removeItemInternal(child->_menuId.c_str(), false))
            {
                break;
                return false;
            }
        }
        return removeItemInternal(menuId, true);
    }

    return false;
}


void PlayerMenuServiceWin::updateChildrenOrder(PlayerMenuItemWin *parent)
{
    auto *children = &parent->_children;
    int order = 0;
    for (auto it = children->begin(); it != children->end(); ++it)
    {
        (*it)->_order = order;
        order++;
    }
}

PLAYER_NS_END
