from yta_validation.parameter import ParameterValidator
from yta_programming.singleton import SingletonMeta
from typing import Union


"""
TODO: Maybe we can turn the current boolean flag into
a mode that can control the way we print and log the
information into the files (when this functionality
is implemented).
"""
class ConsolePrinter(metaclass = SingletonMeta):
    """
    *Singleton class*

    Class to wrap functionality related to printing
    messages in the console.

    This is useful when we want to control the messages
    we print with a single parameter, that can be a
    general setting that we pass to this singleton
    instance to control it and avoid printing if the
    print mode is not activated.
    """

    def __init__(
        self,
        do_print: bool = True,
        do_write_file: bool = True
    ):
        self._do_print: bool = do_print
        """
        *For internal use only*

        Flag to indicate if we need to print the messages when
        any `.print()` method is called in this instance.
        """
        self._do_write_file: bool = do_write_file
        """
        *For internal use only*

        Flag to indicate if we need to write the messages when
        any `.print()` method is called with the `output_filename`
        parameter set.
        """

    def activate_print(
        self
    ) -> 'ConsolePrinter':
        """
        Activate the printing mode, which means that when any
        `.print()` method is called, it will actually print
        the expected message.
        """
        self._do_print = True

    def deactivate_print(
        self
    ) -> 'ConsolePrinter':
        """
        Deactivate the printing mode, which means that when
        any `.print()` method is called, it will actually print
        not the expected message.
        """
        self._do_print = False

    def activate_write(
        self
    ) -> 'ConsolePrinter':
        """
        Activate the writing mode, which means that when any
        `.print()` method is called with the `output_filename`
        parameter set, it will write the message to that file
        as a new line.
        """
        self._do_write_file = True

    def deactivate_write(
        self
    ) -> 'ConsolePrinter':
        """
        Deactivate the writing mode, which means that when any
        `.print()` method is called with the `output_filename`
        parameter set, it will not write the message to that
        file.
        """
        self._do_write_file = False

    def _print(
        self,
        message: Union[str, any] = '',
        output_filename: Union[str, None] = None
    ) -> 'ConsolePrinter':
        """
        *For internal use only*

        Internal method to simplify the way we print the message
        (or not) according to the internal flag.

        This method should be called by the other printing methods.

        It will write the `message` as a new file of the
        `output_filename` file, if provided, only if the internal
        `_do_write_file` flag is set.
        """
        if self._do_print:
            print(message)

        if (
            self._do_write_file and
            output_filename is not None
        ):
            with open(output_filename, 'a', encoding = 'utf-8') as file:
                file.write(f'{message}\n')

        return self

    def print(
        self,
        message: Union[str, any] = '',
        output_filename: Union[str, None] = None
    ) -> 'ConsolePrinter':
        """
        Print the `message` provided only if the internal flag
        that allows printing (or not) is activated.

        It will write the `message` as a new file of the
        `output_filename` file, if provided.
        """
        return self._print(
            message = message,
            output_filename = output_filename
        )
    
    def print_error(
        self,
        message: str = '',
        output_filename: Union[str, None] = None
    ) -> 'ConsolePrinter':
        """
        Print the `message` provided as an error if the internal
        flag (to print) is activated.

        It will write the `message` as a new file of the
        `output_filename` file, if provided.
        """
        ParameterValidator.validate_string('message', message, do_accept_empty = True)

        return self._print(
            message = f'>>>> [ERROR] <<<< {message}',
            output_filename = output_filename
        )
    
    def print_in_progress(
        self,
        message: str = '',
        output_filename: Union[str, None] = None
    ) -> 'ConsolePrinter':
        """
        Print the `message` provided as an error if the internal
        flag (to print) is activated.

        It will write the `message` as a new file of the
        `output_filename` file, if provided.
        """
        ParameterValidator.validate_string('message', message, do_accept_empty = True)

        return self._print(
            message = f'.... {message}',
            output_filename = output_filename
        )
    
    def print_completed(
        self,
        message: str = '',
        output_filename: Union[str, None] = None
    ) -> 'ConsolePrinter':
        """
        Print the `message` provided as an error if the internal
        flag (to print) is activated.

        It will write the `message` as a new file of the
        `output_filename` file, if provided.
        """
        ParameterValidator.validate_string('message', message, do_accept_empty = True)

        return self._print(
            message = f'[OK] {message}',
            output_filename = output_filename
        )