# encoding=utf-8

__author__ = "Roland Trouville"
__copyright__ = "Copyright 2015+, Consortium MonPaGe"
__license__ = "Creative Commons 4.0 By-Nc-Sa"
__maintainer__ = "Roland Trouville"
__email__ = "contact.monpage@gmail.com"
__status__ = "Production"

import logging
import os
import platform
from enum import auto

try:
	from enum import StrEnum
except ImportError:
	from strenum import StrEnum

logger = logging.getLogger(__name__)


class Options(object):
	"""
	A utility class for managing configurable application-wide options.

	Options are defined as enums and persisted in the filesystem using
	presence/absence of files in a designated directory (`option/`).

	Responsibilities:
	- Define a set of toggleable runtime options using an `Option` enum.
	- Load and persist option states from/to the filesystem.
	- Provide helper methods to query and set options at runtime.
	- Detect OS type (Windows/macOS).
	- Store a global base directory path.

	Attributes:
	    loaded (bool): Tracks whether options have been loaded from disk.
	    base_dir (str or None): A reference base directory for global file operations.
	    OPTIONS_FOLDER (str): The name of the folder where option files are stored.
	    enabled (list[Option]): Currently enabled options.

	Inner Classes:
	    Option (StrEnum): Enumeration of all configurable options.
	"""

	loaded = False
	base_dir = None

	OPTIONS_FOLDER: str = "option"

	class Option(StrEnum):
		STEREO = auto()
		SCREENS = auto()
		RESEARCH = auto()
		DISPLAY_DEBUG = auto()
		FORCE_DB_UPDATE = auto()
		REMAKE_FONT_SIZE = auto()
		FORCE_SAMPLERATE = auto()
		EXPORT_TEXTGRID = auto()

		# Options of logging levels
		LOG_DEBUG = auto()
		LOG_INFO = auto()
		LOG_WARNING = auto()
		LOG_ERROR = auto()
		LOG_CRITICAL = auto()

	enabled: list[Option] = []

	@staticmethod
	def __get_option_filename(option: Option, val: bool) -> str:
		"""
		Generate the filename corresponding to an option and its boolean value.

		Args:
			option (Option): The option enum or identifier.
			val (bool): The boolean value of the option.

		Returns:
			str: A filename in the format "option.on" if val is True, otherwise "option.off".
		"""
		return str(option).lower() + "." + ("on" if val else "off")

	@staticmethod
	def get_full_option_path(option: Option, val: bool) -> str:
		"""
		Construct the full filesystem path for an option file based on the option and its value.

		Args:
			option (Option): The option enum or identifier.
			val (bool): The boolean value of the option.

		Returns:
			str: The full path combining the OPTIONS_FOLDER and the option filename.
		"""
		return os.path.join(
			Options.OPTIONS_FOLDER, Options.__get_option_filename(option, val)
		)

	@staticmethod
	def set_option(option: Option, val: bool, save: bool = False):
		"""
		Enable or disable an option and optionally save this setting to the filesystem.

		Updates the `Options.enabled` list by adding or removing the option based on `val`.
		If `save` is True, creates or removes a corresponding file in the OPTIONS_FOLDER
		to persist the setting.

		Args:
			option (Option): The option to set.
			val (bool): The value to set the option to (True for enabled, False for disabled).
			save (bool, optional): Whether to save the option state as a file. Defaults to False.
		"""
		logger.info(f"Setting option {option} to {val}")

		if val:
			Options.enabled.append(option)
		elif option in Options.enabled:
			Options.enabled.remove(option)

		if save:
			try:
				logger.info(f"Saving the value of {option}")
				path = Options.get_full_option_path(option, val)

				if val:
					open(path, "x")
				else:
					os.remove(path)

			except RuntimeError as e:
				logger.error(f"Error saving the value of {option}")
				logger.exception(e)

		Options.loaded = True

	@staticmethod
	def check_options():
		"""
		Initialize options from file 'option'
		"""
		logger.info("Checking options")

		for option in Options.Option:
			logger.info(f"Checking option {option}")

			for val in [True, False]:
				if os.path.isfile(Options.get_full_option_path(option, val)):
					logger.info(
						f"Option {option} is {'enabled' if val else 'disabled'}"
					)
					Options.set_option(option, val)
				else:
					logger.info(f"Option {option} is not set")

		Options.loaded = True

	@staticmethod
	def is_enabled(option: Option):
		"""
		Checks if the specified option is enabled.

		:param option: The option to check
		:return: True if the option is enabled, False otherwise
		"""

		if not Options.loaded:
			Options.check_options()
		try:
			value = Options.Option(option)
			return Options.enabled.__contains__(value)
		except ValueError:
			logger.error("Option nonexistent")
			return False

	@staticmethod
	def is_windows():
		"""
		:return: True/False whether it's Windows os or not
		"""
		return platform.system() == "Windows"

	@staticmethod
	def is_mac():
		"""
		:return: True/False whether it's mac os or not
		"""
		return platform.system() == "Darwin"

	@staticmethod
	def set_base_dir(dir: str):
		"""
		Set the base path of all directories
		"""
		logger.info(f"Setting base directory to {dir}")
		Options.base_dir = dir

	@staticmethod
	def get_base_dir():
		"""
		:return: The base path of reference
		"""
		return Options.base_dir
