// Path: src/main/java/io/qameta/htmlelements/context/Context.java
// public class Context {
// 
//     public static final String DESCRIPTION_KEY = "description";
// 
//     public static final String LISTENERS_KEY = "listeners";
// 
//     public static final String PROPERTIES_KEY = "properties";
// 
//     public static final String PARAMETERS_KEY = "parameters";
// 
//     public static final String DRIVER_KEY = "driver";
// 
//     private Context parent;
// 
//     private Store store;
// 
//     private ExtensionRegistry registry;
// 
//     private Context() {
//         this.store = new DefaultStore();
//     }
// 
//     public Optional<Context> getParent() {
//         return Optional.ofNullable(parent);
//     }
// 
//     private void setParent(Context parent) {
//         this.parent = parent;
//     }
// 
//     public Store getStore() {
//         return store;
//     }
// 
//     public ExtensionRegistry getRegistry() {
//         return registry;
//     }
// 
//     private void setRegistry(ExtensionRegistry registry) {
//         this.registry = registry;
//     }
// 
//     public Context newChildContext(Method method, Class<?> proxyClass) {
//         Context childContext = new Context();
//         childContext.getStore().put(DESCRIPTION_KEY, ReflectionUtils.getDescription(method, null));
//         childContext.setRegistry(ExtensionRegistry.create(proxyClass));
//         childContext.getRegistry().registerExtensions(method);
//         childContext.setParent(this);
//         //extension
//         getStore().get(PROPERTIES_KEY, Properties.class).ifPresent(properties -> {
//             Properties child = new Properties();
//             child.putAll(properties);
//             childContext.getStore().put(PROPERTIES_KEY, child);
//         });
//         getStore().get(PARAMETERS_KEY, Properties.class).ifPresent(properties -> {
//             Properties child = new Properties();
//             child.putAll(properties);
//             childContext.getStore().put(PARAMETERS_KEY, child);
//         });
//         getStore().get(LISTENERS_KEY, List.class).ifPresent(listeners -> {
//             childContext.getStore().put(LISTENERS_KEY, listeners);
//         });
//         getStore().get(DRIVER_KEY, WebDriver.class).ifPresent(driver -> {
//             childContext.getStore().put(DRIVER_KEY, driver);
//         });
//         return childContext;
//     }
// 
//     public static Context newWebPageContext(Class<?> webPageClass, WebDriver driver) {
//         Context context = new Context();
//         context.setRegistry(ExtensionRegistry.create(webPageClass));
//         context.getStore().put(DESCRIPTION_KEY, ReflectionUtils.getDescription(webPageClass));
//         context.getStore().put(DRIVER_KEY, driver);
//         return context;
//     }
// 
// 
// }
// 
// Path: src/main/java/io/qameta/htmlelements/exception/WebPageException.java
// public class WebPageException extends RuntimeException {
// 
//     public WebPageException(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/io/qameta/htmlelements/extension/MethodHandler.java
// public interface MethodHandler<R> {
// 
//     R handle(Context context, Object proxy, Method method, Object[] args) throws Throwable;
// 
// }
// 
// Path: src/main/java/io/qameta/htmlelements/util/WebDriverUtils.java
// public class WebDriverUtils {
// 
//     public static boolean pageIsLoaded(WebDriver webDriver) {
//         if (webDriver instanceof JavascriptExecutor) {
//                 Object result = ((JavascriptExecutor) webDriver)
//                         .executeScript("if (document.readyState) return document.readyState;");
//                 return result != null && "complete".equals(result);
//         } else {
//             throw new WebPageException("Driver must support javascript execution");
//         }
//     }
// }


import com.google.common.base.Predicate;
import io.qameta.htmlelements.context.Context;
import io.qameta.htmlelements.exception.WebPageException;
import io.qameta.htmlelements.extension.HandleWith;
import io.qameta.htmlelements.extension.MethodHandler;
import io.qameta.htmlelements.util.WebDriverUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import static io.qameta.htmlelements.context.Store.BASE_URL_KEY;
import static io.qameta.htmlelements.context.Store.DRIVER_KEY;
package io.qameta.htmlelements.extension.page;




/**
 * @author ehborisov
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@HandleWith(GoMethod.Extension.class)
public @interface GoMethod {

    class Extension implements MethodHandler {

        @Override
        public Object handle(Context context, Object proxy, Method method, Object[] args) throws Throwable {
            String url = (String) context.getStore().get(BASE_URL_KEY)
                    .orElseThrow(() -> new WebPageException("BaseUrl annotation is not declared for this web page"));
            WebDriver driver = context.getStore().get(DRIVER_KEY, WebDriver.class)
                    .orElseThrow(() -> new WebPageException("WebDriver is missing"));
            driver.get(url);

            new WebDriverWait(driver, 5)
                    .ignoring(Throwable.class)
                    .withMessage(String.format("Couldn't wait for page with url %s to load", url))
                    .until((Predicate<WebDriver>) (d) -> (d != null && d.getCurrentUrl().equals(url)) &&
// Path: src/main/java/io/qameta/htmlelements/context/Context.java
// public class Context {
// 
//     public static final String DESCRIPTION_KEY = "description";
// 
//     public static final String LISTENERS_KEY = "listeners";
// 
//     public static final String PROPERTIES_KEY = "properties";
// 
//     public static final String PARAMETERS_KEY = "parameters";
// 
//     public static final String DRIVER_KEY = "driver";
// 
//     private Context parent;
// 
//     private Store store;
// 
//     private ExtensionRegistry registry;
// 
//     private Context() {
//         this.store = new DefaultStore();
//     }
// 
//     public Optional<Context> getParent() {
//         return Optional.ofNullable(parent);
//     }
// 
//     private void setParent(Context parent) {
//         this.parent = parent;
//     }
// 
//     public Store getStore() {
//         return store;
//     }
// 
//     public ExtensionRegistry getRegistry() {
//         return registry;
//     }
// 
//     private void setRegistry(ExtensionRegistry registry) {
//         this.registry = registry;
//     }
// 
//     public Context newChildContext(Method method, Class<?> proxyClass) {
//         Context childContext = new Context();
//         childContext.getStore().put(DESCRIPTION_KEY, ReflectionUtils.getDescription(method, null));
//         childContext.setRegistry(ExtensionRegistry.create(proxyClass));
//         childContext.getRegistry().registerExtensions(method);
//         childContext.setParent(this);
//         //extension
//         getStore().get(PROPERTIES_KEY, Properties.class).ifPresent(properties -> {
//             Properties child = new Properties();
//             child.putAll(properties);
//             childContext.getStore().put(PROPERTIES_KEY, child);
//         });
//         getStore().get(PARAMETERS_KEY, Properties.class).ifPresent(properties -> {
//             Properties child = new Properties();
//             child.putAll(properties);
//             childContext.getStore().put(PARAMETERS_KEY, child);
//         });
//         getStore().get(LISTENERS_KEY, List.class).ifPresent(listeners -> {
//             childContext.getStore().put(LISTENERS_KEY, listeners);
//         });
//         getStore().get(DRIVER_KEY, WebDriver.class).ifPresent(driver -> {
//             childContext.getStore().put(DRIVER_KEY, driver);
//         });
//         return childContext;
//     }
// 
//     public static Context newWebPageContext(Class<?> webPageClass, WebDriver driver) {
//         Context context = new Context();
//         context.setRegistry(ExtensionRegistry.create(webPageClass));
//         context.getStore().put(DESCRIPTION_KEY, ReflectionUtils.getDescription(webPageClass));
//         context.getStore().put(DRIVER_KEY, driver);
//         return context;
//     }
// 
// 
// }
// 
// Path: src/main/java/io/qameta/htmlelements/exception/WebPageException.java
// public class WebPageException extends RuntimeException {
// 
//     public WebPageException(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/io/qameta/htmlelements/extension/MethodHandler.java
// public interface MethodHandler<R> {
// 
//     R handle(Context context, Object proxy, Method method, Object[] args) throws Throwable;
// 
// }
// 
// Path: src/main/java/io/qameta/htmlelements/util/WebDriverUtils.java
// public class WebDriverUtils {
// 
//     public static boolean pageIsLoaded(WebDriver webDriver) {
//         if (webDriver instanceof JavascriptExecutor) {
//                 Object result = ((JavascriptExecutor) webDriver)
//                         .executeScript("if (document.readyState) return document.readyState;");
//                 return result != null && "complete".equals(result);
//         } else {
//             throw new WebPageException("Driver must support javascript execution");
//         }
//     }
// }


// Path: src/main/java/io/qameta/htmlelements/extension/page/GoMethod.java
import com.google.common.base.Predicate;
import io.qameta.htmlelements.context.Context;
import io.qameta.htmlelements.exception.WebPageException;
import io.qameta.htmlelements.extension.HandleWith;
import io.qameta.htmlelements.extension.MethodHandler;
import io.qameta.htmlelements.util.WebDriverUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import static io.qameta.htmlelements.context.Store.BASE_URL_KEY;
import static io.qameta.htmlelements.context.Store.DRIVER_KEY;

package io.qameta.htmlelements.extension.page;




/**
 * @author ehborisov
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@HandleWith(GoMethod.Extension.class)
public @interface GoMethod {

    class Extension implements MethodHandler {

        @Override
        public Object handle(Context context, Object proxy, Method method, Object[] args) throws Throwable {
            String url = (String) context.getStore().get(BASE_URL_KEY)
                    .orElseThrow(() -> new WebPageException("BaseUrl annotation is not declared for this web page"));
            WebDriver driver = context.getStore().get(DRIVER_KEY, WebDriver.class)
                    .orElseThrow(() -> new WebPageException("WebDriver is missing"));
            driver.get(url);

            new WebDriverWait(driver, 5)
                    .ignoring(Throwable.class)
                    .withMessage(String.format("Couldn't wait for page with url %s to load", url))
                    .until((Predicate<WebDriver>) (d) -> (d != null && d.getCurrentUrl().equals(url)) &&
                            WebDriverUtils.pageIsLoaded(d));