SearchState.kt

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.browser.state.state

import mozilla.components.browser.state.search.RegionState
import mozilla.components.browser.state.search.SearchEngine

/**
 * Value type that represents the state of search.
 *
 * @property region The region of the user.
 * @property regionSearchEngines The list of bundled [SearchEngine]s for the "home" region of the user.
 * @property customSearchEngines The list of custom [SearchEngine]s, added by the user.
 * @property applicationSearchEngines The list of optional [SearchEngine]s, added by application.
 * @property additionalSearchEngines Additional [SearchEngine]s that the application decided to load
 * and that the user explicitly added to their list of search engines.
 * @property additionalAvailableSearchEngines Additional [SearchEngine]s that the application decided
 * to load and that are available for the user to be added to their list of search engines.
 * @property hiddenSearchEngines The list of bundled [SearchEngine]s the user has explicitly hidden.
 * @property disabledSearchEngineIds The list of [SearchEngine]s ids the user has explicitly disabled
 * from being shown in the quick search list.
 * @property userSelectedSearchEngineId The ID of the default [SearchEngine] selected by the user. Or
 * `null` if the user hasn't made an explicit choice.
 * @property userSelectedSearchEngineName The name of the default [SearchEngine] selected by the user.
 * Or `null` if the user hasn't made an explicit choice.
 * @property regionDefaultSearchEngineId The ID of the default [SearchEngine] of the "home" region
 * of the user.
 * @property regionSearchEnginesOrder Ordered list of [SearchEngine] IDs in the preferred order for
 * this region. Can be used when [regionSearchEngines] needs to be reordered.
 * @property complete Flag that indicates whether loading the list of search engines has completed.
 * This can be used for waiting for specific values (e.g. the default search engine) to be available.
 */
data class SearchState(
    val region: RegionState? = null,
    val regionSearchEngines: List<SearchEngine> = emptyList(),
    val customSearchEngines: List<SearchEngine> = emptyList(),
    val applicationSearchEngines: List<SearchEngine> = emptyList(),
    val additionalSearchEngines: List<SearchEngine> = emptyList(),
    val additionalAvailableSearchEngines: List<SearchEngine> = emptyList(),
    val hiddenSearchEngines: List<SearchEngine> = emptyList(),
    val disabledSearchEngineIds: List<String> = emptyList(),
    val userSelectedSearchEngineId: String? = null,
    val userSelectedSearchEngineName: String? = null,
    val regionDefaultSearchEngineId: String? = null,
    val regionSearchEnginesOrder: List<String> = emptyList(),
    val complete: Boolean = false,
)

/**
 * The list of search engines to be used for searches (bundled and custom search engines).
 */
val SearchState.searchEngines: List<SearchEngine>
    get() = (regionSearchEngines + additionalSearchEngines + customSearchEngines + applicationSearchEngines)

/**
 * The list of search engines that are available for the user to be added to their list of search
 * engines.
 */
val SearchState.availableSearchEngines: List<SearchEngine>
    get() = (hiddenSearchEngines + additionalAvailableSearchEngines)

/**
 * The primary search engine to be used by default for searches. This will either be the user
 * selected search engine, if the user has made an explicit choice, or the default search engine for
 * the user's region.
 */
val SearchState.selectedOrDefaultSearchEngine: SearchEngine?
    get() {
        // Does the user have a default search engine id set and is it in the list of available search engines?
        if (userSelectedSearchEngineId != null) {
            searchEngines.find { engine -> userSelectedSearchEngineId == engine.id }?.let { return it }
        }

        // Did we save a default search engine name for this user and can we find it in the list of
        // available search engines?
        if (userSelectedSearchEngineName != null) {
            searchEngines.find { engine -> userSelectedSearchEngineName == engine.name }?.let { return it }
        }

        // Do we have a default search engine for the region of the user and is it available?
        if (regionDefaultSearchEngineId != null) {
            searchEngines.find { engine -> regionDefaultSearchEngineId == engine.id }?.let { return it }
        }

        // Fallback: Use the first search engine in the list
        if (searchEngines.isNotEmpty()) {
            return searchEngines[0]
        }

        // We couldn't find anything.
        return null
    }