package com.frameworkium.tests.internal;

import static com.frameworkium.config.DriverType.determineEffectiveDriverType;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.SessionId;
import org.openqa.selenium.remote.SessionNotFoundException;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Listeners;

import ru.yandex.qatools.allure.annotations.Issue;
import ru.yandex.qatools.allure.annotations.TestCaseId;

import com.frameworkium.capture.ScreenshotCapture;
import com.frameworkium.config.DriverType;
import com.frameworkium.config.WebDriverWrapper;
import com.frameworkium.listeners.CaptureListener;
import com.frameworkium.listeners.MethodInterceptor;
import com.frameworkium.listeners.ResultLoggerListener;
import com.frameworkium.listeners.SauceLabsListener;
import com.frameworkium.listeners.ScreenshotListener;
import com.frameworkium.listeners.TestListener;
import com.frameworkium.reporting.AllureProperties;
import com.saucelabs.common.SauceOnDemandAuthentication;
import com.saucelabs.common.SauceOnDemandSessionIdProvider;
import com.saucelabs.testng.SauceOnDemandAuthenticationProvider;

@Listeners({CaptureListener.class, ScreenshotListener.class, MethodInterceptor.class, SauceLabsListener.class,
        TestListener.class, ResultLoggerListener.class})
public abstract class BaseTest implements SauceOnDemandSessionIdProvider, SauceOnDemandAuthenticationProvider {

    private static List<WebDriverWrapper> activeDrivers = Collections
            .synchronizedList(new ArrayList<WebDriverWrapper>());
    private static ThreadLocal<WebDriverWrapper> driver;
    private static DriverType desiredDriverType = determineEffectiveDriverType();
    private static ThreadLocal<Boolean> requiresReset;
    private static ThreadLocal<ScreenshotCapture> capture;

    private static Logger logger = LogManager.getLogger(BaseTest.class);

    public static String userAgent;
    
    @BeforeSuite(alwaysRun = true)
    public static void instantiateDriverObject() {
        driver = new ThreadLocal<WebDriverWrapper>() {
            @Override
            protected WebDriverWrapper initialValue() {
                WebDriverWrapper webDriver = desiredDriverType.instantiate();
                activeDrivers.add(webDriver);
                return webDriver;
            }
        };
        requiresReset = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return Boolean.FALSE;
            }
        };
        capture = new ThreadLocal<ScreenshotCapture>() {
            @Override
            protected ScreenshotCapture initialValue() {
                return null;
            }
        };
        if (ScreenshotCapture.isRequired()) {
            String testID = "N/S";
            try {
                testID= testMethod.getAnnotation(Issue.class).value();
            } catch (NullPointerException e) {}
            try {
                testID= testMethod.getAnnotation(TestCaseId.class).value();
            } catch (NullPointerException e) {}
            
            capture.set(new ScreenshotCapture(testID, driver.get()));
        }
    }

    public static WebDriverWrapper getDriver() {
        return driver.get();
    }

    @BeforeMethod(alwaysRun = true)
    public static void clearSession() {
        // Reset browser or app
        if (requiresReset.get()) {
            try {
                if (DriverType.isNative()) {
                    getDriver().getWrappedAppiumDriver().resetApp();
                } else {
                    getDriver().manage().deleteAllCookies();
                }
            } catch (SessionNotFoundException e) {
                logger.error("Session quit unexpectedly.", e);
            }
        } else {
            requiresReset.set(Boolean.TRUE);
        }
    }

    @BeforeMethod(alwaysRun = true)
    public static void setUserAgent() {
        userAgent = getUserAgent();
    }
    
    @BeforeMethod(alwaysRun = true)
    public void initialiseNewScreenshotCapture(Method testMethod) {
        if (ScreenshotCapture.isRequired()) {
            String testID = "N/S";
            try {
                testID= testMethod.getAnnotation(Issue.class).value();
            } catch (NullPointerException e) {}
            try {
                testID= testMethod.getAnnotation(TestCaseId.class).value();
            } catch (NullPointerException e) {}
            
            capture.set(new ScreenshotCapture(testID, driver.get()));
        }
    }

    @AfterSuite(alwaysRun = true)
    public static void closeDriverObject() {
        for (WebDriver driver : activeDrivers) {
            try {
                driver.quit();
            } catch (Exception e) {
                logger.warn("Session quit unexpectedly.", e);
            }
        }
    }

    @AfterSuite(alwaysRun = true)
    public static void createAllureProperties() {
        AllureProperties.create();
    }

    /** @return the Job id for the current thread */
    @Override
    public String getSessionId() {
        SessionId sessionId = getDriver().getWrappedRemoteWebDriver().getSessionId();
        return (sessionId == null) ? null : sessionId.toString();
    }

    public static String getUserAgent() {
        String ua = StringUtils.EMPTY;
        try {
            ua = (String) getDriver().executeScript("return navigator.userAgent;");
        }
        catch (Exception e) {
            ua = "Unable to fetch UserAgent";
        }
        logger.debug("User agent is: '" + ua + "'");
        return ua;
    }
    
    /** @return the {@link SauceOnDemandAuthentication} instance containing the Sauce username/access key */
    @Override
    public SauceOnDemandAuthentication getAuthentication() {
        return new SauceOnDemandAuthentication();
    }

    public static ScreenshotCapture getCapture() {
        return capture.get();
    }
}

@@ -3040,460 +3040,8 @@
  %7D;%0A
-        if (ScreenshotCapture.isRequired()) %7B%0A            String testID = %22N/S%22;%0A            try %7B%0A                testID= testMethod.getAnnotation(Issue.class).value();%0A            %7D catch (NullPointerException e) %7B%7D%0A            try %7B%0A                testID= testMethod.getAnnotation(TestCaseId.class).value();%0A            %7D catch (NullPointerException e) %7B%7D%0A            %0A            capture.set(new ScreenshotCapture(testID, driver.get()));%0A        %7D
 %0A   
@@ -4381,24 +4381,32 @@
 er.get()));%0A
+       %0A
         %7D%0A  

Resolve capture instantiate issue introduced by commit