package com.seleniumsimplified.webdriver;

import java.util.List;


import org.junit.*;
import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.google.common.base.Predicate;

import static org.openqa.selenium.support.ui.ExpectedConditions.*;

public class Section18_Lecture120_WebDriverWaitCustomExpectedConditions {
	
	private static WebDriver driver;
	private WebDriverWait wait;
	private String basicAjaxSite = "http://www.compendiumdev.co.uk/selenium/basic_ajax.html";
	
	@BeforeClass
	public static void setUp(){
		FirefoxProfile profile = new FirefoxProfile();
		profile.setPreference("network.proxy.type", 0);
		driver = new FirefoxDriver(profile);
	}
	
	@Before
	public void navigateToSite(){
		driver.get(basicAjaxSite);
		
		wait = new WebDriverWait(driver, 10, 50);
	}


    @Test
    public void doEverythingALot(){

        for(int x = 0; x <100; x++){
            System.out.println("loop : " + x);
            System.out.println("customExpectedConditionUsingAClass");
            navigateToSite();
            customExpectedConditionUsingAClass();
            System.out.println("customExpectedConditionUsingAnonymousClassDirectly");
            navigateToSite();
            customExpectedConditionUsingAnonymousClassDirectly();
            System.out.println("customExpectedConditionUsingMethodThatWrapsTheExpectedCondition");
            navigateToSite();
            customExpectedConditionUsingMethodThatWrapsTheExpectedCondition();
        }

    }


	@Test
	public void customExpectedConditionUsingAClass(){
		//selects Server in the first combo box
		selectServerInCombo1();
		
		//custom wait using a named class
		wait.until(new SelectContainsText(By.id("combo2"), "Java"));
		//new WebDriverWait(driver,10,50).until(new SelectContainsText(By.id("combo2"), "Java"));
		
		//select Java in the second combo box and then clicks submit and checks the correct id on the resulting page
		selectJavaInCombo2SubmitAndCheckPageShowsJavaSelected();
	}
	
	@Test
	public void customExpectedConditionUsingAnonymousClassDirectly(){
		//selects Server in the first combo box
		selectServerInCombo1();
		
		//custom wait which returns an object that implements the ExpectedCondition interface
		//again have to override apply method
		//can't use a constructor because its an anonymous class
		wait.until( new ExpectedCondition<Boolean>(){
	
				public Boolean apply(WebDriver driver){
					return driver.findElement(By.cssSelector("option[value='23']")).isDisplayed();
			}
		});
		
		//select Java in the second combo box and then clicks submit and checks the correct id on the resulting page
		selectJavaInCombo2SubmitAndCheckPageShowsJavaSelected();
	}
	
	
	@Test
	public void customExpectedConditionUsingMethodThatWrapsTheExpectedCondition(){
		selectServerInCombo1();
		
		//custom wait using a named class
		WebElement javaOption = wait.until(optionWithJavaValueDisplayed("23"));
		
		javaOption.click();
		
		selectJavaInCombo2SubmitAndCheckPageShowsJavaSelected();
	}
	
	//this method must return an object which implements ExpectedCondition interface
	//with the method a new ExpectedCondition object is instantiated and returned
	private ExpectedCondition<WebElement> optionWithJavaValueDisplayed(final String value) {
				
		 return new ExpectedCondition<WebElement>(){
			public WebElement apply(WebDriver driver){
				return driver.findElement(By.cssSelector("option[value='" +value+ "']" ));
			}
			
		};
	}


	private class SelectContainsText implements ExpectedCondition<Boolean>{
		private String textToFind;
		private By findBy;
		
		public SelectContainsText(final By comboFindBy, final String textToFind){
			this.findBy = comboFindBy;
			this.textToFind = textToFind;
			
		}

		public Boolean apply(WebDriver driver) {
			WebElement comboBox2 = driver.findElement(this.findBy);

            /* apply with xpath - no probs with stale element
            WebElement comboWithText = comboBox2.findElement(By.xpath("option[.='" + textToFind + "']"));

            return true;
            */

            // warning issue with stale element if dom changes mid loop
			List<WebElement> comboOptions = comboBox2.findElements(By.tagName("option"));

            try{
                for(WebElement element:comboOptions){
                    if (element.getText().equals(this.textToFind)){
                        return true;
                    }
                }
            }catch(StaleElementReferenceException e){
                // if the combo changes in the middle of the apply call
                // we might encounter a StaleElementReferenceException
                // catch it and continue waiting
                return false;
            }

			
			return false;
		}
		
		public String toString(){
			return "select " + this.findBy + "to contain " + this.textToFind;
		}
		
		
	}
	

	private void selectJavaInCombo2SubmitAndCheckPageShowsJavaSelected() {
		driver.findElement(By.cssSelector("#combo2 > option[value='23']")).click();
		
		//submit the form
		driver.findElement(By.name("submitbutton")).submit();
		
		wait.until(titleIs("Processed Form Details"));
		
		//ensure that value language is id 23
        Assert.assertEquals(driver.findElement(By.id("_valuelanguage_id")).getText(), "23");
	}

	private void selectServerInCombo1() {
        // intermittency on wait
        By option3 = By.cssSelector("#combo1 > option[value='3']");
        //wait.until(ExpectedConditions.elementToBeClickable(option3));
		driver.findElement(option3).click();
	}
	
	@AfterClass
	public static void tearDown(){
		driver.quit();
	}

}