// must have the rest server running with at least the contents of the tutorial prefix
const {Builder, By, Key, until} = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');

// window size we're testing with
const winX = 1920, winY = 1080;
const options = new firefox.Options();
// sometimes, selenium will hang forever in the middle of the test if the test is ran headlessly
// if you encounter this issue, comment out this headless flag
// (it doesn't seem to ever hang when running non-headlessly)
// the test shouldnt take more than a minute-ish either way
options.addArguments('-headless');
options.addArguments(`--window-size=${ winX },${ winY }`);

(async function example() {
	let driver = await new Builder().forBrowser('firefox').setFirefoxOptions(options).build();
	await driver.manage().window().setRect({x: 0, y: 0, width: winX, height: winY});
	try {
		await driver.get('http://localhost:9101/');

		/**************************************************************
		 * MEANS EXP
		 */

		// navigate to exp list
		await driver.findElement(By.linkText('experiments')).click();
		//.sendKeys('webdriver', Key.RETURN);
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search experiments..."]'), 2000));

		// click the clone button next to test/test/iris/1/iris
		let eLink = await driver.findElement(By.linkText('test/test/iris/1/iris'));
		await eLink.findElement(By.xpath('../..')).findElement(By.css('button.btn-outline-success')).click();
		await driver.wait(until.elementLocated(By.css('.modal')), 2000);

		// make new exp
		let eUser = await driver.findElement(By.css('input[placeholder="Experiment user..."]'));
		await eUser.clear();
		await eUser.sendKeys('selenium');
		let eName = driver.findElement(By.css('input[placeholder="Experiment name..."]'));
		await eName.clear();
		await eName.sendKeys('means');
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-primary')).click();
		await driver.wait(until.elementLocated(By.linkText('selenium/test/iris/1/means')), 2000);
		await driver.sleep(1000);

		// go to new exp
		await driver.findElement(By.linkText('selenium/test/iris/1/means')).click();
		await driver.wait(until.elementLocated(By.css('div.experimentEditor')), 2000);

		// click on training_alg block
		await driver.findElement(By.css('rect#block_training_alg')).click();
		await driver.wait(until.elementLocated(By.css('div.block_training_alg')), 2000);

		// set the algorithm to the new one
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="test/means_training/1"]')).click();

		// do same for testing_alg block
		await driver.findElement(By.css('rect#block_testing_alg')).click();
		await driver.wait(until.elementLocated(By.css('div.block_testing_alg')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="test/means_testing/1"]')).click();

		// make sure the exp is still valid
		await driver.wait(until.elementLocated(By.css('.badge-success')), 2000);

		// save exp
		await driver.findElement(By.css('div.experimentEditor > div.d-flex > button.btn-outline-secondary')).click();
		await driver.sleep(1000);

		/**************************************************************
		 * CLEANUP
		 */

		// go back and delete exp
		await driver.findElement(By.linkText('experiments')).click();
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search experiments..."]'), 2000));
		eLink = await driver.findElement(By.linkText('selenium/test/iris/1/means'));
		await eLink.findElement(By.xpath('../..')).findElement(By.css('button.btn-outline-danger')).click();
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-danger')).click();
		await driver.sleep(1000);

		/**************************************************************
		 * ADVANCED IRIS TOOLCHAIN
		 */

		// navigate to tc list
		await driver.findElement(By.linkText('toolchains')).click();
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search toolchains..."]'), 2000));

		// click the clone button next to test/iris/1
		let tLink = await driver.findElement(By.linkText('test/iris/1'));
		await tLink.findElement(By.xpath('../..')).findElement(By.css('button.btn-outline-success + button.btn-outline-success')).click();
		await driver.wait(until.elementLocated(By.css('.modal')), 2000);

		// make new tc
		const tUser = await driver.findElement(By.css('input[placeholder="Toolchain user..."]'));
		await tUser.clear();
		await tUser.sendKeys('selenium');
		const tName = driver.findElement(By.css('input[placeholder="New toolchain name..."]'));
		await tName.clear();
		await tName.sendKeys('iris_advanced');
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-primary')).click();
		await driver.wait(until.elementLocated(By.linkText('selenium/iris_advanced/1')), 2000);
		await driver.sleep(1000);

		// go to new tc
		await driver.findElement(By.linkText('selenium/iris_advanced/1')).click();
		await driver.wait(until.elementLocated(By.css('div.toolchainEditor')), 2000);

		// delete conn between training_data.measurements & training_alg.measurements
		let rcEl = await driver.findElement(By.css('#\\#training_data-measurements-training_alg-measurements'));
		await driver.actions().contextClick(rcEl).perform();
		await driver.sleep(1000);
		await driver.findElement(By.css('nav.react-contextmenu--visible > div')).click();

		// delete conn between testing_data.measurements & testing_alg.measurements
		rcEl = await driver.findElement(By.css('#\\#testing_data-measurements-testing_alg-measurements'));
		await driver.actions().contextClick(rcEl).perform();
		await driver.sleep(1000);
		await driver.findElement(By.css('nav.react-contextmenu--visible > div')).click();

		// add training preprocessor block way below the rest of the toolchain
		let originEl = await driver.findElement(By.css('#block_testing_data'));
		await driver.actions().move({x: 100, y: 100, origin: originEl}).pause(500).contextClick().perform();
		await driver.sleep(1000);
		await driver.findElement(By.css('nav.react-contextmenu--visible > div:first-child')).click();

		// edit training preprocessor
		await driver.findElement(By.css('#block_block')).click();
		await driver.sleep(1000);
		// name
		let tbName = await driver.findElement(By.css('#tcModalInitFocus'));
		tbName.clear();
		tbName.sendKeys('pre_training');
		// input
		await driver.findElement(By.css('.modal-body .btn-secondary')).click();
		let tbiName = await driver.findElement(By.css('.modal-body > form > div.row input'));
		await tbiName.clear();
		await tbiName.sendKeys('measurements');
		// output
		await driver.findElement(By.css('.modal-body .col-sm-6 + .col-sm-6 .btn-secondary')).click();
		let tboName = await driver.findElement(By.css('.modal-body > form > div.row .col-sm-6 + .col-sm-6 input'));
		await tboName.clear();
		await tboName.sendKeys('measurements');
		await driver.sleep(1000);
		//close
		await driver.findElement(By.css('.modal button.close')).click();
		await driver.sleep(1000);

		// copy the block
		rcEl = await driver.findElement(By.css('#block_pre_training'));
		await driver.actions().contextClick(rcEl).perform();
		await driver.sleep(1000);
		await driver.findElement(By.css('nav.react-contextmenu--visible > div + div')).click();

		// paste testing preprocessor block way below the rest of the toolchain
		originEl = await driver.findElement(By.css('#block_pre_training'));
		await driver.actions().move({x: 150, origin: originEl}).pause(500).contextClick().perform();
		await driver.sleep(1000);
		await driver.findElement(By.css('nav.react-contextmenu--visible > div:first-child')).click();

		// unselect pasted block
		await driver.actions().move({y: 200, origin: originEl}).pause(500).click().pause(500).perform();

		// edit the block name
		await driver.findElement(By.css('#block_pre_training0')).click();
		await driver.sleep(1000);
		tbName = await driver.findElement(By.css('#tcModalInitFocus'));
		await tbName.clear();
		await tbName.sendKeys('pre_testing');
		await driver.sleep(1000);
		//close
		await driver.findElement(By.css('.modal button.close')).click();
		await driver.sleep(1000);

		// connect pre_training to rest of tc
		// data to pre
		let fromBox = await driver.findElement(By.css('rect#training_data-output-measurements'));
		let toBox = await driver.findElement(By.css('rect#pre_training-input-measurements'));
		await driver.actions().dragAndDrop(fromBox, toBox).pause(200).perform();
		// pre to alg
		fromBox = await driver.findElement(By.css('rect#pre_training-output-measurements'));
		toBox = await driver.findElement(By.css('rect#training_alg-input-measurements'));
		await driver.actions().dragAndDrop(fromBox, toBox).pause(200).perform();

		// connect pre_testing to rest of tc
		// data to pre
		fromBox = await driver.findElement(By.css('rect#testing_data-output-measurements'));
		toBox = await driver.findElement(By.css('rect#pre_testing-input-measurements'));
		await driver.actions().dragAndDrop(fromBox, toBox).pause(500).perform();
		// pre to alg
		fromBox = await driver.findElement(By.css('rect#pre_testing-output-measurements'));
		toBox = await driver.findElement(By.css('rect#testing_alg-input-measurements'));
		await driver.actions().dragAndDrop(fromBox, toBox).pause(500).perform();

		// fix the channel on pre_training
		await driver.findElement(By.css('#block_pre_training')).click();
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal-body select.form-control')).click();
		await driver.findElement(By.css('.modal-body option[value="training_data"]')).click();
		await driver.sleep(500);
		await driver.findElement(By.css('.modal button.close')).click();
		await driver.sleep(1000);

		// layout the tc
		await driver.findElement(By.xpath('//button[contains(.,"Layout")]')).click();
		// needs to communicate w graphviz, give it some time
		await driver.sleep(5000);

		// make sure the tc is still valid
		await driver.wait(until.elementLocated(By.css('.badge-success')), 2000);

		// save
		await driver.findElement(By.css('div.toolchainEditor > div.d-flex > button.btn-outline-secondary')).click();
		await driver.sleep(1000);

		/**************************************************************
		 * CLEANUP
		 */

		/**************************************************************
		 * ADVANCED IRIS PREPROCESSOR ALGORITHM
		 */

		// navigate to alg list
		await driver.findElement(By.linkText('algorithms')).click();
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search algorithms..."]'), 2000));

		// click the new button
		await driver.findElement(By.css('.dropdown + div > .btn-outline-success')).click();
		await driver.wait(until.elementLocated(By.css('.modal')), 2000);

		// make new alg
		const aUser = await driver.findElement(By.css('input[placeholder="Algorithm user..."]'));
		await aUser.clear();
		await aUser.sendKeys('selenium');
		const aName = driver.findElement(By.css('input[placeholder="New algorithm name..."]'));
		await aName.clear();
		await aName.sendKeys('iris_preprocessor');
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-primary')).click();
		await driver.wait(until.elementLocated(By.linkText('selenium/iris_preprocessor/1')), 2000);
		await driver.sleep(1000);

		// go to new alg
		await driver.findElement(By.linkText('selenium/iris_preprocessor/1')).click();
		await driver.wait(until.elementLocated(By.css('ul.nav-fill.nav.nav-tabs')), 2000);

		// add an input & output
		await driver.findElement(By.css('#newInputBtn')).click();
		await driver.findElement(By.css('#newOutputBtn')).click();

		// change input & output name to "measurements"
		let aiDiv = await driver.findElement(By.css('#newInputBtn')).findElement(By.xpath('..')).findElement(By.css('div:first-child'));
		let aoDiv = await driver.findElement(By.css('#newOutputBtn')).findElement(By.xpath('..')).findElement(By.css('div:first-child'));
		let aiName = await aiDiv.findElement(By.css('input'));
		await aiName.clear();
		await aiName.sendKeys('measurements');
		let aoName = await aoDiv.findElement(By.css('input'));
		await aoName.clear();
		await aoName.sendKeys('measurements');

		// select type system/array_1d_floats/1
		await aiDiv.findElement(By.css('select.custom-select')).click();
		await aiDiv.findElement(By.css('select.custom-select option[value="system/array_1d_floats/1"]')).click();
		await aoDiv.findElement(By.css('select.custom-select')).click();
		await aoDiv.findElement(By.css('select.custom-select option[value="system/array_1d_floats/1"]')).click();
		await driver.sleep(500);

		// save
		await driver.findElement(By.css('.tab-pane.active > div > div.d-flex > button.btn-outline-secondary')).click();
		await driver.sleep(1000);

		/**************************************************************
		 * ADVANCED IRIS EXPERIMENT
		 */

		// navigate to exp list
		await driver.findElement(By.linkText('experiments')).click();
		//.sendKeys('webdriver', Key.RETURN);
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search experiments..."]'), 2000));

		// click the new button
		await driver.findElement(By.css('.dropdown + div > .btn-outline-success')).click();
		await driver.wait(until.elementLocated(By.css('.modal')), 2000);

		// make new exp
		eUser = await driver.findElement(By.css('input[placeholder="Experiment user..."]'));
		await eUser.clear();
		await eUser.sendKeys('selenium');
		let eTc = await driver.findElement(By.css('.modal select'));
		await eTc.click();
		await eTc.findElement(By.css('select option[value="selenium/iris_advanced/1"]')).click();
		eName = driver.findElement(By.css('input[placeholder="Experiment name..."]'));
		await eName.clear();
		await eName.sendKeys('iris');
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-primary')).click();
		await driver.wait(until.elementLocated(By.linkText('selenium/selenium/iris_advanced/1/iris')), 2000);
		await driver.sleep(1000);

		// go to new exp
		await driver.findElement(By.linkText('selenium/selenium/iris_advanced/1/iris')).click();
		await driver.wait(until.elementLocated(By.css('div.experimentEditor')), 2000);

		// assign dataset protocol
		await driver.findElement(By.css('.datasets select')).click();
		await driver.findElement(By.css(`.datasets select option[value='{"testing_data":{"database":"iris/1","protocol":"Main","set":"training"},"training_data":{"database":"iris/1","protocol":"Main","set":"testing"}}']`)).click();

		// pre_training block
		await driver.findElement(By.css('rect#block_pre_training')).click();
		await driver.wait(until.elementLocated(By.css('div.block_pre_training')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="selenium/iris_preprocessor/1"]')).click();

		// pre_testing block
		await driver.findElement(By.css('rect#block_pre_testing')).click();
		await driver.wait(until.elementLocated(By.css('div.block_pre_testing')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="selenium/iris_preprocessor/1"]')).click();

		// training_alg block
		await driver.findElement(By.css('rect#block_training_alg')).click();
		await driver.wait(until.elementLocated(By.css('div.block_training_alg')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="test/iris_training/1"]')).click();

		// testing_alg block
		await driver.findElement(By.css('rect#block_testing_alg')).click();
		await driver.wait(until.elementLocated(By.css('div.block_testing_alg')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="test/iris_testing/1"]')).click();

		// analyzer block
		await driver.findElement(By.css('rect#block_analyzer')).click();
		await driver.wait(until.elementLocated(By.css('div.block_analyzer')), 2000);
		await driver.findElement(By.css('select.custom-select')).click();
		await driver.findElement(By.css('select.custom-select option[value="test/iris_analyzer/1"]')).click();

		// make sure the exp is still valid
		await driver.wait(until.elementLocated(By.css('.badge-success')), 2000);

		// save exp
		await driver.findElement(By.css('div.experimentEditor > div.d-flex > button.btn-outline-secondary')).click();
		await driver.sleep(1000);

		/**************************************************************
		 * CLEANUP
		 */

		// go back and delete toolchain (which also deletes the associated experiment)
		await driver.findElement(By.linkText('toolchains')).click();
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search toolchains..."]'), 2000));
		tLink = await driver.findElement(By.linkText('selenium/iris_advanced/1'));
		await tLink.findElement(By.xpath('../..')).findElement(By.css('button.btn-outline-danger')).click();
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-danger')).click();
		await driver.sleep(1000);

		// go back and delete algorithm
		await driver.findElement(By.linkText('algorithms')).click();
		await driver.wait(until.elementLocated(By.css('input[placeholder="Search algorithms..."]'), 2000));
		tLink = await driver.findElement(By.linkText('selenium/iris_preprocessor/1'));
		await tLink.findElement(By.xpath('../..')).findElement(By.css('button.btn-outline-danger')).click();
		await driver.sleep(1000);
		await driver.findElement(By.css('.modal button.btn-danger')).click();
		await driver.sleep(1000);

		console.log('selenium tests finished successfully!');
	} catch(e) {
		console.log('selenium tests failed!');
		console.error(e);
	} finally {
		await driver.quit();
	}
})();
