/*
 *  This file is part of Poedit (https://poedit.com)
 *
 *  Copyright (C) 1999-2026 Vaclav Slavik
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "menus.h"

#include "app_updates.h"
#include "recent_files.h"
#include "str_helpers.h"

#include <wx/xrc/xmlres.h>

#ifdef __WXOSX__
    #include <wx/private/bmpbndl.h>
#endif


#ifdef __WXOSX__

// TODO: move this to app delegate, once we have it (proxying to wx's one)
@interface POMenuActions : NSObject
@end

@implementation POMenuActions

- (void)showWelcomeWindow: (id)sender
{
    #pragma unused(sender)
    wxCommandEvent event(wxEVT_MENU, XRCID("menu_welcome"));
    wxTheApp->ProcessEvent(event);
}

@end

struct MenusManager::NativeMacData
{
    NSMenu *windowMenu = nullptr;
    NSMenuItem *windowMenuItem = nullptr;
    wxMenuBar *menuBar = nullptr;
    POMenuActions *actions = nullptr;
};
#endif

MenusManager::MenusManager()
{
#ifdef __WXOSX__
    m_nativeMacData.reset(new NativeMacData);
    m_nativeMacData->actions = [POMenuActions new];
    wxMenuBar::SetAutoWindowMenu(false);
#endif
}

MenusManager::~MenusManager()
{
}


wxMenuBar *MenusManager::CreateMenu(Menu purpose)
{
    wxMenuBar *bar = nullptr;

    switch (purpose)
    {
        case Menu::Global:
#ifdef __WXOSX__
            bar = wxXmlResource::Get()->LoadMenuBar("mainmenu_global");
#endif
            break;
        case Menu::WelcomeWindow:
#ifndef __WXOSX__
            bar = wxXmlResource::Get()->LoadMenuBar("mainmenu_global");
#endif
            break;
        case Menu::Editor:
            bar = wxXmlResource::Get()->LoadMenuBar("mainmenu");
            break;
    }

    wxASSERT( bar );

    RecentFiles::Get().UseMenu(bar->FindItem(XRCID("open_recent")));

#ifdef __WXOSX__
    TweakOSXMenuBar(bar);

    if (purpose == Menu::Global)
        wxMenuBar::MacSetCommonMenuBar(bar);
#endif

#ifndef HAVE_HTTP_CLIENT
    wxMenu *menu;
    wxMenuItem *item;
    item = bar->FindItem(XRCID("menu_cloud_sync"), &menu);
    if (item)
        menu->Destroy(item);
    item = bar->FindItem(XRCID("menu_open_cloud"), &menu);
    if (item)
        menu->Destroy(item);
#endif

    return bar;
}


#ifdef __WXOSX__

namespace
{

NSMenuItem *AddNativeItem(NSMenu *menu, int pos, const wxString& text, SEL ac, NSString *key)
{
    NSString *str = str::to_NS(text);
    if (pos == -1)
        return [menu addItemWithTitle:str action:ac keyEquivalent:key];
    else
        return [menu insertItemWithTitle:str action:ac keyEquivalent:key atIndex:pos];
}

} // anonymous namespace


void SetMacMenuIcon(wxMenuItem *item, const char *symbol)
{
    if (@available(macOS 26, *))
    {
        NSString *name = str::to_NS(symbol);
        NSImage *image = [NSImage imageWithSystemSymbolName:name accessibilityDescription:nil];
        if (!image)
            image = [NSImage imageNamed:name];
        if (image)
        {
            auto bmp = wxOSXMakeBundleFromImage(image);
            if (bmp.IsOk())
                item->SetBitmap(bmp);
        }
    }
}


void MenusManager::TweakOSXMenuBar(wxMenuBar *bar)
{
    wxMenu *apple = bar->OSXGetAppleMenu();
    if (apple)
    {
        apple->Insert(1, XRCID("menu_check_for_updates"), _(L"Check for Updates…"));

        apple->Insert(4, XRCID("menu_manager"), _("Catalogs Manager"));
        apple->InsertSeparator(4);
    }

    if (@available(macOS 13.0, *))
    {
        // Correct label is already used
    }
    else
    {
        wxMenuItem *prefsItem = bar->FindItem(wxID_PREFERENCES);
        if (prefsItem)
        {
            prefsItem->SetItemLabel(_(L"&Preferences…") + "\tCtrl+,");
            SetMacMenuIcon(prefsItem, "gear");
        }
    }

    wxMenu *fileMenu = nullptr;
    wxMenuItem *fileCloseItem = bar->FindItem(wxID_CLOSE, &fileMenu);
    if (fileMenu && fileCloseItem)
    {
        NSMenuItem *nativeCloseItem = [fileMenu->GetHMenu() itemWithTitle:str::to_NS(fileCloseItem->GetItemLabelText())];
        if (nativeCloseItem)
        {
            nativeCloseItem.target = nil;
            nativeCloseItem.action = @selector(performClose:);
        }
    }

    int editMenuPos = bar->FindMenu(_("&Edit"));
    if (editMenuPos == wxNOT_FOUND)
        editMenuPos = 1;
    wxMenu *edit = bar->GetMenu(editMenuPos);
    int pasteItem = -1;
    int findItem = -1;
    int pos = 0;
    for (auto& i : edit->GetMenuItems())
    {
        if (i->GetId() == wxID_PASTE)
            pasteItem = pos;
        else if (i->GetId() == XRCID("menu_sub_find"))
            findItem = pos;
        pos++;
    }

    NSMenu *editNS = edit->GetHMenu();

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wundeclared-selector"
    AddNativeItem(editNS, 0, _("Undo"), @selector(undo:), @"z");
    AddNativeItem(editNS, 1, _("Redo"), @selector(redo:), @"Z");
    #pragma clang diagnostic pop
    [editNS insertItem:[NSMenuItem separatorItem] atIndex:2];
    if (pasteItem != -1) pasteItem += 3;
    if (findItem != -1) findItem += 3;

    NSMenuItem *item;
    if (pasteItem != -1)
    {
        item = AddNativeItem(editNS, pasteItem+1, _("Paste and Match Style"),
                             @selector(pasteAsPlainText:), @"V");
        [item setKeyEquivalentModifierMask:NSEventModifierFlagCommand | NSEventModifierFlagOption];
        item = AddNativeItem(editNS, pasteItem+2, _("Delete"),
                             @selector(delete:), @"");
        [item setKeyEquivalentModifierMask:NSEventModifierFlagCommand];
        if (findItem != -1) findItem += 2;
    }

    #define FIND_PLUS(ofset) ((findItem != -1) ? (findItem+ofset) : -1)
    if (findItem == -1)
        [editNS addItem:[NSMenuItem separatorItem]];
    item = AddNativeItem(editNS, FIND_PLUS(1), _("Spelling and Grammar"), NULL, @"");
    NSMenu *spelling = [[NSMenu alloc] initWithTitle:@"Spelling and Grammar"];
    AddNativeItem(spelling, -1, _("Show Spelling and Grammar"), @selector(showGuessPanel:), @":");
    AddNativeItem(spelling, -1, _("Check Document Now"), @selector(checkSpelling:), @";");
    [spelling addItem:[NSMenuItem separatorItem]];
    AddNativeItem(spelling, -1, _("Check Spelling While Typing"), @selector(toggleContinuousSpellChecking:), @"");
    AddNativeItem(spelling, -1, _("Check Grammar With Spelling"), @selector(toggleGrammarChecking:), @"");
    AddNativeItem(spelling, -1, _("Correct Spelling Automatically"), @selector(toggleAutomaticSpellingCorrection:), @"");
    [editNS setSubmenu:spelling forItem:item];

    item = AddNativeItem(editNS, FIND_PLUS(2), _("Substitutions"), NULL, @"");
    NSMenu *subst = [[NSMenu alloc] initWithTitle:@"Substitutions"];
    AddNativeItem(subst, -1, _("Show Substitutions"), @selector(orderFrontSubstitutionsPanel:), @"");
    [subst addItem:[NSMenuItem separatorItem]];
    AddNativeItem(subst, -1, _("Smart Copy/Paste"), @selector(toggleSmartInsertDelete:), @"");
    AddNativeItem(subst, -1, _("Smart Quotes"), @selector(toggleAutomaticQuoteSubstitution:), @"");
    AddNativeItem(subst, -1, _("Smart Dashes"), @selector(toggleAutomaticDashSubstitution:), @"");
    AddNativeItem(subst, -1, _("Smart Links"), @selector(toggleAutomaticLinkDetection:), @"");
    AddNativeItem(subst, -1, _("Text Replacement"), @selector(toggleAutomaticTextReplacement:), @"");
    [editNS setSubmenu:subst forItem:item];

    item = AddNativeItem(editNS, FIND_PLUS(3), _("Transformations"), NULL, @"");
    NSMenu *trans = [[NSMenu alloc] initWithTitle:@"Transformations"];
    AddNativeItem(trans, -1, _("Make Upper Case"), @selector(uppercaseWord:), @"");
    AddNativeItem(trans, -1, _("Make Lower Case"), @selector(lowercaseWord:), @"");
    AddNativeItem(trans, -1, _("Capitalize"), @selector(capitalizeWord:), @"");
    [editNS setSubmenu:trans forItem:item];

    item = AddNativeItem(editNS, FIND_PLUS(4), _("Speech"), NULL, @"");
    NSMenu *speech = [[NSMenu alloc] initWithTitle:@"Speech"];
    AddNativeItem(speech, -1, _("Start Speaking"), @selector(startSpeaking:), @"");
    AddNativeItem(speech, -1, _("Stop Speaking"), @selector(stopSpeaking:), @"");
    [editNS setSubmenu:speech forItem:item];

    int viewMenuPos = bar->FindMenu(_("&View"));
    if (viewMenuPos != wxNOT_FOUND)
    {
        NSMenu *viewNS = bar->GetMenu(viewMenuPos)->GetHMenu();
        // TRANSLATORS: This must be the same as OS X's translation of this View menu item
        item = AddNativeItem(viewNS, -1, _("Show Toolbar"), @selector(toggleToolbarShown:), @"t");
        [item setKeyEquivalentModifierMask:NSEventModifierFlagCommand | NSEventModifierFlagOption];
        // TRANSLATORS: This must be the same as OS X's translation of this View menu item
        AddNativeItem(viewNS, -1, _(L"Customize Toolbar…"), @selector(runToolbarCustomizationPalette:), @"");
        [viewNS addItem:[NSMenuItem separatorItem]];
        // TRANSLATORS: This must be the same as OS X's translation of this View menu item
        item = AddNativeItem(viewNS, -1, _("Enter Full Screen"), @selector(toggleFullScreen:), @"f");
        [item setKeyEquivalentModifierMask:NSEventModifierFlagCommand | NSEventModifierFlagControl];

    }

    if (!m_nativeMacData->windowMenu)
    {
        NSMenu *windowMenu = [[NSMenu alloc] initWithTitle:str::to_NS(_("Window"))];
        AddNativeItem(windowMenu, -1, _("Minimize"), @selector(performMiniaturize:), @"m");
        AddNativeItem(windowMenu, -1, _("Zoom"), @selector(performZoom:), @"");
        [windowMenu addItem:[NSMenuItem separatorItem]];
        item = AddNativeItem(windowMenu, -1, _("Welcome to Poedit"), @selector(showWelcomeWindow:), @"1");
        item.target = m_nativeMacData->actions;
        [item setKeyEquivalentModifierMask: NSEventModifierFlagShift | NSEventModifierFlagCommand];

        [windowMenu addItem:[NSMenuItem separatorItem]];
        AddNativeItem(windowMenu, -1, _("Bring All to Front"), @selector(arrangeInFront:), @"");
        [NSApp setWindowsMenu:windowMenu];
        m_nativeMacData->windowMenu = windowMenu;
    }
    
    if (@available(macOS 26.0, *))
    {
        SetMacMenuIcon(bar, XRCID("show_sidebar"), "poedit.sidebar");
        SetMacMenuIcon(bar, XRCID("menu_validate"), "poedit.validate");
        SetMacMenuIcon(bar, XRCID("menu_pretranslate"), "poedit.pretranslate");
        SetMacMenuIcon(bar, XRCID("menu_update_from_src"), "poedit.update");
        SetMacMenuIcon(bar, XRCID("menu_cloud_sync"), "poedit.sync");
        SetMacMenuIcon(bar, wxID_NEW, "plus");
        SetMacMenuIcon(bar, wxID_OPEN, "arrow.up.right");
        SetMacMenuIcon(bar, XRCID("open_recent"), "clock");
        SetMacMenuIcon(bar, XRCID("menu_open_cloud"), "cloud");
        SetMacMenuIcon(bar, wxID_SAVE, "square.and.arrow.down");
        SetMacMenuIcon(bar, XRCID("menu_compile_mo"), "hammer");
        SetMacMenuIcon(bar, XRCID("menu_export_html"), "arrow.up.document");

        SetMacMenuIcon(bar, XRCID("menu_clear"), "delete.backward");
        SetMacMenuIcon(bar, XRCID("menu_copy_from_src"), "document.on.document");
        SetMacMenuIcon(bar, XRCID("menu_fuzzy"), "exclamationmark.circle");
        SetMacMenuIcon(bar, XRCID("menu_comment"), "bubble");
        SetMacMenuIcon(bar, XRCID("menu_suggestions"), "list.dash");
        
        SetMacMenuIcon(bar, XRCID("menu_ids"), "textformat.numbers");
        SetMacMenuIcon(bar, XRCID("menu_warnings"), "exclamationmark.triangle");
        
        SetMacMenuIcon(bar, XRCID("sort_by_order"), "list.number");
        SetMacMenuIcon(bar, XRCID("sort_by_source"), "text.alignleft");
        SetMacMenuIcon(bar, XRCID("sort_by_translation"), "text.alignright");

        SetMacMenuIcon(bar, XRCID("sort_group_by_context"), "rectangle.stack");
        SetMacMenuIcon(bar, XRCID("sort_errors_first"), "exclamationmark.octagon");
        SetMacMenuIcon(bar, XRCID("sort_untrans_first"), "square.dashed");
        
        SetMacMenuIcon(bar, XRCID("show_suggestions"), "list.dash");
        SetMacMenuIcon(bar, XRCID("menu_references"), "curlybraces");

        SetMacMenuIcon(bar, XRCID("menu_source_text"), "globe");
        SetMacMenuIcon(bar, XRCID("menu_remove_same_as_source"), "trash");
        SetMacMenuIcon(bar, XRCID("menu_catproperties"), "info.circle.text.page");

        SetMacMenuIcon(bar, XRCID("go_done_and_next"), "return");
        SetMacMenuIcon(bar, XRCID("go_previously_edited"), "arrow.uturn.up");
        SetMacMenuIcon(bar, XRCID("go_prev"), "chevron.up");
        SetMacMenuIcon(bar, XRCID("go_next"), "chevron.down");
        SetMacMenuIcon(bar, XRCID("go_prev_unfinished"), "chevron.up.circle");
        SetMacMenuIcon(bar, XRCID("go_next_unfinished"), "chevron.down.circle");
        SetMacMenuIcon(bar, XRCID("go_prev_pluralform"), "chevron.compact.backward");
        SetMacMenuIcon(bar, XRCID("go_next_pluralform"), "chevron.compact.forward");

        SetMacMenuIcon(bar, wxID_HELP, "safari");
        SetMacMenuIcon(bar, XRCID("menu_support_web"), "safari");
        SetMacMenuIcon(bar, XRCID("menu_support_email"), "envelope");

        SetMacMenuIcon(apple, wxID_ABOUT, "info.circle");
        SetMacMenuIcon(apple, wxID_PREFERENCES, "gear");
        SetMacMenuIcon(apple, XRCID("menu_manager"), "archivebox");
    }
}

void MenusManager::FixupMenusForMacIfNeeded()
{
    auto installed = wxMenuBar::MacGetInstalledMenuBar();
    if (m_nativeMacData->menuBar == installed)
        return;  // nothing to do

    m_nativeMacData->menuBar = nullptr;

    RecentFiles::Get().MacTransferMenuTo(installed);

    if (m_nativeMacData->windowMenuItem)
        [m_nativeMacData->windowMenuItem setSubmenu:nil];

    if (!installed)
        return;

    NSMenuItem *windowItem = [[NSApp mainMenu] itemWithTitle:str::to_NS(_("Window"))];
    if (windowItem)
    {
        [windowItem setSubmenu:m_nativeMacData->windowMenu];
        m_nativeMacData->windowMenuItem = windowItem;
    }

    m_nativeMacData->menuBar = installed;
}

#endif // __WXOSX__


void AppendMenuSectionHeader(wxMenu *menu, const wxString& title)
{
#ifdef __WXOSX__
    if (@available(macOS 14.0, *))
    {
        NSMenu *native = menu->GetHMenu();
        NSMenuItem *header = [NSMenuItem sectionHeaderWithTitle:str::to_NS(title)];
        [native addItem:header];
    }
    else
#endif // __WXOSX__
    {
        wxMenuItem *item = new wxMenuItem(menu, wxID_ANY, title);
        menu->Append(item);
        item->Enable(false);
    }
}
