// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '//resources/cr_elements/cr_button/cr_button.js';
import '//resources/cr_elements/cr_icon/cr_icon.js';
import '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
import '//resources/cr_elements/icons.html.js';
import { assert, assertInstanceof } from '//resources/js/assert.js';
import { EventTracker } from '//resources/js/event_tracker.js';
import { loadTimeData } from '//resources/js/load_time_data.js';
import { afterNextRender, PolymerElement } from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import { BrowserProxyImpl } from './browser_proxy.js';
import { LanguageBrowserProxyImpl } from './language_browser_proxy.js';
import { UserAction } from './lens.mojom-webui.js';
import { INVOCATION_SOURCE } from './lens_overlay_app.js';
import { recordLensOverlayInteraction } from './metrics_utils.js';
import { focusShimmerOnRegion, ShimmerControlRequester, unfocusShimmer } from './selection_utils.js';
import { getTemplate } from './translate_button.html.js';
// The language codes that are supported to be translated by the server.
const SUPPORTED_TRANSLATION_LANGUAGES = new Set([
    'af', 'sq', 'am', 'ar', 'hy', 'az', 'eu', 'be', 'bn', 'bs', 'bg',
    'ca', 'ceb', 'zh-CN', 'zh-TW', 'co', 'hr', 'cs', 'da', 'nl', 'en', 'eo',
    'et', 'fi', 'fr', 'fy', 'gl', 'ka', 'de', 'el', 'gu', 'ht', 'ha',
    'haw', 'hi', 'hmn', 'hu', 'is', 'ig', 'id', 'ga', 'it', 'iw', 'ja',
    'jv', 'kn', 'kk', 'km', 'rw', 'ko', 'ku', 'ky', 'lo', 'la', 'lv',
    'lt', 'lb', 'mk', 'mg', 'ms', 'ml', 'mt', 'mi', 'mr', 'mn', 'my',
    'ne', 'no', 'ny', 'or', 'ps', 'fa', 'pl', 'pt', 'pa', 'ro', 'ru',
    'sm', 'gd', 'sr', 'st', 'sn', 'sd', 'si', 'sk', 'sl', 'so', 'es',
    'su', 'sw', 'sv', 'tl', 'tg', 'ta', 'tt', 'te', 'th', 'tr', 'tk',
    'uk', 'ur', 'ug', 'uz', 'vi', 'cy', 'xh', 'yi', 'yo', 'zu',
]);
export class TranslateButtonElement extends PolymerElement {
    static get is() {
        return 'translate-button';
    }
    static get template() {
        return getTemplate();
    }
    static get properties() {
        return {
            clientSourceLanguageList: { type: Array },
            clientTargetLanguageList: { type: Array },
            contentLanguage: {
                type: String,
                reflectToAttribute: true,
                value: '',
            },
            isLensOverlayContextualSearchboxEnabled: {
                type: Boolean,
                reflectToAttribute: true,
            },
            isSourceLanguageSearchboxOpen: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
            isTargetLanguageSearchboxOpen: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
            isTranslateModeEnabled: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
            languagePickerButtonsVisible: {
                type: Boolean,
                computed: `computeLanguagePickerButtonsVisible(
              isTranslateModeEnabled, sourceLanguageMenuVisible,
              targetLanguageMenuVisible)`,
            },
            recentSourceLanguages: {
                type: Array,
                value: () => [],
            },
            recentTargetLanguages: {
                type: Array,
                value: () => [],
            },
            shouldFetchSupportedLanguages: {
                type: Boolean,
                value: () => loadTimeData.getBoolean('shouldFetchSupportedLanguages'),
                readOnly: true,
                reflectToAttribute: true,
            },
            shouldShowRecentSourceLanguages: {
                type: Boolean,
                computed: 'shouldShowRecentLanguages(recentSourceLanguages)',
                reflectToAttribute: true,
            },
            shouldShowRecentTargetLanguages: {
                type: Boolean,
                computed: 'shouldShowRecentLanguages(recentTargetLanguages)',
                reflectToAttribute: true,
            },
            searchboxHasText: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
            searchedLanguageList: {
                type: Array,
                value: () => [],
            },
            serverSourceLanguageList: {
                type: Array,
                value: () => [],
            },
            serverTargetLanguageList: {
                type: Array,
                value: () => [],
            },
            shouldShowStarsIcon: {
                type: Boolean,
                computed: 'computeShouldShowStarsIcon(sourceLanguage)',
                reflectToAttribute: true,
            },
            sourceLanguage: {
                type: Object,
                value: null,
            },
            sourceLanguageList: {
                type: Array,
                computed: `getSourceLanguageList(clientSourceLanguageList,
                                   serverSourceLanguageList)`,
            },
            sourceLanguageMenuVisible: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
            targetLanguage: {
                type: Object,
                value: null,
            },
            targetLanguageList: {
                type: Array,
                computed: `getTargetLanguageList(clientTargetLanguageList,
                                   serverTargetLanguageList)`,
            },
            targetLanguageMenuVisible: {
                type: Boolean,
                reflectToAttribute: true,
                value: false,
            },
        };
    }
    eventTracker = new EventTracker();
    // The list of source translate language codes supported by Lens. This differs
    // from the server source translate list because it is a list of language
    // codes that can currently be reliably sent to Lens for translation.
    supportedSourceLanguages = new Set(loadTimeData.getString('translateSourceLanguages').split(','));
    // The list of target translate language codes supported by Lens. This differs
    // from the server target translate list because it is a list of language
    // codes that can currently be reliably sent to Lens for translation. This set
    // needs to be combined with `supportedSourceLanguages` before use.
    supportedTargetLanguages = new Set(loadTimeData.getString('translateTargetLanguages').split(','));
    // A browser proxy for communicating with the C++ Lens overlay controller.
    browserProxy = BrowserProxyImpl.getInstance();
    // A browser proxy for fetching the language settings from the Chrome API.
    languageBrowserProxy = LanguageBrowserProxyImpl.getInstance();
    // An array for keeping track of the events the translate button listens to
    // from the browser proxy.
    listenerIds;
    connectedCallback() {
        super.connectedCallback();
        this.handleFetchLanguageList();
        this.eventTracker.add(document, 'received-content-language', (e) => {
            // Lens sends 'zh' and 'zh-Hant', which need to be converted to
            // 'zh-CN' and 'zh-TW' to match the language codes used by
            // chrome.languageSettingsPrivate.
            if (e.detail.contentLanguage === 'zh') {
                this.contentLanguage = 'zh-CN';
            }
            else if (e.detail.contentLanguage === 'zh-Hant') {
                this.contentLanguage = 'zh-TW';
            }
            else {
                this.contentLanguage = e.detail.contentLanguage;
            }
        });
        this.eventTracker.add(this.$.sourceLanguagePickerMenu, 'focusout', (event) => {
            const targetWithFocus = event.relatedTarget;
            if (!targetWithFocus || !(targetWithFocus instanceof Node) ||
                !this.$.sourceLanguagePickerMenu.contains(targetWithFocus)) {
                this.hideLanguagePickerMenus(/*shouldFocus=*/ false);
            }
        });
        this.eventTracker.add(this.$.targetLanguagePickerMenu, 'focusout', (event) => {
            const targetWithFocus = event.relatedTarget;
            if (!targetWithFocus || !(targetWithFocus instanceof Node) ||
                !this.$.targetLanguagePickerMenu.contains(targetWithFocus)) {
                this.hideLanguagePickerMenus(/*shouldFocus=*/ false);
            }
        });
        // Set up listener to listen to events from C++.
        this.listenerIds = [
            this.browserProxy.callbackRouter.setTranslateMode.addListener(this.setTranslateMode.bind(this)),
        ];
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.listenerIds.forEach(id => assert(this.browserProxy.callbackRouter.removeListener(id)));
        this.listenerIds = [];
        this.eventTracker.removeAll();
    }
    getTranslateEnableButton() {
        return this.$.translateEnableButton;
    }
    handleLanguagePickerKeyDown(event) {
        // If the searchbox is open, then we should do nothing in this case.
        if (this.isTargetLanguageSearchboxOpen ||
            this.isSourceLanguageSearchboxOpen) {
            return;
        }
        // A language picker must be focused and visible in order to receive this
        // event.
        assert(this.sourceLanguageMenuVisible || this.targetLanguageMenuVisible);
        // The key must be of length 1 if it is a character.
        if (event.key.length !== 1) {
            return;
        }
        // We should open the searchbox with the key if it is active.
        if (this.shouldFetchSupportedLanguages) {
            const isTarget = this.targetLanguageMenuVisible ? true : false;
            if (isTarget) {
                this.openTargetLanguageSearchbox();
            }
            else {
                this.openSourceLanguageSearchbox();
            }
            return;
        }
        // Get the appropriate language list by checking which language menu is
        // visible.
        const languageList = this.sourceLanguageMenuVisible ?
            this.getSourceLanguageList() :
            this.getTargetLanguageList();
        let scrollLanguageIndex = -1;
        const startingChar = event.key.toLowerCase();
        for (let i = 0; i < languageList.length; i++) {
            const language = languageList[i];
            const languageStartingChar = language.name.charAt(0).toLowerCase();
            if (startingChar === languageStartingChar) {
                scrollLanguageIndex = i;
                break;
            }
        }
        if (scrollLanguageIndex >= 0) {
            const pickerMenu = this.sourceLanguageMenuVisible ?
                this.$.sourceLanguagePickerMenu :
                this.$.targetLanguagePickerMenu;
            const menuItems = pickerMenu.querySelectorAll('cr-button:not(#sourceAutoDetectButton)');
            const languageElement = menuItems[scrollLanguageIndex];
            languageElement.scrollIntoView();
            languageElement.focus();
        }
    }
    handleFetchLanguageList() {
        if (this.shouldFetchSupportedLanguages) {
            // Combine the source and target translate languages into one set.
            this.supportedSourceLanguages.forEach((code) => this.supportedTargetLanguages.add(code));
            this.languageBrowserProxy.getStoredServerLanguages(this.browserProxy)
                .then(this.onServerLanguageListRetrieved.bind(this));
        }
        this.languageBrowserProxy.getClientLanguageList().then(this.onClientLanguageListRetrieved.bind(this));
    }
    onServerLanguageListRetrieved(languages) {
        this.serverSourceLanguageList =
            languages.sourceLanguages.filter((language) => {
                return this.supportedSourceLanguages.has(language.languageCode);
            });
        this.serverTargetLanguageList =
            languages.targetLanguages.filter((language) => {
                return this.supportedTargetLanguages.has(language.languageCode);
            });
        // Since we always want to use the server languages over the client
        // languages, we set a variable to always override any existing language if
        // the initial languages were already set.
        this.maybeSetInitialLanguagesInPicker(/*overrideExisting=*/ true);
    }
    onClientLanguageListRetrieved(languageList) {
        const supportedSourceTranslateLanguages = this.shouldFetchSupportedLanguages ? this.supportedSourceLanguages :
            SUPPORTED_TRANSLATION_LANGUAGES;
        const supportedTargetTranslateLanguages = this.shouldFetchSupportedLanguages ? this.supportedTargetLanguages :
            SUPPORTED_TRANSLATION_LANGUAGES;
        this.clientSourceLanguageList = languageList.filter((language) => {
            return supportedSourceTranslateLanguages.has(language.languageCode);
        });
        this.clientTargetLanguageList = languageList.filter((language) => {
            return supportedTargetTranslateLanguages.has(language.languageCode);
        });
        this.maybeSetInitialLanguagesInPicker();
    }
    maybeSetInitialLanguagesInPicker(overrideExisting = false) {
        // If the target language was already set and we do not want to override
        // anyway, then we should return early.
        if (this.targetLanguage && !overrideExisting) {
            return;
        }
        // Last used source and target languages are stored in local storage if
        // feature enabled.
        if (this.shouldFetchSupportedLanguages) {
            // Set the recent languages for source and target language pickers. If
            // there are none, this is a no-op.
            this.maybeSetRecentLanguages();
            const sourceLanguageCode = this.languageBrowserProxy.getLastUsedSourceLanguage();
            const targetLanguageCode = this.languageBrowserProxy.getLastUsedTargetLanguage();
            const initialSourceLanguage = this.getSourceLanguageList().find(language => language.languageCode === sourceLanguageCode);
            const initialTargetLanguage = this.getTargetLanguageList().find(language => language.languageCode === targetLanguageCode);
            this.sourceLanguage =
                initialSourceLanguage ? initialSourceLanguage : null;
            this.targetLanguage =
                initialTargetLanguage ? initialTargetLanguage : null;
            // If target language was still not set, then we still need to get the
            // translate target language from the language browser proxy. Otherwise,
            // return.
            if (this.targetLanguage) {
                return;
            }
        }
        // Get the default translate target language. This needs to happen after
        // fetching the language list so we can use the list to fetch the language's
        // display name.
        this.languageBrowserProxy.getTranslateTargetLanguage().then(this.onTargetLanguageRetrieved.bind(this));
    }
    maybeSetRecentLanguages() {
        const recentSourceLanguageCodes = this.languageBrowserProxy.getRecentSourceLanguages();
        if (recentSourceLanguageCodes.length > 0) {
            this.recentSourceLanguages =
                this.getSourceLanguageList().filter((language) => {
                    return recentSourceLanguageCodes.includes(language.languageCode);
                });
        }
        const recentTargetLanguageCodes = this.languageBrowserProxy.getRecentTargetLanguages();
        if (recentTargetLanguageCodes.length > 0) {
            this.recentTargetLanguages =
                this.getTargetLanguageList().filter((language) => {
                    return recentTargetLanguageCodes.includes(language.languageCode);
                });
        }
    }
    onTargetLanguageRetrieved(targetLanguageCode) {
        const defaultLanguage = this.getTargetLanguageList().find(language => language.languageCode === targetLanguageCode);
        // If the target language is set to one supported by Lens, then we set it
        // and are done.
        if (defaultLanguage) {
            this.targetLanguage = defaultLanguage;
            return;
        }
        // Otherwise, we default to the first language in the list.
        this.targetLanguage = this.getTargetLanguageList()[0];
    }
    onAutoDetectMenuItemClick() {
        this.sourceLanguage = null;
        this.languageBrowserProxy.storeLastUsedSourceLanguage(null);
        this.hideLanguagePickerMenus();
        this.maybeIssueTranslateRequest();
        recordLensOverlayInteraction(INVOCATION_SOURCE, UserAction.kTranslateSourceLanguageChanged);
    }
    onSourceLanguageButtonClick() {
        this.notifyLanguagePickerOpened();
        this.sourceLanguageMenuVisible = !this.sourceLanguageMenuVisible;
        this.targetLanguageMenuVisible = false;
        // We need to wait for the language picker to render before focusing.
        afterNextRender(this, () => {
            this.$.sourceLanguagePickerBackButton.focus();
        });
    }
    onTargetLanguageButtonClick() {
        this.notifyLanguagePickerOpened();
        this.targetLanguageMenuVisible = !this.targetLanguageMenuVisible;
        this.sourceLanguageMenuVisible = false;
        // We need to wait for the language picker to render before focusing.
        afterNextRender(this, () => {
            this.$.targetLanguagePickerBackButton.focus();
        });
    }
    onSourceLanguageMenuItemClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const newSourceLanguage = this.$.sourceLanguagePickerContainer.itemForElement(event.target);
        this.setNewSourceLanguage(newSourceLanguage);
    }
    onRecentSourceLanguageClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const newSourceLanguage = this.$.recentSourceLanguagesContainer.itemForElement(event.target);
        this.setNewSourceLanguage(newSourceLanguage);
    }
    onSourceSearchLanguageItemClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const searchedLanguage = this.$.searchSourceLanguagesContainer.itemForElement(event.target);
        this.setNewSourceLanguage(searchedLanguage.language);
    }
    setNewSourceLanguage(sourceLanguage) {
        this.sourceLanguage = sourceLanguage;
        if (this.shouldFetchSupportedLanguages) {
            this.languageBrowserProxy.storeLastUsedSourceLanguage(sourceLanguage ? sourceLanguage.languageCode : null);
            this.addRecentSourceLanguage(sourceLanguage);
        }
        this.clearSearchboxState();
        this.hideLanguagePickerMenus();
        this.maybeIssueTranslateRequest();
        recordLensOverlayInteraction(INVOCATION_SOURCE, UserAction.kTranslateSourceLanguageChanged);
    }
    onTargetLanguageMenuItemClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const newTargetLanguage = this.$.targetLanguagePickerContainer.itemForElement(event.target);
        this.setNewTargetLanguage(newTargetLanguage);
    }
    onTargetSearchLanguageItemClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const searchedLanguage = this.$.searchTargetLanguagesContainer.itemForElement(event.target);
        this.setNewTargetLanguage(searchedLanguage.language);
    }
    onRecentTargetLanguageClick(event) {
        assertInstanceof(event.target, HTMLElement);
        const newTargetLanguage = this.$.recentTargetLanguagesContainer.itemForElement(event.target);
        this.setNewTargetLanguage(newTargetLanguage);
    }
    setNewTargetLanguage(targetLanguage) {
        this.targetLanguage = targetLanguage;
        if (this.shouldFetchSupportedLanguages) {
            this.languageBrowserProxy.storeLastUsedTargetLanguage(targetLanguage ? targetLanguage.languageCode : null);
            this.addRecentTargetLanguage(targetLanguage);
        }
        this.clearSearchboxState();
        this.hideLanguagePickerMenus();
        this.maybeIssueTranslateRequest();
        recordLensOverlayInteraction(INVOCATION_SOURCE, UserAction.kTranslateTargetLanguageChanged);
        this.updateTranslateModeState(true);
    }
    onTranslateButtonClick() {
        // Toggle translate mode on button click.
        this.isTranslateModeEnabled = !this.isTranslateModeEnabled;
        if (this.isTranslateModeEnabled) {
            this.browserProxy.handler.maybeCloseTranslateFeaturePromo(
            /*featureEngaged=*/ true);
            this.maybeIssueTranslateRequest();
        }
        else {
            this.browserProxy.handler.issueEndTranslateModeRequest();
        }
        recordLensOverlayInteraction(INVOCATION_SOURCE, this.isTranslateModeEnabled ? UserAction.kTranslateButtonEnableAction :
            UserAction.kTranslateButtonDisableAction);
        // Focus or unfocus the shimmer depending on whether translate was
        // enabled/disabled.
        if (this.isTranslateModeEnabled) {
            focusShimmerOnRegion(this, /*top=*/ 0, /*left=*/ 0, /*width=*/ 0, /*height=*/ 0, ShimmerControlRequester.TRANSLATE);
            this.$.sourceLanguageButton.focus();
        }
        else {
            unfocusShimmer(this, ShimmerControlRequester.TRANSLATE);
            this.$.translateEnableButton.focus();
        }
        // Dispatch event to let other components know the overlay translate mode
        // state.
        this.updateTranslateModeState(true);
    }
    maybeIssueTranslateRequest() {
        if (this.isTranslateModeEnabled && this.targetLanguage) {
            this.browserProxy.handler.issueTranslateFullPageRequest(this.sourceLanguage ? this.sourceLanguage.languageCode : 'auto', this.targetLanguage.languageCode);
        }
    }
    setTranslateMode(sourceLanguage, targetLanguage) {
        if (sourceLanguage.length === 0 && targetLanguage.length === 0) {
            this.disableTranslateMode();
            return;
        }
        const newSourceLanguage = sourceLanguage === 'auto' ?
            null :
            this.getSourceLanguageList().find(language => language.languageCode === sourceLanguage);
        const newTargetLanguage = this.getTargetLanguageList().find(language => language.languageCode === targetLanguage);
        // Do nothing if the languages set are not in the language list. Source
        // language can be null to indicate we should auto-detect source language.
        // Also, do nothing if we set the translate mode to the same source and
        // target language that already appear on the screen.
        if (newSourceLanguage === undefined || !newTargetLanguage ||
            (this.sourceLanguage === newSourceLanguage &&
                this.targetLanguage === newTargetLanguage &&
                this.isTranslateModeEnabled)) {
            return;
        }
        this.sourceLanguage = newSourceLanguage;
        this.targetLanguage = newTargetLanguage;
        // Refocus the shimmer into translate mode if it was not already.
        if (!this.isTranslateModeEnabled) {
            focusShimmerOnRegion(this, /*top=*/ 0, /*left=*/ 0, /*width=*/ 0, /*height=*/ 0, ShimmerControlRequester.TRANSLATE);
        }
        this.isTranslateModeEnabled = true;
        this.maybeIssueTranslateRequest();
        this.updateTranslateModeState(true);
    }
    disableTranslateMode() {
        if (!this.isTranslateModeEnabled) {
            return;
        }
        this.isTranslateModeEnabled = false;
        unfocusShimmer(this, ShimmerControlRequester.TRANSLATE);
        this.updateTranslateModeState(false);
    }
    handleBackButtonClick() {
        if (this.isTargetLanguageSearchboxOpen ||
            this.isSourceLanguageSearchboxOpen) {
            this.clearSearchboxState();
            return;
        }
        this.hideLanguagePickerMenus();
    }
    hideLanguagePickerMenus(shouldFocus = true) {
        this.$.sourceLanguagePickerMenu.scroll(0, 0);
        this.$.targetLanguagePickerMenu.scroll(0, 0);
        // Depending on which language picker menu was opened, return focus to
        // the corresponding language button.
        if (shouldFocus) {
            if (this.sourceLanguageMenuVisible) {
                this.$.sourceLanguageButton.focus();
            }
            else {
                this.$.targetLanguageButton.focus();
            }
        }
        this.notifyLanguagePickerClosed();
        this.targetLanguageMenuVisible = false;
        this.sourceLanguageMenuVisible = false;
    }
    getSourceLanguageDisplayName() {
        if (this.sourceLanguage) {
            return this.sourceLanguage.name;
        }
        // There is a race condition where the DOM can render before the language
        // browser proxy returns the language list. For this reason, we need to
        // check if the translate language list is present before attempting to find
        // the content language display name inside of it.
        if (this.contentLanguage !== '' && this.getSourceLanguageList()) {
            const detectedLanguage = this.getSourceLanguageList().find(language => language.languageCode === this.contentLanguage);
            if (detectedLanguage !== undefined) {
                return detectedLanguage.name;
            }
        }
        return loadTimeData.getString('detectLanguage');
    }
    getTargetLanguageDisplayName() {
        if (this.targetLanguage) {
            return this.targetLanguage.name;
        }
        return '';
    }
    getContentLanguageDisplayName() {
        // There is a race condition where the DOM can render before the language
        // browser proxy returns the language list. For this reason, we need to
        // check if the translate language list is present before attempting to find
        // the content language display name inside of it.
        if (this.contentLanguage !== '' && this.getSourceLanguageList()) {
            const detectedLanguage = this.getSourceLanguageList().find(language => language.languageCode === this.contentLanguage);
            if (detectedLanguage !== undefined) {
                return detectedLanguage.name;
            }
        }
        return '';
    }
    notifyLanguagePickerOpened() {
        document.dispatchEvent(new CustomEvent('language-picker-opened', {
            bubbles: true,
            composed: true,
        }));
    }
    notifyLanguagePickerClosed() {
        document.dispatchEvent(new CustomEvent('language-picker-closed', {
            bubbles: true,
            composed: true,
        }));
    }
    openSourceLanguageSearchbox() {
        this.isSourceLanguageSearchboxOpen = true;
        this.$.sourceLanguageSearchbox.focus();
        this.onSourceSearchboxInputChange();
    }
    openTargetLanguageSearchbox() {
        this.isTargetLanguageSearchboxOpen = true;
        this.$.targetLanguageSearchbox.focus();
        this.onTargetSearchboxInputChange();
    }
    onSourceSearchboxInputChange() {
        const searchString = this.$.sourceLanguageSearchbox.value.trim();
        this.updateSearchedListOnInputChanged(this.getSourceLanguageList(), searchString);
    }
    onTargetSearchboxInputChange() {
        const searchString = this.$.targetLanguageSearchbox.value.trim();
        this.updateSearchedListOnInputChanged(this.getTargetLanguageList(), searchString);
    }
    updateSearchedListOnInputChanged(languageList, searchString) {
        this.searchboxHasText = searchString.length > 0;
        if (this.searchboxHasText) {
            const lowerCaseSearchString = searchString.toLowerCase();
            this.searchedLanguageList =
                languageList
                    .filter((language) => {
                    const lowerCaseLanguageName = language.name.toLowerCase();
                    return lowerCaseLanguageName.includes(lowerCaseSearchString);
                })
                    .sort((previous, next) => {
                    const lowerCaseA = previous.name.toLowerCase();
                    const lowerCaseB = next.name.toLowerCase();
                    const startsWithA = lowerCaseA.startsWith(lowerCaseSearchString);
                    const startsWithB = lowerCaseB.startsWith(lowerCaseSearchString);
                    if (startsWithA && !startsWithB) {
                        return -1;
                    }
                    else if (!startsWithA && startsWithB) {
                        return 1;
                    }
                    else {
                        return 0;
                    }
                })
                    .map((language) => {
                    const lowerCaseLanguageName = language.name.toLowerCase();
                    const startIndex = lowerCaseLanguageName.indexOf(lowerCaseSearchString);
                    if (startIndex !== -1) {
                        const beforeSearch = language.name.substring(0, startIndex);
                        const highlightText = language.name.substring(startIndex, startIndex + lowerCaseSearchString.length);
                        const afterSearch = language.name.substring(startIndex + lowerCaseSearchString.length);
                        return {
                            beforeHighlightText: beforeSearch,
                            searchHighlightText: highlightText,
                            afterHighlightText: afterSearch,
                            language,
                        };
                    }
                    return {
                        beforeHighlightText: language.name,
                        searchHighlightText: '',
                        afterHighlightText: '',
                        language,
                    };
                });
        }
    }
    clearSearchboxState() {
        this.searchedLanguageList = [];
        this.$.targetLanguageSearchbox.value = '';
        this.$.sourceLanguageSearchbox.value = '';
        this.searchboxHasText = false;
        this.isSourceLanguageSearchboxOpen = false;
        this.isTargetLanguageSearchboxOpen = false;
    }
    computeShouldShowStarsIcon() {
        return this.sourceLanguage === null;
    }
    getTabIndexForTranslateEntry() {
        return this.isTranslateModeEnabled ? -1 : 0;
    }
    getTabIndexForTranslateExit() {
        if ((this.sourceLanguageMenuVisible || this.targetLanguageMenuVisible) &&
            this.isLensOverlayContextualSearchboxEnabled) {
            return 0;
        }
        return this.getTabIndexForLanguagePickerButtons();
    }
    getTabIndexForLanguagePickerButtons() {
        return this.computeLanguagePickerButtonsVisible() ? 0 : -1;
    }
    computeLanguagePickerButtonsVisible() {
        return this.isTranslateModeEnabled && !this.sourceLanguageMenuVisible &&
            !this.targetLanguageMenuVisible;
    }
    getSourceLanguageButtonAriaLabel() {
        let sourceLanguageAriaLabel = this.getSourceLanguageDisplayName();
        // If the source language is set to auto detect language, the label should
        // have both the source language display name (if found) and detect language
        // string.
        if (this.sourceLanguage === null &&
            sourceLanguageAriaLabel !== loadTimeData.getString('detectLanguage')) {
            sourceLanguageAriaLabel = `${this.getSourceLanguageDisplayName()}, ${loadTimeData.getString('detectLanguage')}`;
        }
        return loadTimeData.getStringF('sourceLanguageAriaLabel', sourceLanguageAriaLabel);
    }
    getTargetLanguageButtonAriaLabel() {
        return loadTimeData.getStringF('targetLanguageAriaLabel', this.getTargetLanguageDisplayName());
    }
    getAutoCheckedClass(sourceLanguage) {
        return sourceLanguage === null ? 'selected' : '';
    }
    getSearchedLanguageCheckedClass(searchedLanguage, selectedLanguage) {
        return this.getLanguageCheckedClass(searchedLanguage.language, selectedLanguage);
    }
    getLanguageCheckedClass(language, selectedLanguage) {
        if (!selectedLanguage) {
            return '';
        }
        return selectedLanguage.languageCode === language.languageCode ?
            'selected' :
            '';
    }
    shouldShowRecentLanguages(recentLanguages) {
        return recentLanguages.length > 0;
    }
    updateTranslateModeState(shouldUnselectWords) {
        this.dispatchEvent(new CustomEvent('translate-mode-state-changed', {
            bubbles: true,
            composed: true,
            detail: {
                translateModeEnabled: this.isTranslateModeEnabled,
                targetLanguage: this.targetLanguage.languageCode,
                shouldUnselectWords: shouldUnselectWords,
            },
        }));
    }
    addRecentSourceLanguage(language) {
        if (!language) {
            return;
        }
        this.prepareRecentLanguageListForAddition(this.recentSourceLanguages, language);
        // This needs to happen this way in order for properties to properly update
        // the HTML.
        this.recentSourceLanguages = [language, ...this.recentSourceLanguages];
        this.languageBrowserProxy.storeRecentSourceLanguages(this.recentSourceLanguages.map((language) => {
            return language.languageCode;
        }));
    }
    addRecentTargetLanguage(language) {
        assert(language);
        this.prepareRecentLanguageListForAddition(this.recentTargetLanguages, language);
        // This needs to happen this way in order for properties to properly update
        // the HTML.
        this.recentTargetLanguages = [language, ...this.recentTargetLanguages];
        this.languageBrowserProxy.storeRecentTargetLanguages(this.recentTargetLanguages.map((language) => {
            return language.languageCode;
        }));
    }
    // Prepares the recent language list for addition by removing the language if
    // it is already in the list or popping languages if its length is above the
    // max.
    prepareRecentLanguageListForAddition(languageList, language) {
        // If the language is already present in the queue, remove it and then
        // re-add it so it's at the top. If the slots are full, then we should
        // dequeue languages until it is not and then add the most recent language.
        const index = languageList.findIndex((recentLanguage) => recentLanguage.languageCode === language.languageCode);
        if (index > -1) {
            languageList.splice(index, 1);
        }
        while (languageList.length >=
            loadTimeData.getInteger('recentLanguagesAmount')) {
            languageList.pop();
        }
    }
    getSourceLanguageList() {
        return this.serverSourceLanguageList.length === 0 ?
            this.clientSourceLanguageList :
            this.serverSourceLanguageList;
    }
    getTargetLanguageList() {
        return this.serverTargetLanguageList.length === 0 ?
            this.clientTargetLanguageList :
            this.serverTargetLanguageList;
    }
}
customElements.define(TranslateButtonElement.is, TranslateButtonElement);
