text: # -*- coding: utf-8 -*-
  
  """yt-dlg module responsible for parsing the options. """
  
  
  from pathlib import Path
  from typing import Any, Dict, List, Optional, Union
  
  from .utils import remove_shortcuts
  
  
  class OptionHolder:
      """Simple data structure that holds informations for the given option.
  
      Args:
          name (str): Option name. Must be a valid option name
              from the optionsmanager.OptionsManager class.
              See optionsmanager.OptionsManager load_default() method.
  
          flag (str): The option command line switch.
              See https://github.com/ytdl-org/youtube-dl/#options
  
          default_value (Any): The option default value. Must be the same type
              with the corresponding option from the optionsmanager.OptionsManager
              class.
  
          requirements (list): The requirements for the given option. This
              argument is a list of strings with the name of all the options
              that this specific option needs. For example 'subs_lang' needs the
              'write_subs' option to be enabled.
  
      """
  
      def __init__(
          self,
          name: str,
          flag: str,
          default_value: Union[str, int, bool],
          requirements: Optional[List[str]] = None,
      ):
          self.name = name
          self.flag = flag
          self.default_value = default_value
          self.requirements = requirements
  
      def is_boolean(self) -> bool:
          """Returns True if the option is a boolean switch else False. """
          return type(self.default_value) is bool
  
      def check_requirements(self, options_dict: Dict[str, Any]) -> bool:
          """Check if the required options are enabled.
  
          Args:
              options_dict (dict): Dictionary with all the options.
  
          Returns:
              True if any of the required options is enabled else False.
  
          """
          if self.requirements is None:
              return True
  
          return any(options_dict[req] for req in self.requirements)
  
  
  class OptionsParser:
      """Parse optionsmanager.OptionsManager options.
  
      This class is responsible for turning some of the youtube-dlg options
      to youtube-dl command line options.
  
      """
  
      def __init__(self):
          self._ydl_options = [
              OptionHolder("playlist_start", "--playlist-start", 1),
              OptionHolder("playlist_end", "--playlist-end", 0),
              OptionHolder("max_downloads", "--max-downloads", 0),
              OptionHolder("username", "-u", ""),
              OptionHolder("password", "-p", ""),
              OptionHolder("video_password", "--video-password", ""),
              OptionHolder("retries", "-R", 10),
              OptionHolder("proxy", "--proxy", ""),
              OptionHolder("user_agent", "--user-agent", ""),
              OptionHolder("referer", "--referer", ""),
              OptionHolder("ignore_errors", "-i", False),
              OptionHolder("write_description", "--write-description", False),
              OptionHolder("write_info", "--write-info-json", False),
              OptionHolder("write_thumbnail", "--write-thumbnail", False),
              OptionHolder("min_filesize", "--min-filesize", 0),
              OptionHolder("max_filesize", "--max-filesize", 0),
              OptionHolder("write_all_subs", "--all-subs", False),
              OptionHolder("write_auto_subs", "--write-auto-sub", False),
              OptionHolder("write_subs", "--write-sub", False),
              OptionHolder("keep_video", "-k", False),
              OptionHolder("restrict_filenames", "--restrict-filenames", False),
              OptionHolder("save_path", "-o", ""),
              OptionHolder(
                  "embed_subs", "--embed-subs", False, ["write_auto_subs", "write_subs"]
              ),
              OptionHolder("to_audio", "-x", False),
              OptionHolder("audio_format", "--audio-format", ""),
              OptionHolder("video_format", "-f", "0"),
              OptionHolder("subs_lang", "--sub-lang", "", ["write_subs"]),
              OptionHolder("audio_quality", "--audio-quality", "5", ["to_audio"]),
              OptionHolder("youtube_dl_debug", "-v", False),
              OptionHolder("ignore_config", "--ignore-config", False),
              OptionHolder("native_hls", "--hls-prefer-native", False),
              OptionHolder("nomtime", "--no-mtime", False),
              OptionHolder("embed_thumbnail", "--embed-thumbnail", False),
              OptionHolder("add_metadata", "--add-metadata", False),
          ]
  
      def parse(self, options_dictionary: Dict[str, Any]) -> List[str]:
          """Parse optionsmanager.OptionsManager options.
  
          Parses the given options to youtube-dl command line arguments.
  
          Args:
              options_dictionary (dict): Dictionary with all the options.
  
          Returns:
              List of strings with all the youtube-dl command line options.
  
          """
          options_list: List[str] = ["--newline"]
  
          # Create a copy of options_dictionary
          # We don't want to edit the original options dictionary
          # and change some of the options values like 'save_path' etc..
          options_dict = options_dictionary.copy()
  
          self._build_savepath(options_dict)
          self._build_videoformat(options_dict)
          self._build_filesizes(options_dict)
  
          # Parse basic youtube-dl command line options
          for option in self._ydl_options:
              # NOTE Special case should be removed
              if option.name == "to_audio" and not options_dict["audio_format"]:
                  value = options_dict[option.name]
  
                  if value != option.default_value:
                      options_list.append(option.flag)
              elif option.name == "audio_format":
                  value = options_dict[option.name]
  
                  if value != option.default_value:
                      if "-x" not in options_list:
                          options_list.append("-x")
                      options_list.append(option.flag)
                      options_list.append(str(value))
  
                      # NOTE Temp fix
                      # If current 'audio_quality' is not the default one ('5')
                      # then append the audio quality flag and value to the
                      # options list
                      if options_dict["audio_quality"] != "5":
                          options_list.append("--audio-quality")
                          options_list.append(str(options_dict["audio_quality"]))
              elif option.name == "audio_quality":
                  # If the '--audio-quality' is not already in the options list
                  # from the above branch then follow the standard procedure.
                  # We don't have to worry for the sequence in which the code
                  # will be executed since the 'audio_quality' option is placed
                  # after the 'audio_format' option in the self._ydl_options list
                  if option.flag not in options_list and option.check_requirements(
                      options_dict
                  ):
                      value = options_dict[option.name]
                      assert 0 <= int(value) <= 9
  
                      options_list.append(option.flag)
                      options_list.append(str(value))
              elif option.check_requirements(options_dict):
                  value = options_dict[option.name]
  
                  if value != option.default_value:
                      options_list.append(option.flag)
  
                      if not option.is_boolean():
                          options_list.append(str(value))
  
          self.parse_cmd_args(options_dict, options_list)
  
          return options_list
  
      @staticmethod
      def parse_cmd_args(options_dict: Dict[str, Any], options_list: List[str]) -> None:
          """
          Parse cmd_args
  
          Args:
              options_dict (dict): Reference to options dictionary.
              options_list (list): Reference to options list to parse.
  
          """
          # TODO: Handle special cases of single and doble queotes in options better
  
          # Indicates whether an item needs special handling
          special_case = False
  
          # Temp list to hold special items
          special_items = []
  
          for item in options_dict["cmd_args"].split():
  
              # Its a special case if its already a special case
              # or an item starts with single/double quotes
              special_case = special_case or item[0] in ['"', "'"]
  
              if special_case:
                  special_items.append(item)
              else:
                  options_list.append(item)
  
              # If its a special case and we meet a double quote
              # at the end of the item, special case is over and
              # we need to join, filter and append our special items
              # to the options list
              if special_case and item[-1] in ['"', "'"]:
                  options_list.append(" ".join(special_items)[1:-1])
  
                  special_case = False
                  special_items = []
  
      @staticmethod
      def _build_savepath(options_dict: Dict[str, str]) -> None:
          """Build the save path.
  
          We use this method to build the value of the 'save_path' option and
          store it back to the options dictionary.
  
          Args:
              options_dict (dict): Copy of the original options dictionary.
  
          """
          # Check OUTPUT_FORMATS values (str)
          assert isinstance(options_dict["output_format"], str)
  
          save_path: str = remove_shortcuts(options_dict["save_path"])
  
          if options_dict["output_format"] == "0":
              template: str = "%(id)s.%(ext)s"
          elif options_dict["output_format"] == "1":
              template = "%(title)s.%(ext)s"
          elif options_dict["output_format"] == "2":
              template = "%(title)s-%(id)s.%(ext)s"
          elif options_dict["output_format"] == "4":
              template = "%(title)s-%(height)sp.%(ext)s"
          elif options_dict["output_format"] == "5":
              template = "%(title)s-%(id)s-%(height)sp.%(ext)s"
          else:
              template = options_dict["output_template"]
  
          options_dict["save_path"] = str(Path(save_path) / Path(template))
  
      @staticmethod
      def _build_videoformat(options_dict: Dict[str, str]) -> None:
          """Build the video format.
  
          We use this method to build the value of the 'video_format' option and
          store it back to the options dictionary.
  
          Args:
              options_dict (dict): Copy of the original options dictionary.
  
          """
          if (
              options_dict["video_format"] != "0"
              and options_dict["second_video_format"] != "0"
          ):
              options_dict[
                  "video_format"
              ] = f'{options_dict["video_format"]}+{options_dict["second_video_format"]}'
  
      @staticmethod
      def _build_filesizes(options_dict: Dict[str, Union[str, int]]) -> None:
          """Build the filesize options values.
  
          We use this method to build the values of 'min_filesize' and
          'max_filesize' options and store them back to options dictionary.
  
          Args:
              options_dict (dict): Copy of the original options dictionary.
  
          """
          min_filesize = options_dict.get("min_filesize")
          max_filesize = options_dict.get("max_filesize")
  
          if min_filesize:
              options_dict[
                  "min_filesize"
              ] = f'{options_dict["min_filesize"]}{options_dict["min_filesize_unit"]}'
  
          if max_filesize:
              options_dict[
                  "max_filesize"
              ] = f'{options_dict["max_filesize"]}{options_dict["max_filesize_unit"]}'
  