from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

import multiprocessing
import argparse


class ElementDisplay(object):
    '''Custom expected condition '''
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        try:
            element = EC._find_element(driver, self.locator)
            return element.value_of_css_property("display") == "none"
        except Exception:
            return False


class SeleniumTestError(Exception):
    pass


class SeleniumTest():
    def __init__(self, url, quiz_name, module_name, course_name):
        self.driver = webdriver.Firefox()
        self.quiz_name = quiz_name
        self.module_name = module_name
        self.course_name = course_name
        self.url = url

    def run_load_test(self, url, username, password):
        try:
            self.driver.delete_all_cookies()
            self.driver.get(self.url)
            self.login(username, password)
            self.open_quiz()
            self.quit_quiz()
            self.close_quiz()
            self.logout()
            self.driver.close()
        except Exception as e:
            self.driver.close()
            msg = ("An Error occurred while running the Selenium load"
                   " test on Yaksh!\n"
                   "Error:\n{0}".format(e))

            raise SeleniumTestError(msg)

    def login(self, username, password):
        # get the username, password and submit form elements
        username_elem = self.driver.find_element_by_id("id_username")
        password_elem = self.driver.find_element_by_id("id_password")
        submit_login_elem = self.driver.find_element_by_css_selector(
            'button.btn')

        # Type in the username, password and submit form
        username_elem.send_keys(username)
        password_elem.send_keys(password)
        submit_login_elem.click()

    def submit_answer(self, question_label, answer, loop_count=1):
        self.driver.implicitly_wait(2)
        for count in range(loop_count):
            self.driver.find_element_by_link_text(question_label).click()
            submit_answer_elem = self.driver.find_element_by_id("check")
            self.driver.execute_script(
                'global_editor.editor.setValue({});'.format(answer))
            submit_answer_elem.click()
            WebDriverWait(self.driver, 90).until(ElementDisplay(
                (By.XPATH, "//*[@id='ontop']")))

    def test_c_question(self, question_label):
        # Incorrect Answer
        loop_count = 10
        answer = '\"int add(int a, int b, int c)\\n{return;}\"'
        self.submit_answer(question_label, answer, loop_count)

        # Infinite Loop
        loop_count = 3
        answer = '\"int add(int a, int b, int c)\\n{while(1){}}\"'
        self.submit_answer(question_label, answer, loop_count)

        # Correct Answer
        loop_count = 1
        answer = '\"int add(int a, int b, int c)\\n{return a + b + c;}\"'
        self.submit_answer(question_label, answer, loop_count)

    def test_python_question(self, question_label):
        # Incorrect Answer
        loop_count = 10
        answer = '\"def is_palindrome(s):\\n    return s\"'
        self.submit_answer(question_label, answer, loop_count)

        # Infinite Loop
        loop_count = 3
        answer = '\"while True:\\n    pass"'
        self.submit_answer(question_label, answer, loop_count)

        # Correct Answer
        loop_count = 1
        answer = '\"def is_palindrome(s):\\n    return s[::-1] == s\"'
        self.submit_answer(question_label, answer, loop_count)

    def test_bash_question(self, question_label):
        # Incorrect Answer
        loop_count = 10
        answer = '\"#!/bin/bash\\nls\"'
        self.submit_answer(question_label, answer, loop_count)

        # Infinite Loop
        loop_count = 3
        answer = '\"#!/bin/bash\\nwhile [ 1 ]; do : ; done\"'
        self.submit_answer(question_label, answer, loop_count)

        # Correct Answer
        loop_count = 1
        answer = '\"#!/bin/bash\\ncat $1 | cut -d: -f2 | paste -d: $3 - $2\"'
        self.submit_answer(question_label, answer, loop_count)

    def open_quiz(self):
        # open module link
        self.driver.find_elements_by_partial_link_text(
            self.course_name)[0].click()
        self.driver.find_element_by_link_text(self.module_name).click()
        # open quiz link
        self.driver.find_element_by_link_text(self.quiz_name).click()

        # Get page elements
        start_exam_elem = WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located((By.NAME, "start"))
        )
        start_exam_elem.click()

        self.test_c_question(question_label=7)
        self.test_python_question(question_label=5)
        self.test_bash_question(question_label=4)

    def quit_quiz(self):
        quit_link_elem = WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located((By.NAME, "quit"))
        )
        quit_link_elem.click()

        quit_link_elem = WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located((By.NAME, "yes"))
        )
        quit_link_elem.click()

    def close_quiz(self):
        quit_link_elem = WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located((By.ID, "Next"))
        )
        quit_link_elem.click()

    def logout(self):
        logout_link_elem = WebDriverWait(self.driver, 5).until(
            EC.presence_of_element_located((By.ID, "logout"))
        )
        logout_link_elem.click()


def user_gen(url, ids):
    return [(url, 'User%d' % x, 'User%d' % x) for x in ids]


def wrap_run_load_test(args):
    url = "http://yaksh.fossee.aero.iitb.ac.in/exam/"
    selenium_test = SeleniumTest(url=url, quiz_name=quiz_name)
    return selenium_test.run_load_test(*args)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'url', type=str, help="url of the website being tested"
    )
    parser.add_argument('start', type=int, help="Starting user id")
    parser.add_argument(
        "-n", "--number", type=int, default=10, help="number of users"
    )
    opts = parser.parse_args()

    quiz_name = "Demo quiz"
    module_name = "Demo Module"
    course_name = "Yaksh Demo course"
    selenium_test = SeleniumTest(url=opts.url, quiz_name=quiz_name,
                                 module_name=module_name,
                                 course_name=course_name)
    pool = multiprocessing.Pool(opts.number)
    pool.map(
        wrap_run_load_test,
        user_gen(opts.url, range(opts.start, opts.start + opts.number))
    )
    pool.close()
    pool.join()