package com.greinet.tvtotalripper.crawler;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

/**
 * Helper class to get show, season und episode elements from the website
 * @author agreiner
 */
public class CrawlerUtil {
    
    static{
        System.setProperty("webdriver.chrome.driver", "resources/chromedriver.exe");
    }
    
    /** The logger */
    private static final Logger logger = LogManager.getLogger(CrawlerUtil.class);
    /** The WebDriver for show information */
    private final static WebDriver driverShows = createShowDriver();
    /** The WebDriver for season and episode information */
    private final static WebDriver driverSeasonsAndEpisodes = createChromeDriver();
    /** The WebDriver for fetchfile.net URL conversion */
    private final static WebDriver driverFetchFile = createFetchFileDriver();
    /** The show */
    private static String show = "";
    
    /**
     * Create a basic ChromeDriver
     * @return the WebDriver
     */
    private static WebDriver createChromeDriver(){
        ChromeOptions options = new ChromeOptions();
        options.addArguments("start-maximized");
        return new ChromeDriver(options);
    }
    
    /**
     * Create the WebDriver to get show information
     * @return the WebDriver
     */
    private static WebDriver createShowDriver(){
        WebDriver driver = createChromeDriver();
        driver.get("https://www.myspass.de/sendungen-a-bis-z/");
        return driver;
    }
    
    /**
     * Create the WebDriver to get the video URLs from FetchFile.net
     * @return the WebDriver
     */
    private static WebDriver createFetchFileDriver(){
        WebDriver driver = createChromeDriver();
        driver.get("https://de.fetchfile.net/herunterladen-von-myspass");
        return driver;
    }
    
    /**
     * Navigate to the given URL or do nothing, if already at that URL
     * @param url the URL
     */
    private static void navigateToShow(String url){
        if(!driverSeasonsAndEpisodes.getCurrentUrl().equals(url)){
            driverSeasonsAndEpisodes.get(url);
        }
        try{
        driverSeasonsAndEpisodes.findElement(By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a")).click();
        }catch(ElementClickInterceptedException e){
            driverSeasonsAndEpisodes.findElement(By.id("cmpbntyestxt")).click();
            driverSeasonsAndEpisodes.findElement(By.xpath("/html/body/div[4]/div[1]/div[2]/ul/li[2]/a")).click();
        }
    }
    
    /**
     * Returns the WebElements representing the shows.
     * @return the show WebElements
     */
    public static List<WebElement> getShows(){
        return driverShows.findElements(By.xpath("/html/body/div[5]/div/div/div/div/a"));
    }
    
    /**
     * Returns the Webelements represeting the seasons of a specific show
     * @param url the URL to the show
     * @return the season WebElements
     */
    public static List<WebElement> getSeasons(String url){
        navigateToShow(url);
        
        String urlNotTrailingSlash = url.substring(0,url.lastIndexOf("/"));
        
        show = urlNotTrailingSlash.substring(urlNotTrailingSlash.lastIndexOf("/")+1, urlNotTrailingSlash.length());
        return driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
        
    }
    
    /**
     * Returns the information from the episodes from a specific show and season packed into a wrapper class
     * @param url the URL of the show
     * @param seasonName the name of the season
     * @return the list of episodes
     */
    public static List<EpisodeWrapper> getEpisodes(String url, String seasonName){
        
        // navigate the WebDriver to the specified URL
        navigateToShow(url);
        
        // Search the specified season
        List<WebElement> elements = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div[1]/select/option"));
        Optional<WebElement> seasonElement = elements.stream().filter(e -> e.getText().equals(seasonName)).findFirst();
        
        // If the season was found, navigate there
        if(seasonElement.isPresent()){
            seasonElement.get().click();
            
            // Wait for the website to load and then switch to the list view
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                logger.log(Level.ERROR, "Could not find list view button for url:[{0}], season:[{1}]",url, seasonName);
            }
            driverSeasonsAndEpisodes.findElement(By.className("listView--toggle_input")).click();
            
            // Get the parent div of the episode information list
            List<WebElement> episodeListDivs = driverSeasonsAndEpisodes.findElements(By.xpath("/html/body/div[4]/div[1]/div[3]/div[2]/div"));
            List<WebElement> filteredNoDisplay = episodeListDivs.stream().filter(e -> !"none".equals(e.getCssValue("display"))).collect(Collectors.toList());
            
            
            if(!filteredNoDisplay.isEmpty()){
                // Get the Webelements with the link and title information
                List<WebElement> linksToEpisodes = new ArrayList<>();
                filteredNoDisplay.forEach(e -> 
                    linksToEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td/a")))
                );
                // Get the WebElements with the duration information
                List<WebElement> durationOfEpisodes = new ArrayList<>();
                filteredNoDisplay.forEach(e -> 
                    durationOfEpisodes.addAll(e.findElements(By.xpath(".//div/table/tbody/tr/td[3]")))
                );
                // Put the information into episode wrappers and return the list
                List<EpisodeWrapper> wrappers = new ArrayList<>();
                for(int i=0;i<linksToEpisodes.size();i++){
                    wrappers.add(new EpisodeWrapper(linksToEpisodes.get(i).getAttribute("href"), linksToEpisodes.get(i).getText(), linksToEpisodes.get(i).getText(), seasonName, show, durationOfEpisodes.get(i).getText()));
                }
                return wrappers;
            }else{
                logger.log(Level.WARN, "No episodes found for url:[{0}], season:[{1}]",url, seasonName);
                return new ArrayList<>();
            }
        }else{
            logger.log(Level.ERROR, "No seaons found for url:[{0}], season:[{1}]",url, seasonName);
            return new ArrayList<>();
        }
    }
    
    /**
     * Returns the FetchFile.net URL to the myspass.com video file
     * @param myspassURL the URL to the myspass.com video
     * @return the FetchFile.net URL
     */
    public static String getFetchfileURL(String myspassURL){
        synchronized(driverFetchFile){
            WebElement videoPathInput = driverFetchFile.findElement(By.id("videoPath"));
            videoPathInput.sendKeys(myspassURL);
            WebElement submitButton = driverFetchFile.findElement(By.id("home-submit"));
            submitButton.click();
            WebDriverWait wait = new WebDriverWait(driverFetchFile, 20);
            WebElement downloadButton = wait.until(ExpectedConditions.elementToBeClickable(By.className("download-link")));
            String downloadLink = downloadButton.getAttribute("href");
            driverFetchFile.get("https://de.fetchfile.net/herunterladen-von-myspass");

            // Close advertisement tabs
            List<String> tabs = new ArrayList<>(driverFetchFile.getWindowHandles());
            for (int i=1;i<tabs.size();i++){
                driverFetchFile.switchTo().window(tabs.get(i));
                driverFetchFile.close();
            }
            driverFetchFile.switchTo().window(driverFetchFile.getWindowHandles().toArray(new String[1])[0]);

            return downloadLink;
        }
    }
}
