package io.appium.java_client.android;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import io.appium.java_client.functions.AppiumFunction;
import io.appium.java_client.functions.ExpectedCondition;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AndroidFunctionTest extends BaseAndroidTest {

    private AppiumFunction<WebDriver, List<WebElement>> searchingFunction = input -> {
        List<WebElement> result = input.findElements(By.tagName("a"));

        if (result.size() > 0) {
            return result;
        }
        return null;
    };

    private AppiumFunction<Pattern, WebDriver> contextFunction = input -> {
        Set<String> contexts = driver.getContextHandles();
        String current = driver.getContext();
        contexts.forEach(context -> {
            Matcher m = input.matcher(context);
            if (m.find()) {
                driver.context(context);
            }
        });
        if (!current.equals(driver.getContext())) {
            return driver;
        }
        return null;
    };

    private AppiumFunction<List<WebElement>, List<WebElement>> filteringFunction = input -> {
        final List<WebElement> result = new ArrayList<>();
        input.forEach(element -> {
            if (element.getText().equals("Hello World! - 1")) {
                result.add(element);
            }
        });

        if (result.size() > 0) {
            return result;
        }
        return null;
    };

    @Before public void setUp() {
        driver.startActivity("io.appium.android.apis", ".view.WebView1");
    }

    @Test public void complexWaitingTestWithPreCondition() {
        AppiumFunction<Pattern, List<WebElement>> compositeFunction =
                searchingFunction.compose(contextFunction);

        Wait<Pattern> wait = new FluentWait<>(Pattern.compile("WEBVIEW"))
                .withTimeout(30, TimeUnit.SECONDS);
        List<WebElement> elements = wait.until(compositeFunction);

        assertThat("Element size should be 1", elements.size(), is(1));
        assertThat("WebView is expected", driver.getContext(), containsString("WEBVIEW"));
    }

    @Test public void complexWaitingTestWithPostConditions() {
        final List<Boolean> calls = new ArrayList<>();

        AppiumFunction<Pattern, WebDriver> waitingForContext = input -> {
            WebDriver result = contextFunction.apply(input);
            if (result != null) {
                calls.add(true);
            }
            return result;
        };

        AppiumFunction<Pattern, List<WebElement>> compositeFunction = waitingForContext
                .andThen((ExpectedCondition<List<WebElement>>) input -> {
                    List<WebElement> result = searchingFunction.apply(input);
                    if (result != null) {
                        calls.add(true);
                    }
                    return result;
                })
                .andThen((AppiumFunction<List<WebElement>, List<WebElement>>) input -> {
                    List<WebElement> result = filteringFunction.apply(input);
                    if (result != null) {
                        calls.add(true);
                    }
                    return result;
                });

        Wait<Pattern> wait = new FluentWait<>(Pattern.compile("WEBVIEW"))
                .withTimeout(30, TimeUnit.SECONDS);
        List<WebElement> elements = wait.until(compositeFunction);
        assertThat("Element size should be 1", elements.size(), is(1));
        assertThat("WebView is expected", driver.getContext(), containsString("WEBVIEW"));
        assertThat("There should be 3 calls", calls.size(), is(3));
    }

    @Test(expected = TimeoutException.class) public void nullPointerExceptionSafetyTestWithPrecondition() {
        Wait<Pattern> wait = new FluentWait<>(Pattern.compile("Fake_context"))
                .withTimeout(30, TimeUnit.SECONDS).pollingEvery(500, TimeUnit.MILLISECONDS);
        assertTrue(wait.until(searchingFunction.compose(contextFunction)).size() > 0);
    }

    @Test(expected = TimeoutException.class) public void nullPointerExceptionSafetyTestWithPostConditions() {
        Wait<Pattern> wait = new FluentWait<>(Pattern.compile("Fake_context"))
                .withTimeout(30, TimeUnit.SECONDS).pollingEvery(500, TimeUnit.MILLISECONDS);
        assertTrue(wait.until(contextFunction.andThen(searchingFunction).andThen(filteringFunction)).size() > 0);
    }
}

@@ -360,16 +360,46 @@
 Before;%0A
+import org.junit.BeforeClass;%0A
 import o
@@ -2061,24 +2061,29 @@
  @Before
+Class
  public 
 void set
@@ -2078,93 +2078,256 @@
 lic 
-void setUp() %7B%0A        driver.startActivity(%22io.appium.android.apis%22, %22.view.WebView1
+static void startWebViewActivity() throws Exception  %7B%0A        if (driver != null) %7B%0A            driver.startActivity(%22io.appium.android.apis%22, %22.view.WebView1%22);%0A        %7D%0A    %7D%0A%0A    @Before public void setUp() %7B%0A        driver.context(%22NATIVE_APP
 %22);%0A

Test of AppiumFunction was optimized.