package cz.moneta.test.harness.endpoints;

import cz.moneta.test.harness.connectors.web.SeleniumChromeConnector;
import cz.moneta.test.harness.connectors.web.SeleniumEdgeConnector;
import cz.moneta.test.harness.connectors.web.SeleniumWebConnector;
import cz.moneta.test.harness.context.StoreAccessor;
import cz.moneta.test.harness.data.Browser;
import cz.moneta.test.harness.exception.HarnessConfigurationException;
import cz.moneta.test.harness.exception.HarnessException;
import cz.moneta.test.harness.support.web.Clickable;
import cz.moneta.test.harness.support.web.Key;
import cz.moneta.test.harness.support.web.Lookup;
import cz.moneta.test.harness.support.web.TextContainer;
import cz.moneta.test.harness.support.web.Until;
import cz.moneta.test.harness.support.web.Wait;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.platform.commons.util.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.logging.LogType;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WebEndpoint extends CommonEndpoint {

    private static final String BROWSER_CONFIG_KEY = "browser";
    private static final String WAIT_DEFAULT_CONFIG_KEY = "waitSecondsForElementDefault";
    private static final Logger logger = LogManager.getLogger(WebEndpoint.class);

    protected ThreadLocal<SeleniumWebConnector> connector;
    private String applicationBase;
    private Browser browser;
    private int waitDefaultFromConfig;

    public WebEndpoint(String endpointUrl, Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
        super(store);
        this.applicationBase = getApplicationUrlFromConfig(endpointUrl, store);
        this.browser = getBrowserByConfigName(store);
        this.waitDefaultFromConfig = getWaitDefaultFromConfig(store);
        initConnector(defaultLookup, store, extraOptions);
    }

    public WebEndpoint(String endpointUrl, Function<String, By> defaultLookup, StoreAccessor store, Browser defaultBrowser, String... extraOptions) {
        super(store);
        this.applicationBase = getApplicationUrlFromConfig(endpointUrl, store);
        this.browser = getBrowserWithCheckIgnoreDefaultParam(defaultBrowser, store);
        this.waitDefaultFromConfig = getWaitDefaultFromConfig(store);
        initConnector(defaultLookup, store, extraOptions);
    }

    private boolean ignoreDefaultBrowser(StoreAccessor store) {
        return Boolean.parseBoolean(store.getConfig("ignore-default-browser", "false"));
    }

    private void initConnector(Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
        Supplier<SeleniumWebConnector> connectorSupplier = getConnectorSupplier(defaultLookup, store, extraOptions);

        this.connector = ThreadLocal.withInitial(connectorSupplier);
        this.connector.set(connectorSupplier.get());
    }

    private String getApplicationUrlFromConfig(String endpointUrl, StoreAccessor store) {
        String applicationUrl = Optional.ofNullable(store.getConfig(endpointUrl))
                .orElseThrow(() ->
                        new IllegalStateException("You need to configure " + endpointUrl + " to work with application!"));
        logger.info("Successfully found endpoint URL: {}", () -> applicationUrl);
        return applicationUrl;
    }

    private Browser getBrowserWithCheckIgnoreDefaultParam(Browser defaultBrowser, StoreAccessor store) {
        if (ignoreDefaultBrowser(store)) {
            return getBrowserByConfigName(store);
        } else {
            logger.info("Default browser will be used. Browser in config file will be ignored!");
            return defaultBrowser;
        }
    }

    private Browser getBrowserByConfigName(StoreAccessor store) {
        String browserConfigName = System.getProperty(BROWSER_CONFIG_KEY);

        if (browserConfigName == null) {
            browserConfigName = store.getConfig(BROWSER_CONFIG_KEY);
        }

        return Browser.getBrowserByConfigName(browserConfigName);
    }

    private Supplier<SeleniumWebConnector> getConnectorSupplier(Function<String, By> defaultLookup, StoreAccessor store, String... extraOptions) {
        switch (browser) {
            case GOOGLE_CHROME:
                return () -> new SeleniumChromeConnector(defaultLookup, store, extraOptions);
            case MS_EDGE:
                return () -> new SeleniumEdgeConnector(defaultLookup, store);
            default:
                throw new HarnessConfigurationException("Unsupported browser defined in configuration.");
        }
    }

    public String getApplicationBase() {
        return this.applicationBase;
    }

    public void openApplication(String... pathParts) {
        connector.get().getDriver()
                .get(getApplicationBase() + concatenatePathParts(pathParts));
        maximizeBrowserWindow();
    }

    private String concatenatePathParts(String[] pathParts) {
        return Arrays.stream(pathParts)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.joining("/"));
    }

    public void maximizeBrowserWindow() {
        connector.get()
                .getDriver()
                .manage()
                .window()
                .maximize();
    }

    public void waitForElementsToLoad(int timeoutSeconds, Lookup lookup, Until until, String... elementsToCheck) {
        int timeout = getTimeoutValue(timeoutSeconds);
        connector.get().waits().waitForElements(timeout, lookup, until, elementsToCheck);
    }

    public void waitForElementsToLoad(int timeoutSeconds, String... elementsToCheck) {
        waitForElementsToLoad(timeoutSeconds, Lookup.DEFAULT, Until.PRESENT_IN_DOM, elementsToCheck);
    }

    public void waitForElementsToLoad(int timeoutSeconds, Lookup lookup, String... elementsToCheck) {
        waitForElementsToLoad(timeoutSeconds, lookup, Until.PRESENT_IN_DOM, elementsToCheck);
    }

    public void waitForElementsToLoad(int timeoutSeconds, Until until, String... elementsToCheck) {
        waitForElementsToLoad(timeoutSeconds, Lookup.DEFAULT, until, elementsToCheck);
    }

    public void waitForAnyElementToLoad(int timeoutSeconds, Lookup lookup, String... elementsToCheck) {
        connector.get().waits().waitForAtLeastOneElement(timeoutSeconds, lookup, elementsToCheck);
    }

    public void waitForAnyElementToLoad(int timeoutSeconds, String... elementsToCheck) {
        waitForAnyElementToLoad(timeoutSeconds, Lookup.DEFAULT, elementsToCheck);
    }

    public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath, Lookup waitElementLookup,
                                         Lookup refreshElementLookup, int timeoutSeconds, int pollingEverySeconds) {
        connector.get().waits().waitForElementAndRefresh(waitElementPath, refreshElementPath, waitElementLookup, refreshElementLookup,
                timeoutSeconds, pollingEverySeconds);
    }

    public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath, int timeoutSeconds,
                                         int pollingEverySeconds) {
        connector.get().waits().waitForElementAndRefresh(waitElementPath, refreshElementPath, Lookup.DEFAULT, Lookup.DEFAULT,
                timeoutSeconds, pollingEverySeconds);
    }

    public void waitForElementAndRefresh(String waitElementPath, String refreshElementPath) {
        connector.get().waits().waitForElementAndRefresh(waitElementPath, refreshElementPath, Lookup.DEFAULT, Lookup.DEFAULT,
                20, 1);
    }

    public void waitForElementAndRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction,
                                         int timeoutSeconds, int pollingEverySeconds) {
        connector.get().waits().waitForElementAndRefresh(waitElementPath, waitElementLookup, refreshFunction,
                timeoutSeconds, pollingEverySeconds, true);
    }

    public void waitForElementDisappearOrRefresh(String waitElementPath, Lookup waitElementLookup, Runnable refreshFunction,
                                                 int timeoutSeconds, int pollingEverySeconds) {
        connector.get().waits().waitForElementAndRefresh(waitElementPath, waitElementLookup, refreshFunction,
                timeoutSeconds, pollingEverySeconds, false);
    }

    public void waitOrFailOnErrorElement(int timeoutSeconds, String pathForWaiting, String pathToFail, Lookup lookup) {
        connector.get().waits().waitOrFailOnErrorElement(timeoutSeconds, pathForWaiting, pathToFail, lookup);
    }

    /**
     * Method for wait if all elements passed as parameter in defined state (Visible, Gone, Present in DOM) and there is not
     * visible fail element. Fail element is for example error screen.
     * <p>
     * Wait is implemented as fluent wait with 500ms polling and all parameters are transformed into expected conditions.
     * These conditions are used in fluent wait.
     * </p>
     * <p>
     * In case of fail element visible is thrown FailElementDisplayedException.
     * </p>
     *
     * @param timeoutSeconds  - wait timeout
     * @param lookup          - define locator strategy
     * @param until           - state to check - Visible, Gone, Present in DOM
     * @param xpathToFail     - xpath for fail element/error screen
     * @param elementsToCheck - elements to be wait for
     */
    public void waitOrFailOnErrorXpath(int timeoutSeconds, Lookup lookup, Until until, String xpathToFail, String... elementsToCheck) {
        int timeout = getTimeoutValue(timeoutSeconds);
        connector.get().waits().waitOrFailOnErrorXpath(timeout, lookup, until, xpathToFail, elementsToCheck);
    }

    public void dismissAlert(int timeout) {
        connector.get().alerts().dismissAlert(timeout);
    }

    public void acceptAlert(int timeout) {
        connector.get().alerts().acceptAlert(timeout);
    }

    public String getAlertText(int timeout) {
        return connector.get().alerts().getAlertText(timeout);
    }

    /**
     * This method is used for get information about alert is present on open web application
     *
     * @param timeout The maximum wait to present alert
     * @return boolean value for existing alert
     */
    public boolean isAlertPresent(int timeout) {
        return connector.get().alerts().isAlertPresent(timeout);
    }

    public void click(Clickable clickable, Lookup lookup) {
        connector.get().clicks().click(clickable, lookup);
    }

    public void click(Clickable clickable) {
        click(clickable, Lookup.DEFAULT);
    }

    public void doubleClick(Clickable clickable, Lookup lookup) {
        connector.get().clicks().doubleClick(clickable, lookup);
    }

    public void jsDoubleClick(Clickable clickable, Lookup lookup) {
        connector.get().clicks().jsDoubleClick(clickable, lookup);
    }

    public void jsClick(Clickable clickable, Lookup lookup) {
        connector.get().clicks().jsClick(clickable, lookup);
    }

    public void jsClick(Clickable clickable) {
        jsClick(clickable, Lookup.DEFAULT);
    }

    public void type(TextContainer input, String text, boolean clear, Lookup lookup) {
        connector.get().types().type(input, text, clear, lookup);
    }

    public void type(TextContainer input, String text, boolean clear) {
        type(input, text, clear, Lookup.DEFAULT);
    }

    public void jsType(TextContainer input, String text, boolean clear, Lookup lookup) {
        connector.get().types().jsType(input, text, clear, lookup);
    }

    public void jsType(TextContainer input, String text, boolean clear) {
        jsType(input, text, clear, Lookup.DEFAULT);
    }

    public void typeWithControlDown(String input, String keys, Lookup lookup) {
        connector.get().types().typeWithControlDown(input, keys, lookup);
    }

    public void typeWithControlDown(String input, String keys) {
        typeWithControlDown(input, keys, Lookup.DEFAULT);
    }

    public void isAtLeastOneElementPresent(String path, Lookup lookup) {
        connector.get().elementsChecks().isAtLeastOneElementPresent(path, lookup);
    }

    public void isAtLeastOneElementPresent(String path) {
        isAtLeastOneElementPresent(path, Lookup.DEFAULT);
    }

    public boolean isElementPresent(String path, Lookup lookup) {
        return connector.get().elementsChecks().isElementPresent(path, lookup);
    }

    public boolean isElementEnabled(String path) {
        return isElementEnabled(path, Lookup.DEFAULT);
    }

    public boolean isElementEnabled(String path, Lookup lookup) {
        return connector.get().elementsChecks().isElementEnabled(path, lookup);
    }

    public void checkElementContent(String id, String content, Lookup lookup) {
        connector.get().elementsChecks().checkElementContent(id, content, lookup);
    }

    public void checkElementContent(String id, String content) {
        checkElementContent(id, content, Lookup.DEFAULT);
    }

    public Object executeScript(String script, Object... args) {
        return connector.get().scripts().executeScript(script, args);
    }

    public String getCookie(String name) {
        return connector.get().cookies().getCookie(name);
    }

    /**
     * Creating a cookie for set values.
     *
     * @param name       The cookie's name Set for default value.
     * @param value      The cookie's value.
     * @param domain     The domain the cookie is visible to. If set to null, will be se to your default domain.
     * @param path       The path the cookie is visible to. If set to null, will be set to "/".
     * @param expiry     The cookie's expiration date. You may be set to null.
     * @param isSecure   You must set whether this cookie requires a secure connection.
     * @param isHttpOnly You must set whether this cookie is a httpOnly.
     */
    public void addCookies(String name, String value, String domain, String path, Date expiry, boolean isSecure, boolean isHttpOnly) {
        connector.get().cookies().addCookie(name, value, domain, path, expiry, isSecure, isHttpOnly);
    }

    // Create a cookie for given name and value with default values
    public void addCookies(String name, String value) {
        connector.get().cookies().addCookie(name, value, null, null, null, true, false);
    }

    public void addCookies(Cookie cookie) {
        connector.get().cookies().addCookie(cookie);
    }

    public void selectByVisibleText(String path, String text, Lookup lookup) {
        connector.get().selects().selectByVisibleText(path, text, lookup);
    }

    public void selectByVisibleText(String path, String text) {
        selectByVisibleText(path, text, Lookup.DEFAULT);
    }


    public void selectByValue(String path, String value, Lookup lookup) {
        connector.get().selects().selectByValue(path, value, lookup);
    }

    public void selectByValue(String path, String value) {
        selectByValue(path, value, Lookup.DEFAULT);
    }

    public void selectByOptionOrder(String path, int optionOrder, Lookup lookup) {
        connector.get().selects().selectByOptionOrder(path, optionOrder, lookup);
    }

    public void selectByOptionOrder(String path, int optionOrder) {
        connector.get().selects().selectByOptionOrder(path, optionOrder, Lookup.DEFAULT);
    }

    /**
     * Select option by text part of option value
     * <p>
     * In some cases is necessary to select option only by part of value in option. This method accepts path to element
     * lookup strategy value and list of text parts of options.
     * </p>
     *
     * @param path             - path to select element
     * @param textOptionsParts - list of option text parts to look for
     */
    public void selectByContainingTexts(String path, List<String> textOptionsParts) {
        selectByContainingTexts(path, Lookup.DEFAULT, textOptionsParts);
    }

    /**
     * Select option by text part of option value
     * <p>
     * In some cases is necessary to select option only by part of value in option. This method accepts path to element
     * lookup strategy value and list of text parts of options.
     * </p>
     *
     * @param path             - path to select element
     * @param lookup           - lookup strategy for select element
     * @param textOptionsParts - list of option text parts to look for
     */
    public void selectByContainingTexts(String path, Lookup lookup, List<String> textOptionsParts) {
        connector.get().selects().selectByContainingTexts(path, lookup, textOptionsParts);
    }

    public String getSelectedFirstItemText(String path, Lookup lookup) {
        return connector.get().selects().getFirstSelectedOptionText(path, lookup);
    }

    public String getSelectedFirstItemText(String path) {
        return getSelectedFirstItemText(path, Lookup.DEFAULT);
    }

    public String getText(String path, Lookup lookup) {
        return connector.get().elementsActions().getText(path, lookup);
    }

    public String getText(String path) {
        return getText(path, Lookup.DEFAULT);
    }

    public void switchToFrame(String frame, Lookup lookup) {
        connector.get().frames().switchToFrame(frame, lookup);
    }

    public void switchToFrame(String frame) {
        switchToFrame(frame, Lookup.DEFAULT);
    }

    public void switchToDefaultContent() {
        connector.get().navigations().switchToDefaultContent();
    }

    public void changePath(String path) {
        connector.get().navigations().navigateTo(applicationBase + path);
    }

    public void changeUrl(String url) {
        connector.get().navigations().navigateTo(url);
    }

    public void navigateBack() {
        connector.get().navigations().navigateBack();
    }

    public void navigateForward() {
        connector.get().navigations().navigateForward();
    }

    /**
     * This method is used for refreshing page which is currently open in webdriver
     */
    public void refeshPage() {
        connector.get().navigations().navigateRefresh();
    }

    public String takeSnapshot(String prefix) {
        return connector.get().driverUtils().takeSnapshot(getFileName(prefix, ".png"));
    }

    public Set<String> captureLogs(String prefix) {
        return Stream.of(LogType.BROWSER, LogType.CLIENT, LogType.DRIVER, LogType.PERFORMANCE, /*LogType.PROFILER,*/ LogType.SERVER)
                .map(logType -> connector.get().driverUtils().captureLog(getFileName(String.format("%s.%s", prefix, logType)), logType))
                .collect(Collectors.toSet());
    }

    public void captureDom(String prefix) {
        connector.get().driverUtils().captureDom(getFileName(prefix, ".dom.dump.html"));
    }

    protected String getFileName(String prefix) {
        return getFileName(prefix, null);
    }

    protected String getFileName(String prefix, String suffix) {
        StringBuilder sb = new StringBuilder();
        if (prefix != null) {
            sb.append(prefix);
            sb.append("-");
        }
        sb.append(getClass().getSimpleName());
        sb.append("-");
        sb.append(System.currentTimeMillis());
        if (suffix != null) {
            sb.append(suffix);
        }
        return sb.toString();
    }

    public String getCurrentUrl() {
        return connector.get().getCurrentUrl();
    }

    @Override
    public void close() {
        connector.get().close();
    }

    public void closeCurrentWindow() {
        connector.get().windows().closeCurrentWindow();
    }

    public void sleepSeconds(int seconds) {
        sleep(TimeUnit.SECONDS, seconds);
    }

    public void sleep(TimeUnit timeUnit, long timeout) {
        try {
            timeUnit.sleep(timeout);
        } catch (InterruptedException e) {
            logger.warn("Sleep throws InterruptedException!", e);
            Thread.currentThread().interrupt();
        }
    }

    public void switchToOtherWindow(int timeout, int expectedNumberOfWindows) {
        connector.get().windows().switchToOtherWindow(timeout, expectedNumberOfWindows);
    }

    public void switchToWindowByTitle(String windowTitle) {
        connector.get().windows().switchToWindowByTitle(windowTitle);
    }

    public void switchToActiveElement() {
        connector.get().getDriver().switchTo().activeElement();
    }

    public void closePopup(int closeWindowTimeout) {
        closeCurrentWindow();
        switchToOtherWindow(closeWindowTimeout, 1);
    }

    public void focusToNewPopup(int openWindowTimeout) {
        switchToOtherWindow(openWindowTimeout, 2);
    }

    public boolean isHeadlessModeOn() {
        return executeScript("return navigator.userAgent").toString().contains("Headless");
    }

    /**
     * FIXME: Currently doesn't work. Workarounded by argument --kiosk-printing
     * Watch https://github.com/MicrosoftEdge/EdgeWebDriver/issues/115
     */
    public void dismissEdgePrintDialog() {
        if (browser != Browser.MS_EDGE)
            throw new HarnessException("Method dismissEdgePrintDialog is designed only for browser MS Edge");

        if (isHeadlessModeOn()) {
            if (isAlertPresent(30)) {
                dismissAlert(1);
            }

        } else {
            String originalWindow = connector.get().getDriver().getWindowHandle();

            try {
                // Switch to Print dialog and close it
                // FIXME: this loop stuck on getTitle method
                /*for (String winHandle : connector.get().getDriver().getWindowHandles()) {
                String title = connector.get().getDriver().switchTo().window(winHandle).getTitle();
                if (title.matches("^[0-9a-fA-F-]{36}")) {// print dialog has generated title, example: fe0c496e-d557-4460-a360-f0e2555265f8
                    connector.get().getDriver().findElement(By.xpath("//button[span[text()='Cancel']]")).click();
                }
                }*/

                // This will work only with 2 windows opened
                switchToOtherWindow(30, 2);
                String btnCancelXpath = "//button[span[text()='Cancel']]";
                waitForElementsToLoad(30, Lookup.XPATH, btnCancelXpath);
                connector.get().getDriver().findElement(By.xpath(btnCancelXpath)).click();
            } catch (Exception e) {
                throw (e);
            } finally {
                connector.get().getDriver().switchTo().window(originalWindow);
            }
        }
    }

    public void switchWindowResolution(int width, int height) {
        connector.get().windows().switchWindowResolution(width, height);
    }

    public void moveToElement(String path, Lookup lookup) {
        connector.get().elementsActions().moveToElement(path, lookup);
    }

    public void moveToElement(String path) {
        moveToElement(path, Lookup.DEFAULT);
    }

    public void moveToElementAndClickToOtherElement(String hoverElementPath, String clickElementPath, Lookup lookup) {
        connector.get().elementsActions().moveToElementAndClick(hoverElementPath, clickElementPath, lookup);
    }

    public void moveToElementAndClickToOtherElement(String hoverElementPath, String clickElementPath) {
        moveToElementAndClickToOtherElement(hoverElementPath, clickElementPath, Lookup.DEFAULT);
    }

    /**
     * @deprecated use sendKeysOneAtATime instead of this method
     */
    @Deprecated
    public void scrollToPageBottom() {
        sendKeysOneAtATime(Key.END);
    }

    /**
     * @deprecated use sendKeysOneAtATime instead of this method
     */
    @Deprecated
    public void scrollToPageTop() {
        sendKeysOneAtATime(Key.HOME);
    }

    public void sendKeysOneAtATime(Key... keys) {
        connector.get().sendKeys().sendKeysOneAtATime(keys);
    }

    public void sendKeysAsChord(Key... keys) {
        connector.get().sendKeys().sendKeysAsChord(keys);
    }

    public boolean isElementVisible(String path, Lookup lookup) {
        return connector.get().elementsActions().isElementVisible(path, lookup);
    }

    public boolean isElementVisible(String path) {
        return isElementVisible(path, Lookup.DEFAULT);
    }

    public boolean isElementVisible(int timeout, String path, Lookup lookup) {
        return connector.get().elementsActions().isElementVisible(timeout, path, lookup);
    }

    public void dragAndDrop(String source, String target, Lookup lookup) {
        connector.get().elementsActions().dragAndDropElement(source, target, lookup);
    }

    public void dragAndDrop(String source, String target) {
        dragAndDrop(source, target, Lookup.DEFAULT);
    }

    public void dragAndDropBy(String source, int xOffset, int yOffset, Lookup lookup) {
        connector.get().elementsActions().dragAndDropBy(source, xOffset, yOffset, lookup);
    }

    public void dragAndDropBy(String source, int xOffset, int yOffset) {
        dragAndDropBy(source, xOffset, yOffset, Lookup.DEFAULT);
    }

    public void scrollIntoView(String path, Lookup lookup) {
        connector.get().elementsActions().scrollIntoView(path, lookup);
    }

    public void scrollIntoView(String path) {
        connector.get().elementsActions().scrollIntoView(path, Lookup.DEFAULT);
    }

    public String getAttribute(String path, Lookup lookup, String attributeName) {
        return connector.get().getAttribute(path, lookup, attributeName);
    }

    public String getAttribute(String path, String attributeName) {
        return getAttribute(path, Lookup.DEFAULT, attributeName);
    }

    public Browser getBrowser() {
        return browser;
    }

    /**
     * This method is used for downloading file in Internet Explorer. It is not possible to turn off IE download prompt.
     * It is necessary to specify wait time for downloading. Download in IE is not possible handle via Selenium and for this
     * reason is used sleep instead of timeout.
     * <p>
     * This method handles standard prompt downloading for Internet Explorer on Selenium Grid.
     * </p>
     * <p>
     * Not implemented for Chrome - download handling is possible via capabilities.
     * </p>
     *
     * @param waitInSeconds - time for sleep in process
     */
    public void downloadFileViaIePrompt(int waitInSeconds) {
        connector.get().downloadFileViaIePrompt(waitInSeconds);
    }

    private int getWaitDefaultFromConfig(StoreAccessor store) {
        String waitForElementDefault = System.getProperty(WAIT_DEFAULT_CONFIG_KEY);

        if (waitForElementDefault == null) {
            waitForElementDefault = store.getConfig(WAIT_DEFAULT_CONFIG_KEY, "0");
        }
        return Integer.parseInt(waitForElementDefault);
    }

    private int getWaitDefaultFromMethod() {
        try {
            return (Integer) Wait.class.getDeclaredMethod("waitSecondsForElement").getDefaultValue();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return 0;
    }

    private int getTimeoutValue(int timeoutSeconds) {
        int timeout = timeoutSeconds;
        if (waitDefaultFromConfig != 0 && timeoutSeconds == getWaitDefaultFromMethod()) {
            timeout = waitDefaultFromConfig;
        }

        return timeout;
    }

    public List<WebElement> findAndGetListOfElements(String path, Lookup lookup) {
        return connector.get().elementsActions().findElements(path, lookup);
    }

    public String getNodeHost() {
        return connector.get().getNodeHost();
    }

    /**
     * Enables to check if specified file exists in download directory.
     * <p>
     * Can be called after file is downloaded. It checks if specified file exists in target download directory.
     * If not specified then download directory is taken from config parameter selenium.download.directory, ex. selenium.download.directory=\\\\localhost\\c$\\Temp
     * Usually it takes some time to download a file therefore use timeoutInSeconds as pause before checking for file existence.
     * By default if file is found it is also deleted from disk.
     * <p>
     * There are two methods for detecting the existence of file.
     * The first is to use the File.exists() method - it works in large folders, but sometimes it returns true only after a long time the file exists.
     * The second is by browsing the list of files in the directory, it is faster but not suitable for directories with a large count of files.
     * </p>
     *
     * @param filename               - name of file to check if exists
     * @param waitInSeconds          - pause before checking for file existence
     * @param directory              - if not defined you must set default directory through config parameter selenium.download.directory
     * @param deleteFile             - set to false if you do not want to delete checked file
     * @param useDirectoryListMethod - set to true if you want to use directory list search method instead of File.exists()
     */
    public Boolean checkIfFileExists(String filename, int waitInSeconds, String directory, boolean deleteFile, boolean useDirectoryListMethod) {
        return connector.get().driverUtils().checkIfFileExists(directory, filename, waitInSeconds, deleteFile, useDirectoryListMethod);
    }

    public Boolean checkIfFileExists(String filename, int waitInSeconds, String directory, boolean deleteFile) {
        return checkIfFileExists(filename, waitInSeconds, directory, deleteFile, false);
    }

    public Boolean checkIfFileExists(String filename, int waitInSeconds, String directory) {
        return checkIfFileExists(filename, waitInSeconds, directory, true);
    }

    public Boolean checkIfFileExists(String filename, int waitInSeconds) {
        return checkIfFileExists(filename, waitInSeconds, null, true);
    }
}