# Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#
# This source code is licensed under the Apache 2.0 license found in
# the LICENSE file in the root directory of this source tree.

import enum
import json
import os


def load_config(
    config_json_dict=None, config_json_file_path=None, skip_default_config=False
):
    if (
        config_json_dict is None
        and config_json_file_path is None
        and skip_default_config
    ):
        raise ValueError(
            "Please specify at least one type for loading StringPackConfig."
        )

    sp_config = StringPackConfig()

    if not skip_default_config:
        default_config_json_file_path = os.path.join(
            os.path.dirname(__file__), "default_config.json"
        )
        sp_config.load_from_file(default_config_json_file_path)

    if config_json_file_path is not None:
        sp_config.load_from_file(config_json_file_path)

    if config_json_dict is not None:
        sp_config.load_from_dict(config_json_dict)

    return sp_config


def is_valid_language_qualifier(resource_qualifier):
    if len(resource_qualifier) == 2:
        # language only, e.g.: en
        return True

    if len(resource_qualifier) == 6 and resource_qualifier[2:4] == "-r":
        # language with region. e.g.: fr-rCA
        return True

    if resource_qualifier.startswith("b+") and "-" not in resource_qualifier:
        # special language: e.g.: b+es+419
        return True

    return False


class LanguageHandlingCase(enum.Enum):
    """Determine how should string packs handles strings for that language"""

    # Keep original file in project, don't pack them. This could happen for non-language qualifier, e.g. values-land.
    KEEP_ORIGINAL = 0
    # Pack them at eventually, so move the string resources to intermediate file
    PACK = 1
    # Remove the strings from original resource file, but don't move to intermediate file
    # See StringPackConfig.languages_to_drop
    DROP = 2


class StringPackConfig:
    """
    Configurations for running string packs scripts.

    This provides the configuration information for string pack scripts to generate the intermediate files, and save
    final content at the right place.

    """

    FIELDS_TO_OVERRIDE = [
        "module",
        "original_resources_directories",
        "find_resource_files_command",
        "languages_to_pack",
        "languages_to_drop",
        "assets_directory",
        "pack_ids_class_file_path",
        "pack_id_mapping",
        "pack_scripts_directory",
    ]

    def __init__(self):
        # The project module that string packs are used for.
        self.module = None

        # The directories above the Android resources directories, where the script can find values/strings.xml and values-xx/ directories.
        # (i.e. app/src/main)
        # These directories will also hold the files of the packable strings.
        # FIND operation checks each directory's res subdirectory for values/strings.xml and values-xx/.strings.xml files. The ids are stored in pack_ids_class_file_path.
        # MOVE operation moves packable strings found in FIND to the directory's string-packs/strings subdirectory.
        # PACK operation packs the files in the directory's string-packs/strings subdirectory into .pack files in assets_directory.
        self.original_resources_directories = []

        # Executable command line that returns all resource files for parsing movable strings.
        self.find_resource_files_command = None

        # List of languages that need to be packed, or ["*"] means all languages.
        self.languages_to_pack = []

        # List of languages that don't need to be packed, and they could be safely removed.
        # e.g.: "zh-rTW" could be drop if "zh-rHK" is set the same, the app would pick it correctly.
        self.languages_to_drop = []

        # The full class name of custom widgets that are already using StringPack resources reader.
        self.safe_widget_classes = set()

        # The assets directory where to save the generated string pack files.
        self.assets_directory = None

        # File path to the class where stores the map from android string key to pack id.
        self.pack_ids_class_file_path = None

        # A dictionary that maps a specific language to its pack ID. The default pack ID is the language code, but the
        # app may decide to pack similar languages to one pack file.
        # For example, it may save space to pack Czech and Slovak in one pack file, assuming the app knows where to
        # look for them.
        self.pack_id_mapping = {}

        # The directory that holds all the python scripts
        self.pack_scripts_directory = None

    def load_from_file(self, config_json_file_path):
        """Load configuration from json file."""

        with open(config_json_file_path) as file_content:
            config_json_dict = json.load(file_content)

        self.load_from_dict(config_json_dict)

    def load_from_dict(self, config_dict):
        """Load configuration from a dictionary which servers as json.

        All configuration values would be overridden with the value that provided in config_json, except
        `safe_widget_classes` which new widget class names would be append to it.
        """

        for field in StringPackConfig.FIELDS_TO_OVERRIDE:
            if field in config_dict:
                setattr(self, field, config_dict[field])

        # Append instead of override
        if "safe_widget_classes" in config_dict:
            self.safe_widget_classes.update(config_dict["safe_widget_classes"])

        return self

    def get_default_string_files(self):
        return [
            os.path.join(source_directory, "res", "values", "strings.xml")
            for source_directory in self.original_resources_directories
        ]

    def get_handling_case(self, resource_qualifier):
        """Determine how to handle the string for given resource qualifier if it's a language qualifier."""

        if resource_qualifier in self.languages_to_drop:
            return LanguageHandlingCase.DROP

        if self.languages_to_pack == ["*"]:
            # Match all valid language
            if is_valid_language_qualifier(resource_qualifier):
                return LanguageHandlingCase.PACK
        elif resource_qualifier in self.languages_to_pack:
            return LanguageHandlingCase.PACK

        return LanguageHandlingCase.KEEP_ORIGINAL
