// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/keyboard/ui_bundled/UIKeyCommand+Chrome.h"
#import "ios/chrome/browser/keyboard/ui_bundled/key_command_actions.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"

namespace {

UIKeyModifierFlags None = 0;
UIKeyModifierFlags Command = UIKeyModifierCommand;
UIKeyModifierFlags Control = UIKeyModifierControl;
UIKeyModifierFlags AltCommand = UIKeyModifierAlternate | UIKeyModifierCommand;
UIKeyModifierFlags ShiftCommand = UIKeyModifierShift | UIKeyModifierCommand;
UIKeyModifierFlags AltShiftCommand =
    UIKeyModifierAlternate | UIKeyModifierShift | UIKeyModifierCommand;
UIKeyModifierFlags ControlShift = UIKeyModifierControl | UIKeyModifierShift;

}  // namespace

const char kMobileKeyCommandClose[] = "MobileKeyCommandClose";

@implementation UIKeyCommand (Chrome)

#pragma mark - Specific Keyboard Commands

+ (UIKeyCommand*)cr_openNewTab {
  UIImage* image = SymbolWithConfiguration(SymbolPlusInCircle, nil);
  return [self cr_commandWithInput:@"t"
                     modifierFlags:Command
                            action:@selector(keyCommand_openNewTab)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_TAB"
                             image:image];
}

+ (UIKeyCommand*)cr_openNewRegularTab {
  return [self keyCommandWithInput:@"n"
                     modifierFlags:Command
                            action:@selector(keyCommand_openNewRegularTab)];
}

+ (UIKeyCommand*)cr_openNewIncognitoTab {
  UIImage* image = SymbolWithConfiguration(SymbolIncognito, nil);
  return [self cr_commandWithInput:@"n"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_openNewIncognitoTab)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_INCOGNITO_TAB"
                             image:image];
}

+ (UIKeyCommand*)cr_openNewWindow {
  UIImage* image = SymbolWithConfiguration(SymbolPlusRectangle, nil);
  return [self cr_commandWithInput:@"n"
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_openNewWindow)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_WINDOW"
                             image:image];
}

+ (UIKeyCommand*)cr_openNewIncognitoWindow {
  UIImage* image = SymbolWithConfiguration(SymbolIncognitoRectangle, nil);
  return [self cr_commandWithInput:@"n"
                     modifierFlags:AltShiftCommand
                            action:@selector(keyCommand_openNewIncognitoWindow)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_INCOGNITO_WINDOW"
                             image:image];
}

+ (UIKeyCommand*)cr_reopenLastClosedTab {
  UIImage* image = SymbolWithConfiguration(SymbolArrowUTurnForward, nil);
  return [self cr_commandWithInput:@"t"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_reopenLastClosedTab)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_REOPEN_CLOSED_TAB"
                             image:image];
}

+ (UIKeyCommand*)cr_find {
  return [self cr_commandWithInput:@"f"
                     modifierFlags:Command
                            action:@selector(keyCommand_find)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_FIND"];
}

+ (UIKeyCommand*)cr_findNext {
  return [self cr_commandWithInput:@"g"
                     modifierFlags:Command
                            action:@selector(keyCommand_findNext)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_FIND_NEXT"];
}

+ (UIKeyCommand*)cr_findPrevious {
  return [self cr_commandWithInput:@"g"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_findPrevious)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_FIND_PREVIOUS"];
}

+ (UIKeyCommand*)cr_openLocation {
  UIImage* image = SymbolWithConfiguration(SymbolGlobe, nil);
  return [self cr_commandWithInput:@"l"
                     modifierFlags:Command
                            action:@selector(keyCommand_openLocation)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_OPEN_LOCATION"
                             image:image];
}

+ (UIKeyCommand*)cr_closeTab {
  UIImage* image = SymbolWithConfiguration(SymbolXMarkSquare, nil);
  return [self cr_commandWithInput:@"w"
                     modifierFlags:Command
                            action:@selector(keyCommand_closeTab)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_CLOSE_TAB"
                             image:image];
}

+ (UIKeyCommand*)cr_showNextTab {
  UIImage* image = SymbolWithConfiguration(SymbolArrowRightSquare, nil);
  UIKeyCommand* keyCommand =
      [self cr_commandWithInput:@"\t"
                  modifierFlags:Control
                         action:@selector(keyCommand_showNextTab)
                titleIDAsString:@"IDS_IOS_KEYBOARD_NEXT_TAB"
                          image:image];
  keyCommand.wantsPriorityOverSystemBehavior = YES;
  return keyCommand;
}

+ (UIKeyCommand*)cr_showPreviousTab {
  UIImage* image = SymbolWithConfiguration(SymbolArrowLeftSquare, nil);
  UIKeyCommand* keyCommand =
      [self cr_commandWithInput:@"\t"
                  modifierFlags:ControlShift
                         action:@selector(keyCommand_showPreviousTab)
                titleIDAsString:@"IDS_IOS_KEYBOARD_PREVIOUS_TAB"
                          image:image];
  keyCommand.wantsPriorityOverSystemBehavior = YES;
  return keyCommand;
}

+ (UIKeyCommand*)cr_showNextTab_2 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of braces.
  return [self keyCommandWithInput:@"}"
                     modifierFlags:Command
                            action:@selector(keyCommand_showNextTab)];
}

+ (UIKeyCommand*)cr_showPreviousTab_2 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of braces.
  return [self keyCommandWithInput:@"{"
                     modifierFlags:Command
                            action:@selector(keyCommand_showPreviousTab)];
}

+ (UIKeyCommand*)cr_showNextTab_3 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of arrows.
  return [self keyCommandWithInput:UIKeyInputRightArrow
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showNextTab)];
}

+ (UIKeyCommand*)cr_showPreviousTab_3 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of arrows.
  return [self keyCommandWithInput:UIKeyInputLeftArrow
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showPreviousTab)];
}

+ (UIKeyCommand*)cr_showBookmarks {
  UIImage* image = SymbolWithConfiguration(SymbolBookmarks, nil);
  return [self cr_commandWithInput:@"b"
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showBookmarks)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_BOOKMARKS"
                             image:image];
}

+ (UIKeyCommand*)cr_addToBookmarks {
  UIImage* image = SymbolWithConfiguration(SymbolStarLeadingHalfFilled, nil);
  return [self cr_commandWithInput:@"d"
                     modifierFlags:Command
                            action:@selector(keyCommand_addToBookmarks)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_ADD_TO_BOOKMARKS"
                             image:image];
}

+ (UIKeyCommand*)cr_reload {
  UIImage* image = SymbolWithConfiguration(SymbolArrowClockWise, nil);
  return [self cr_commandWithInput:@"r"
                     modifierFlags:Command
                            action:@selector(keyCommand_reload)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_RELOAD"
                             image:image];
}

+ (UIKeyCommand*)cr_back {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of brackets.
  UIImage* image = SymbolWithConfiguration(SymbolArrowLeft, nil);
  return [self cr_commandWithInput:@"["
                     modifierFlags:Command
                            action:@selector(keyCommand_back)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_HISTORY_BACK"
                             image:image];
}

+ (UIKeyCommand*)cr_forward {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true
  // by default. It handles flipping the direction of brackets.
  UIImage* image = SymbolWithConfiguration(SymbolArrowRight, nil);
  return [self cr_commandWithInput:@"]"
                     modifierFlags:Command
                            action:@selector(keyCommand_forward)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_HISTORY_FORWARD"
                             image:image];
}

+ (UIKeyCommand*)cr_back_2 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is
  // true by default. It handles flipping the direction of arrows.
  return [self keyCommandWithInput:UIKeyInputLeftArrow
                     modifierFlags:Command
                            action:@selector(keyCommand_back)];
}

+ (UIKeyCommand*)cr_forward_2 {
  // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is
  // true by default. It handles flipping the direction of arrows.
  return [self keyCommandWithInput:UIKeyInputRightArrow
                     modifierFlags:Command
                            action:@selector(keyCommand_forward)];
}

+ (UIKeyCommand*)cr_showHistory {
  UIImage* image = SymbolWithConfiguration(
      SymbolClockArrowTriangleheadCounterclockwiseRotate90, nil);
  return [self cr_commandWithInput:@"y"
                     modifierFlags:Command
                            action:@selector(keyCommand_showHistory)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_HISTORY"
                             image:image];
}

+ (UIKeyCommand*)cr_voiceSearch {
  UIImage* image = SymbolWithConfiguration(SymbolMicrophone, nil);
  return [self cr_commandWithInput:@"."
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_voiceSearch)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_VOICE_SEARCH"
                             image:image];
}

+ (UIKeyCommand*)cr_close {
  return [self keyCommandWithInput:UIKeyInputEscape
                     modifierFlags:None
                            action:@selector(keyCommand_close)];
}

+ (UIKeyCommand*)cr_showSettings {
  NSString* titleID = @"IDS_IOS_KEYBOARD_SHOW_SETTINGS";
  if (@available(iOS 26, *)) {
    titleID = @"IDS_IOS_KEYBOARD_SETTINGS";
  }
  UIImage* image = SymbolWithConfiguration(SymbolSettings, nil);
  return [self cr_commandWithInput:@","
                     modifierFlags:Command
                            action:@selector(keyCommand_showSettings)
                   titleIDAsString:titleID
                             image:image];
}

+ (UIKeyCommand*)cr_stop {
  UIImage* image = SymbolWithConfiguration(SymbolXMark, nil);
  return [self cr_commandWithInput:@"."
                     modifierFlags:Command
                            action:@selector(keyCommand_stop)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_STOP"
                             image:image];
}

+ (UIKeyCommand*)cr_showHelp {
  UIImage* image = SymbolWithConfiguration(SymbolHelp, nil);
  return [self cr_commandWithInput:@"?"
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showHelp)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_HELP"
                             image:image];
}

+ (UIKeyCommand*)cr_showDownloads {
  UIImage* image = SymbolWithConfiguration(SymbolDownload, nil);
  return [self cr_commandWithInput:@"l"
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showDownloads)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_DOWNLOADS"
                             image:image];
}

+ (UIKeyCommand*)cr_showDownloads_2 {
  return [self keyCommandWithInput:@"j"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_showDownloads)];
}

+ (UIKeyCommand*)cr_select1 {
  UIImage* image = SymbolWithConfiguration(SymbolArrowLeftToLineSquare, nil);
  UIKeyCommand* keyCommand =
      [self cr_commandWithInput:@"1"
                  modifierFlags:Command
                         action:@selector(keyCommand_select1)
                titleIDAsString:@"IDS_IOS_KEYBOARD_FIRST_TAB"
                          image:image];
  keyCommand.allowsAutomaticLocalization = NO;
  return keyCommand;
}

+ (UIKeyCommand*)cr_select2 {
  UIKeyCommand* keyCommand =
      [self keyCommandWithInput:@"2"
                  modifierFlags:Command
                         action:@selector(keyCommand_select2)];
  keyCommand.allowsAutomaticLocalization = NO;
  return keyCommand;
}

+ (UIKeyCommand*)cr_select3 {
  UIKeyCommand* keyCommand =
      [self keyCommandWithInput:@"3"
                  modifierFlags:Command
                         action:@selector(keyCommand_select3)];
  keyCommand.allowsAutomaticLocalization = NO;
  return keyCommand;
}

+ (UIKeyCommand*)cr_select4 {
  return [self keyCommandWithInput:@"4"
                     modifierFlags:Command
                            action:@selector(keyCommand_select4)];
}

+ (UIKeyCommand*)cr_select5 {
  return [self keyCommandWithInput:@"5"
                     modifierFlags:Command
                            action:@selector(keyCommand_select5)];
}

+ (UIKeyCommand*)cr_select6 {
  return [self keyCommandWithInput:@"6"
                     modifierFlags:Command
                            action:@selector(keyCommand_select6)];
}

+ (UIKeyCommand*)cr_select7 {
  return [self keyCommandWithInput:@"7"
                     modifierFlags:Command
                            action:@selector(keyCommand_select7)];
}

+ (UIKeyCommand*)cr_select8 {
  return [self keyCommandWithInput:@"8"
                     modifierFlags:Command
                            action:@selector(keyCommand_select8)];
}

+ (UIKeyCommand*)cr_select9 {
  UIImage* image = SymbolWithConfiguration(SymbolArrowRightToLineSquare, nil);
  return [self cr_commandWithInput:@"9"
                     modifierFlags:Command
                            action:@selector(keyCommand_select9)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_LAST_TAB"
                             image:image];
}

+ (UIKeyCommand*)cr_reportAnIssue {
  UIImage* image = SymbolWithConfiguration(SymbolExclamationMarkBubble, nil);
  return [self cr_commandWithInput:@"i"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_reportAnIssue)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_REPORT_AN_ISSUE"
                             image:image];
}

+ (UIKeyCommand*)cr_reportAnIssue_2 {
  return [self keyCommandWithInput:@"i"
                     modifierFlags:AltShiftCommand
                            action:@selector(keyCommand_reportAnIssue)];
}

+ (UIKeyCommand*)cr_addToReadingList {
  UIImage* image = SymbolWithConfiguration(SymbolReadLaterAction, nil);
  return [self cr_commandWithInput:@"d"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_addToReadingList)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_ADD_TO_READING_LIST"
                             image:image];
}

+ (UIKeyCommand*)cr_showReadingList {
  UIImage* image = SymbolWithConfiguration(SymbolReadingList, nil);
  return [self cr_commandWithInput:@"r"
                     modifierFlags:AltCommand
                            action:@selector(keyCommand_showReadingList)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_READING_LIST"
                             image:image];
}

+ (UIKeyCommand*)cr_goToTabGrid {
  UIImage* image = SymbolWithConfiguration(SymbolTabs, nil);
  return [self cr_commandWithInput:@"\\"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_goToTabGrid)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_GO_TO_TAB_GRID"
                             image:image];
}

+ (UIKeyCommand*)cr_clearBrowsingData {
  UIImage* image = SymbolWithConfiguration(SymbolTrash, nil);
  return [self cr_commandWithInput:UIKeyInputDelete
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_clearBrowsingData)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_CLEAR_BROWSING_DATA"
                             image:image];
}

+ (UIKeyCommand*)cr_closeAll {
  UIImage* image = SymbolWithConfiguration(SymbolXMarkSquareFill, nil);
  return [self cr_commandWithInput:@"w"
                     modifierFlags:ShiftCommand
                            action:@selector(keyCommand_closeAll)
                   titleIDAsString:@"IDS_IOS_KEYBOARD_CLOSE_ALL"
                             image:image];
}

+ (UIKeyCommand*)cr_undo {
  UIKeyCommand* keyCommand =
      [self keyCommandWithInput:@"z"
                  modifierFlags:Command
                         action:@selector(keyCommand_undo)];
  keyCommand.wantsPriorityOverSystemBehavior = YES;
  return keyCommand;
}

#pragma mark - Symbolic Description

- (NSString*)cr_symbolicDescription {
  NSMutableString* description = [NSMutableString string];

  if (self.modifierFlags & UIKeyModifierNumericPad) {
    [description appendString:@"Num lock "];
  }
  if (self.modifierFlags & UIKeyModifierControl) {
    [description appendString:@"⌃"];
  }
  if (self.modifierFlags & UIKeyModifierAlternate) {
    [description appendString:@"⌥"];
  }
  if (self.modifierFlags & UIKeyModifierShift) {
    [description appendString:@"⇧"];
  }
  if (self.modifierFlags & UIKeyModifierAlphaShift) {
    [description appendString:@"⇪"];
  }
  if (self.modifierFlags & UIKeyModifierCommand) {
    [description appendString:@"⌘"];
  }

  if ([self.input isEqualToString:UIKeyInputDelete]) {
    [description appendString:@"⌫"];
  } else if ([self.input isEqualToString:@"\r"]) {
    [description appendString:@"↵"];
  } else if ([self.input isEqualToString:@"\t"]) {
    [description appendString:@"⇥"];
  } else if ([self.input isEqualToString:UIKeyInputUpArrow]) {
    [description appendString:@"↑"];
  } else if ([self.input isEqualToString:UIKeyInputDownArrow]) {
    [description appendString:@"↓"];
  } else if ([self.input isEqualToString:UIKeyInputLeftArrow]) {
    [description appendString:@"←"];
  } else if ([self.input isEqualToString:UIKeyInputRightArrow]) {
    [description appendString:@"→"];
  } else if ([self.input isEqualToString:UIKeyInputEscape]) {
    [description appendString:@"⎋"];
  } else if ([self.input isEqualToString:@" "]) {
    [description appendString:@"␣"];
  } else {
    [description appendString:[self.input uppercaseString]];
  }
  return description;
}

#pragma mark - Private

// The title ID string is used as a key to NSLocalizedString because key
// commands can be requested by the OS very early on, before the resource bundle
// of the localized strings is loaded.
+ (instancetype)cr_commandWithInput:(NSString*)input
                      modifierFlags:(UIKeyModifierFlags)modifierFlags
                             action:(SEL)action
                    titleIDAsString:(NSString*)messageID {
  return [self cr_commandWithInput:input
                     modifierFlags:modifierFlags
                            action:action
                   titleIDAsString:messageID
                             image:nil];
}

// The title ID string is used as a key to NSLocalizedString because key
// commands can be requested by the OS very early on, before the resource bundle
// of the localized strings is loaded.
+ (instancetype)cr_commandWithInput:(NSString*)input
                      modifierFlags:(UIKeyModifierFlags)modifierFlags
                             action:(SEL)action
                    titleIDAsString:(NSString*)messageID
                              image:(UIImage*)image {
  UIKeyCommand* keyCommand =
      [self commandWithTitle:NSLocalizedString(messageID, @"")
                       image:image
                      action:action
                       input:input
               modifierFlags:modifierFlags
                propertyList:nil];
  keyCommand.discoverabilityTitle = keyCommand.title;
  return keyCommand;
}

@end
