import builtins
import sys
import time
import traceback

from objecttoreturn import ObjectToReturn

from robot.utils import eq, normalize, timestr_to_secs


class ExampleLibrary:

    def print_(self, msg, stream="stdout"):
        """Print given message to selected stream (stdout or stderr)"""
        print(msg, file=getattr(sys, stream))

    def print_n_times(self, msg, count, delay=0):
        """Print given message n times"""
        for i in range(int(count)):
            print(msg)
            self._sleep(delay)

    def print_many(self, *msgs):
        """Print given messages"""
        for msg in msgs:
            print(msg, end=" ")
        print()

    def print_to_stdout_and_stderr(self, msg):
        print("stdout: " + msg, file=sys.stdout)
        print("stderr: " + msg, file=sys.stderr)

    def single_line_doc(self):
        """One line keyword documentation."""
        pass

    def multi_line_doc(self):
        """Only the first line of a multi line keyword doc should be logged.

        Thus for example this text here should not be there
        and neither should this.
        """
        pass

    def exception(self, name, msg="", class_only=False):
        exception = getattr(builtins, name)
        if class_only:
            raise exception
        raise exception(msg)

    def external_exception(self, name, msg):
        ObjectToReturn("failure").exception(name, msg)

    def implicitly_chained_exception(self):
        try:
            try:
                1 / 0
            except Exception:
                ooops  # noqa: F821
        except Exception:
            self._log_python_traceback()
            raise

    def explicitly_chained_exception(self):
        try:
            try:
                assert False
            except Exception as err:
                raise AssertionError("Expected error") from err
        except Exception:
            self._log_python_traceback()
            raise

    def _log_python_traceback(self):
        print("".join(traceback.format_exception(*sys.exc_info())).rstrip())

    def return_string_from_library(self, string="This is a string from Library"):
        return string

    def return_list_from_library(self, *args):
        return list(args)

    def return_three_strings_from_library(self, one="one", two="two", three="three"):
        return one, two, three

    def return_object(self, name="<noname>"):
        return ObjectToReturn(name)

    def check_object_name(self, object, name):
        assert object.name == name, f"{object.name} != {name}"

    def set_object_name(self, object, name):
        object.name = name

    def set_attribute(self, name, value):
        setattr(self, normalize(name), normalize(value))

    def get_attribute(self, name):
        return getattr(self, normalize(name))

    def check_attribute(self, name, expected):
        try:
            actual = getattr(self, normalize(name))
        except AttributeError:
            raise AssertionError(f"Attribute '{name}' not set.")
        if not eq(actual, expected):
            raise AssertionError(
                f"Attribute '{name}' was '{actual}', expected '{expected}'."
            )

    def check_attribute_not_set(self, name):
        if hasattr(self, normalize(name)):
            raise AssertionError(f"Attribute '{name}' should not be set.")

    def backslashes(self, count=1):
        return "\\" * int(count)

    def read_and_log_file(self, path, binary=False):
        if binary:
            mode = "rb"
            encoding = None
        else:
            mode = "r"
            encoding = "UTF-8"
        _file = open(path, mode, encoding=encoding)
        print(_file.read())
        _file.close()

    def print_control_chars(self):
        print("\033[31mRED\033[m\033[32mGREEN\033[m")

    def long_message(self, line_length, line_count, chars="a"):
        line_length = int(line_length)
        line_count = int(line_count)
        msg = chars * line_length + "\n"
        print(msg * line_count)

    def loop_forever(self, no_print=False):
        i = 0
        while True:
            i += 1
            self._sleep(1)
            if not no_print:
                print(f"Looping forever: {i}")

    def write_to_file_after_sleeping(self, path, sec, msg=None):
        with open(path, "w", encoding="UTF-8") as file:
            self._sleep(sec)
            file.write(msg or f"Slept {sec} seconds")

    def sleep_without_logging(self, timestr):
        seconds = timestr_to_secs(timestr)
        self._sleep(seconds)

    def _sleep(self, seconds):
        endtime = time.time() + float(seconds)
        while True:
            remaining = endtime - time.time()
            if remaining <= 0:
                break
            time.sleep(min(remaining, 0.001))

    def return_consumable_iterable(self, *values):
        return iter(values)

    def return_list_subclass(self, *values):
        return _MyList(values)

    def return_unrepresentable_objects(self, identifier=None, just_one=False):
        class FailingStr:

            def __init__(self, identifier=identifier):
                self.identifier = identifier

            def __str__(self):
                raise RuntimeError

        if just_one:
            return FailingStr()
        return FailingStr(), FailingStr()

    def fail_with_suppressed_exception_name(self, msg):
        raise ExceptionWithSuppressedName(msg)

    def exception_with_empty_message_and_name(self):
        raise ExceptionWithEmptyName("")


class _MyList(list):
    pass


class ExceptionWithSuppressedName(AssertionError):
    ROBOT_SUPPRESS_NAME = True


class ExceptionWithEmptyName(AssertionError):
    pass


ExceptionWithEmptyName.__name__ = ""
