from sys import stderr
from typing import List, Union

from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.edge.service import Service
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.ie.service import Service
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.safari.service import Service
from selenium.webdriver.support.wait import WebDriverWait

from je_web_runner.je_web_runner.web_element_wrapper import web_element_wrapper
from je_web_runner.je_web_runner.webdriver_with_options import set_webdriver_options_capability_wrapper
from je_web_runner.utils.assert_value.result_check import check_webdriver
from je_web_runner.utils.exception.exception_tag import selenium_wrapper_opera_path_error
from je_web_runner.utils.exception.exception_tag import selenium_wrapper_web_driver_not_found_error
from je_web_runner.utils.exception.exceptions import WebDriverException, WebDriverIsNoneException
from je_web_runner.utils.exception.exceptions import WebDriverNotFoundException
from je_web_runner.utils.test_object.test_object_class import TestObject
from je_web_runner.utils.test_object.test_object_record.test_object_record_class import test_object_record
from je_web_runner.webdriver_manager.chrome import ChromeDriverManager
from je_web_runner.webdriver_manager.firefox import GeckoDriverManager
from je_web_runner.webdriver_manager.microsoft import EdgeChromiumDriverManager
from je_web_runner.webdriver_manager.microsoft import IEDriverManager
from je_web_runner.webdriver_manager.opera import OperaDriverManager
from je_web_runner.webdriver_manager.utils import ChromeType
from je_web_runner.utils.test_record.record_test_class import record_action_to_list

_webdriver_dict = {
    "chrome": webdriver.Chrome,
    "chromium": webdriver.Chrome,
    "firefox": webdriver.Firefox,
    "opera": webdriver.Opera,
    "edge": webdriver.Edge,
    "ie": webdriver.Ie,
    "safari": webdriver.Safari,
}

_webdriver_manager_dict = {
    "chrome": ChromeDriverManager,
    "chromium": ChromeDriverManager(chrome_type=ChromeType.CHROMIUM),
    "firefox": GeckoDriverManager,
    "opera": OperaDriverManager,
    "edge": EdgeChromiumDriverManager,
    "ie": IEDriverManager,
}

_webdriver_service_dict = {
    "chrome": webdriver.chrome.service.Service,
    "chromium": webdriver.chrome.service.Service,
    "firefox": webdriver.firefox.service.Service,
    "edge": webdriver.edge.service.Service,
    "ie": webdriver.ie.service.Service,
    "safari": webdriver.safari.service.Service,
}


class WebDriverWrapper(object):

    def __init__(self):
        self.current_webdriver: [WebDriver, None] = None
        self._webdriver_name: [str, None] = None
        self._action_chain: [ActionChains, None] = None

    # start a new webdriver

    def set_driver(self, webdriver_name: str, opera_path: str = None, **kwargs) -> \
            Union[
            webdriver.Chrome,
            webdriver.Chrome,
            webdriver.Firefox,
            webdriver.Opera,
            webdriver.Edge,
            webdriver.Ie,
            webdriver.Safari,
            ]:
        param = locals()
        try:
            webdriver_name = str(webdriver_name).lower()
            webdriver_value = _webdriver_dict.get(webdriver_name)
            if webdriver_value is None:
                raise WebDriverNotFoundException(selenium_wrapper_web_driver_not_found_error)
            webdriver_install_manager = _webdriver_manager_dict.get(webdriver_name)
            if webdriver_name in ["opera"]:
                if opera_path is None:
                    raise WebDriverException(selenium_wrapper_opera_path_error)
                opera_options = webdriver.ChromeOptions()
                opera_options.add_argument('allow-elevated-browser')
                opera_options.binary_location = opera_path
                self.current_webdriver = webdriver_value(
                    executable_path=_webdriver_manager_dict.get(webdriver_name)().install(), options=opera_options, **kwargs
                )
            else:
                webdriver_service = _webdriver_service_dict.get(webdriver_name)(
                    webdriver_install_manager().install(),
                )
                self.current_webdriver = webdriver_value(service=webdriver_service, **kwargs)
                self._webdriver_name = webdriver_name
                self._action_chain = ActionChains(self.current_webdriver)
                record_action_to_list("webdriver wrapper set_driver", param, None)
            return self.current_webdriver
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_driver", param, error)
            raise WebDriverException

    def set_webdriver_options_capability(self, key_and_vale_dict: dict) -> \
            Union[
            webdriver.Chrome,
            webdriver.Chrome,
            webdriver.Firefox,
            webdriver.Opera,
            webdriver.Edge,
            webdriver.Ie,
            webdriver.Safari,
            ]:
        param = locals()
        try:
            if self._webdriver_name is None:
                raise WebDriverIsNoneException(selenium_wrapper_web_driver_not_found_error)
            record_action_to_list("webdriver wrapper set_webdriver_options_capability", param, None)
            return set_webdriver_options_capability_wrapper(self._webdriver_name, key_and_vale_dict)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_webdriver_options_capability", param, error)
            raise WebDriverException

    # web element
    def find_element(self, test_object: TestObject) -> WebElement:
        param = locals()
        try:
            if self.current_webdriver is None:
                raise WebDriverIsNoneException(selenium_wrapper_web_driver_not_found_error)
            web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                test_object.test_object_type, test_object.test_object_name)
            return web_element_wrapper.current_web_element
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper find_element", param, error)

    def find_elements(self, test_object: TestObject) -> List[WebElement]:
        param = locals()
        try:
            if self.current_webdriver is None:
                raise WebDriverIsNoneException(selenium_wrapper_web_driver_not_found_error)
            web_element_wrapper.current_web_element_list = self.current_webdriver.find_elements(
                test_object.test_object_type, test_object.test_object_name)
            record_action_to_list("webdriver wrapper find_elements", param, None)
            return web_element_wrapper.current_web_element_list
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper find_elements", param, error)

    def find_element_with_test_object_record(self, element_name: str) -> WebElement:
        param = locals()
        try:
            if self.current_webdriver is None:
                raise WebDriverIsNoneException(selenium_wrapper_web_driver_not_found_error)
            web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            record_action_to_list("webdriver wrapper find_element_with_test_object_record", param, None)
            return web_element_wrapper.current_web_element
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper find_element_with_test_object_record", param, error)

    def find_elements_with_test_object_record(self, element_name: str) -> List[WebElement]:
        param = locals()
        try:
            if self.current_webdriver is None:
                raise WebDriverIsNoneException(selenium_wrapper_web_driver_not_found_error)
            web_element_wrapper.current_web_element_list = self.current_webdriver.find_elements(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            record_action_to_list("webdriver wrapper find_elements_with_test_object_record", param, None)
            return web_element_wrapper.current_web_element
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper find_elements_with_test_object_record", param, error)

    # wait
    def implicitly_wait(self, time_to_wait: int) -> None:
        param = locals()
        try:
            self.current_webdriver.implicitly_wait(time_to_wait)
            record_action_to_list("webdriver wrapper implicitly_wait", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper implicitly_wait", param, error)

    def explict_wait(self, wait_time: int, statement: bool, until_type: bool = True):
        param = locals()
        try:
            if until_type:
                record_action_to_list("webdriver wrapper explict_wait", param, None)
                return WebDriverWait(self.current_webdriver, wait_time).until(statement)
            else:
                record_action_to_list("webdriver wrapper explict_wait", param, None)
                return WebDriverWait(self.current_webdriver, wait_time).until_not(statement)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper explict_wait", param, error)

    # webdriver url redirect

    def to_url(self, url: str) -> None:
        param = locals()
        try:
            self.current_webdriver.get(url)
            record_action_to_list("webdriver wrapper to_url", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper to_url", param, error)

    def forward(self) -> None:
        try:
            self.current_webdriver.forward()
            record_action_to_list("webdriver wrapper forward", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper forward", None, error)

    def back(self) -> None:
        try:
            self.current_webdriver.back()
            record_action_to_list("webdriver wrapper back", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper back", None, error)

    def refresh(self) -> None:
        try:
            self.current_webdriver.refresh()
            record_action_to_list("webdriver wrapper refresh", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper refresh", None, error)

    # webdriver new page
    def switch(self, switch_type: str, switchy_target_name: str = None):
        param = locals()
        try:
            switch_type = switch_type.lower()
            switch_type_dict = {
                "active_element": self.current_webdriver.switch_to.active_element,
                "default_content": self.current_webdriver.switch_to.default_content,
                "frame": self.current_webdriver.switch_to.frame,
                "parent_frame": self.current_webdriver.switch_to.parent_frame,
                "window": self.current_webdriver.switch_to.window,
            }
            try:
                switch_type_dict.update(
                    {"alert": self.current_webdriver.switch_to.alert}
                )
            except NoAlertPresentException as error:
                switch_type_dict.update(
                    {"alert": None}
                )
            if switch_type in ["active_element", "alert"]:
                record_action_to_list("webdriver wrapper switch", param, None)
                return switch_type_dict.get(switch_type)
            elif switch_type in ["default_content", "parent_frame"]:
                record_action_to_list("webdriver wrapper switch", param, None)
                return switch_type_dict.get(switch_type)()
            else:
                record_action_to_list("webdriver wrapper switch", param, None)
                return switch_type_dict.get(switch_type)(switchy_target_name)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper switch", param, error)

    # timeout
    def set_script_timeout(self, time_to_wait) -> None:
        param = locals()
        try:
            self.current_webdriver.set_script_timeout(time_to_wait)
            record_action_to_list("webdriver wrapper set_script_timeout", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_script_timeout", param, error)

    def set_page_load_timeout(self, time_to_wait) -> None:
        param = locals()
        try:
            self.current_webdriver.set_page_load_timeout(time_to_wait)
            record_action_to_list("webdriver wrapper set_page_load_timeout", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_page_load_timeout", param, error)

    # cookie
    def get_cookies(self) -> List[dict]:
        try:
            record_action_to_list("webdriver wrapper get_cookies", None, None)
            return self.current_webdriver.get_cookies()
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_cookies", None, error)

    def get_cookie(self, name) -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper get_cookie", param, None)
            return self.current_webdriver.get_cookie(name)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_cookie", param, error)

    def add_cookie(self, cookie_dict: dict):
        param = locals()
        try:
            self.current_webdriver.add_cookie(cookie_dict)
            record_action_to_list("webdriver wrapper add_cookie", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper add_cookie", param, error)

    def delete_cookie(self, name) -> None:
        param = locals()
        try:
            self.current_webdriver.delete_cookie(name)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper delete_cookie", param, error)

    def delete_all_cookies(self) -> None:
        try:
            self.current_webdriver.delete_all_cookies()
            record_action_to_list("webdriver wrapper delete_all_cookies", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper delete_all_cookies", None, error)

    # exec selenium command
    def execute(self, driver_command: str, params: dict = None) -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper execute", param, None)
            return self.current_webdriver.execute(driver_command, params)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper execute", param, error)

    def execute_script(self, script, *args):
        param = locals()
        try:
            self.current_webdriver.execute_script(script, *args)
            record_action_to_list("webdriver wrapper execute_script", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper execute_script", param, error)

    def execute_async_script(self, script: str, *args):
        param = locals()
        try:
            self.current_webdriver.execute_async_script(script, *args)
            record_action_to_list("webdriver wrapper execute_async_script", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper execute_async_script", param, error)

    # ActionChains
    def move_to_element(self, targe_element: WebElement):
        param = locals()
        try:
            self._action_chain.move_to_element(targe_element)
            record_action_to_list("webdriver wrapper move_to_element", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper move_to_element", param, error)

    def move_to_element_with_test_object(self, element_name: str):
        param = locals()
        try:
            element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            self._action_chain.move_to_element(element)
            record_action_to_list("webdriver wrapper move_to_element_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper move_to_element_with_test_object", param, error)

    def move_to_element_with_offset(self, target_element: WebElement, x: int, y: int):
        param = locals()
        try:
            self._action_chain.move_to_element_with_offset(target_element, x, y)
            record_action_to_list("webdriver wrapper move_to_element_with_offset", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper move_to_element_with_offset", param, error)

    def move_to_element_with_offset_and_test_object(self, element_name: str, x: int, y: int):
        param = locals()
        try:
            element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            self._action_chain.move_to_element_with_offset(element, x, y)
            record_action_to_list("webdriver wrapper move_to_element_with_offset_and_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper move_to_element_with_offset_and_test_object", param, error)

    def drag_and_drop(self, web_element: WebElement, targe_element: WebElement):
        param = locals()
        try:
            self._action_chain.drag_and_drop(web_element, targe_element)
            record_action_to_list("webdriver wrapper drag_and_drop", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper drag_and_drop", param, error)

    def drag_and_drop_with_test_object(self, element_name: str, target_element_name: str):
        param = locals()
        try:
            element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            another_element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(target_element_name).test_object_type,
                test_object_record.test_object_record_dict.get(target_element_name).test_object_name
            )
            self._action_chain.drag_and_drop(element, another_element)
            record_action_to_list("webdriver wrapper drag_and_drop_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper drag_and_drop_with_test_object", param, error)

    def drag_and_drop_offset(self, web_element: WebElement, target_x: int, target_y: int):
        param = locals()
        try:
            self._action_chain.drag_and_drop_by_offset(web_element, target_x, target_y)
            record_action_to_list("webdriver wrapper drag_and_drop_offset", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper drag_and_drop_offset", param, error)

    def drag_and_drop_offset_with_test_object(self, element_name: str, target_x: int, target_y: int):
        param = locals()
        try:
            element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            self._action_chain.drag_and_drop_by_offset(element, target_x, target_y)
            record_action_to_list("webdriver wrapper drag_and_drop_offset_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper drag_and_drop_offset_with_test_object", param, error)

    def perform(self):
        try:
            self._action_chain.perform()
            record_action_to_list("webdriver wrapper perform", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper perform", None, error)

    def reset_actions(self):
        try:
            self._action_chain.reset_actions()
            record_action_to_list("webdriver wrapper reset_actions", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper reset_actions", None, error)

    def left_click(self, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.click(on_element)
            record_action_to_list("webdriver wrapper left_click", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_click", param, error)

    def left_click_with_test_object(self, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.click(None)
            else:
                element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.click(element)
                record_action_to_list("webdriver wrapper left_click_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_click_with_test_object", param, error)

    def left_click_and_hold(self, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.click_and_hold(on_element)
            record_action_to_list("webdriver wrapper left_click_and_hold", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_click_and_hold", param, error)

    def left_click_and_hold_with_test_object(self, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.click_and_hold(None)
            else:
                element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.click_and_hold(element)
            record_action_to_list("webdriver wrapper left_click_and_hold_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_click_and_hold_with_test_object", param, error)

    def right_click(self, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.context_click(on_element)
            record_action_to_list("webdriver wrapper right_click", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper right_click", param, error)

    def right_click_with_test_object(self, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.context_click(None)
            else:
                element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.context_click(element)
            record_action_to_list("webdriver wrapper right_click_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper right_click_with_test_object", param, error)

    def left_double_click(self, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.double_click(on_element)
            record_action_to_list("webdriver wrapper left_double_click", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_double_click", param, error)

    def left_double_click_with_test_object(self, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.double_click(None)
            else:
                web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.double_click(web_element_wrapper.current_web_element)
            record_action_to_list("webdriver wrapper left_double_click_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper left_double_click_with_test_object", param, error)

    def release(self, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.release(on_element)
            record_action_to_list("webdriver wrapper release", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper release", param, error)

    def release_with_test_object(self, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.release(None)
            else:
                web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.release(web_element_wrapper.current_web_element)
            record_action_to_list("webdriver wrapper release_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper release_with_test_object", param, error)

    def press_key(self, keycode_on_key_class, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.key_down(keycode_on_key_class, on_element)
            record_action_to_list("webdriver wrapper press_key", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper press_key", param, error)

    def press_key_with_test_object(self, keycode_on_key_class, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.key_down(keycode_on_key_class, None)
            else:
                web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.key_down(keycode_on_key_class, web_element_wrapper.current_web_element)
            record_action_to_list("webdriver wrapper press_key_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper press_key_with_test_object", param, error)

    def release_key(self, keycode_on_key_class, on_element: WebElement = None):
        param = locals()
        try:
            self._action_chain.key_up(keycode_on_key_class, on_element)
            record_action_to_list("webdriver wrapper release_key", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper release_key", param, error)

    def release_key_with_test_object(self, keycode_on_key_class, element_name: str = None):
        param = locals()
        try:
            if element_name is None:
                self._action_chain.key_up(keycode_on_key_class, None)
            else:
                web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                    test_object_record.test_object_record_dict.get(element_name).test_object_type,
                    test_object_record.test_object_record_dict.get(element_name).test_object_name
                )
                self._action_chain.key_up(keycode_on_key_class, web_element_wrapper.current_web_element)
            record_action_to_list("webdriver wrapper release_key_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper release_key_with_test_object", param, error)

    def move_by_offset(self, x: int, y: int):
        param = locals()
        try:
            self._action_chain.move_by_offset(x, y)
            record_action_to_list("webdriver wrapper move_by_offset", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper move_by_offset", param, error)

    def pause(self, seconds: int):
        param = locals()
        try:
            self._action_chain.pause(seconds)
            record_action_to_list("webdriver wrapper pause", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper pause", param, error)

    def send_keys(self, keys_to_send):
        param = locals()
        try:
            self._action_chain.send_keys(*keys_to_send)
            record_action_to_list("webdriver wrapper send_keys", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper send_keys", param, error)

    def send_keys_to_element(self, element: WebElement, keys_to_send):
        param = locals()
        try:
            self._action_chain.send_keys_to_element(element, *keys_to_send)
            record_action_to_list("webdriver wrapper send_keys_to_element", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper send_keys_to_element", param, error)

    def send_keys_to_element_with_test_object(self, element_name: str, keys_to_send):
        param = locals()
        try:
            web_element_wrapper.current_web_element = self.current_webdriver.find_element(
                test_object_record.test_object_record_dict.get(element_name).test_object_type,
                test_object_record.test_object_record_dict.get(element_name).test_object_name
            )
            self._action_chain.send_keys_to_element(web_element_wrapper.current_web_element, *keys_to_send)
            record_action_to_list("webdriver wrapper send_keys_to_element_with_test_object", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper send_keys_to_element_with_test_object", param, error)

    def scroll(self, x: int, y: int, delta_x: int, delta_y: int, duration: int = 0, origin: str = "viewport"):
        param = locals()
        try:
            self._action_chain.scroll(x, y, delta_x, delta_y, duration, origin)
            record_action_to_list("webdriver wrapper scroll", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper scroll", param, error)

    # window
    def maximize_window(self) -> None:
        try:
            self.current_webdriver.maximize_window()
            record_action_to_list("webdriver wrapper maximize_window", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper maximize_window", None, error)

    def fullscreen_window(self) -> None:
        try:
            self.current_webdriver.fullscreen_window()
            record_action_to_list("webdriver wrapper fullscreen_window", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper fullscreen_window", None, error)

    def minimize_window(self) -> None:
        try:
            self.current_webdriver.minimize_window()
            record_action_to_list("webdriver wrapper minimize_window", None, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper minimize_window", None, error)

    def set_window_size(self, width, height, window_handle='current') -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper set_window_size", param, None)
            return self.current_webdriver.set_window_size(width, height, window_handle)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_window_size", param, error)

    def set_window_position(self, x, y, window_handle='current') -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper set_window_position", param, None)
            return self.current_webdriver.set_window_position(x, y, window_handle)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_window_position", param, error)

    def get_window_position(self, window_handle='current') -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper get_window_position", param, None)
            return self.current_webdriver.get_window_position(window_handle)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_window_position", param, error)

    def get_window_rect(self) -> dict:
        try:
            record_action_to_list("webdriver wrapper get_window_position", None, None)
            return self.current_webdriver.get_window_rect()
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_window_position", None, error)

    def set_window_rect(self, x=None, y=None, width=None, height=None) -> dict:
        param = locals()
        try:
            record_action_to_list("webdriver wrapper set_window_rect", param, None)
            return self.current_webdriver.set_window_rect(x, y, width, height)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper set_window_rect", param, error)

    # save as file
    def get_screenshot_as_png(self) -> bytes:
        try:
            record_action_to_list("webdriver wrapper get_screenshot_as_png", None, None)
            return self.current_webdriver.get_screenshot_as_png()
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_screenshot_as_png", None, error)

    def get_screenshot_as_base64(self) -> str:
        try:
            record_action_to_list("webdriver wrapper get_screenshot_as_base64", None, None)
            return self.current_webdriver.get_screenshot_as_base64()
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_screenshot_as_base64", None, error)

    # log
    def get_log(self, log_type):
        try:
            record_action_to_list("webdriver wrapper get_log", None, None)
            return self.current_webdriver.get_log
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper get_log", None, error)

    # webdriver wrapper add function
    def check_current_webdriver(self, check_dict: dict):
        param = locals()
        try:
            check_webdriver(self.current_webdriver, check_dict)
            record_action_to_list("webdriver wrapper check_current_webdriver", param, None)
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper check_current_webdriver", param, error)

    # close event
    def quit(self):
        try:
            test_object_record.clean_record()
            self._action_chain = None
            record_action_to_list("webdriver wrapper quit", None, None)
            self.current_webdriver.quit()
        except Exception as error:
            print(repr(error), file=stderr)
            record_action_to_list("webdriver wrapper quit", None, error)
            raise WebDriverException


webdriver_wrapper = WebDriverWrapper()
