#!groovy

def call(Closure body) {
	def config = [
	    arduino_cli: '/opt/arduino-cli/arduino-cli',
    ]
	body.resolveStrategy = Closure.DELEGATE_FIRST
	body.delegate = config
	body()

	// Helpers
	def resolveGitSha = { root, ref ->
		sh(
			returnStdout: true,
			script: """#!/bin/bash
				cd ${root}
				git log -n 1 --pretty=format:'%H' ${ref}
			"""
		).trim()
	}

	def preRegisterBuildStatuses = { cfg ->
		// Pre-register all build statues so github shows what is going to happen
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate', 'Validating...', '${BUILD_URL}flowGraphTable/')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Butler)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Code analysis - Cppcheck)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Documentation)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Linux builds - Serial GW)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Linux builds - Ethernet GW)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Linux builds - MQTT GW)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Arduino Uno - Tests)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Arduino Uno - Examples)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (ESP32 - Tests)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (ESP8266 - Tests)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (nRF52 - Tests)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (STM32F1 - Tests)', 'Not run yet...', '')
		cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (STM32F4 - Tests)', 'Not run yet...', '')

		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (MySensorsMicro - Tests)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (MySensorsGW - Tests)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (nRF51822 - Tests)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (Arduino Mega - Tests)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (MySensorsMicro - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (MySensorsGW - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (nRF52832 - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (nRF51822 - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (nRF52 - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (STM32F1 - Examples)', 'Not run yet...', '')
		// cfg.pr.setBuildStatus(cfg, 'PENDING', 'Toll gate (STM32F4 - Examples)', 'Not run yet...', '')
	}

	def sendPrFailureNotifications = { cfg ->
		slackSend color: 'danger',
			message: "Failed to build <${env.CHANGE_URL}|PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE}>. Job <${env.BUILD_URL}|${env.JOB_NAME} #${env.BUILD_NUMBER}> ended with ${currentBuild.result}."

		emailext(
			subject: "PR#${env.CHANGE_ID} - ${env.CHANGE_TITLE} failed to build",
			body: '''Greetings!<p>
					I am The Butler. My task is to help you create a pull request that fit the MySensors organizations coding style and builds for all supported platforms.<p>
					I am afraid I failed to validate your pull request. Result was ''' + currentBuild.result + '''.
					<br>Please check the attached build log or <a href="${BUILD_URL}">here</a> for a hint on what the problem might be.<p>
					If you have difficulties determining the cause for the failure, feel free to ask questions <a href="${CHANGE_URL}">here</a>.<p>
					Changes:<br>
					${CHANGES}<p>
					Notable build log lines:<br>
					${BUILD_LOG_REGEX, regex="^.*?Terminated.*?$", linesBefore=0, linesAfter=0, maxMatches=5, showTruncatedLines=false, escapeHtml=true}<p>
					My personal evaluation of your pull request is available <a href="${BUILD_URL}The_20Butler_20report/butler.html">here</a>.<p>
					--<br>
					Yours sincerely, The Butler, serving the MySensors community''',
			mimeType: 'text/html',
			to: env.CHANGE_AUTHOR_EMAIL,
			attachLog: true,
			compressLog: false
		)
	}

	def sendBranchFailureNotifications = { cfg ->
		slackSend color: 'danger',
			message: "Failed to build branch ${env.BRANCH_NAME}. Job <${env.BUILD_URL}|${env.JOB_NAME} #${env.BUILD_NUMBER}> ended with ${currentBuild.result}."

		emailext(
			subject: "MySensors branch ${env.BRANCH_NAME} failed to build",
			body: '''I am afraid I failed to build branch ${BRANCH_NAME}. Result was ''' + currentBuild.result + '''.
					<br>Please check the attached build log or <a href="${BUILD_URL}">here</a> for a hint on what the problem might be.<p>
					Changes:<br>
					${CHANGES}<p>
					Notable build log lines:<br>
					${BUILD_LOG_REGEX, regex="^.*?Terminated.*?$", linesBefore=0, linesAfter=0, maxMatches=5, showTruncatedLines=false, escapeHtml=true}<p>
					My personal evaluation of your pull request is available <a href="${BUILD_URL}The_20Butler_20report/butler.html">here</a>.<p>
					--<br>
					Yours sincerely, The Butler, serving the MySensors community''',
			mimeType: 'text/html',
			to: 'builds@mysensors.org',
			attachLog: true,
			compressLog: false
		)
	}

	// Load shared scripts
	config.pr   = load("${config.repository_root}.ci/pr-toolbox.groovy")
	def linux   = load("${config.repository_root}.ci/linux.groovy")
	def arduino = load("${config.repository_root}.ci/arduino.groovy")

	// Git / PR metadata
	if (env.CHANGE_ID) {
		config.is_pull_request = true
		echo "Building pull request: #${env.CHANGE_ID}\nTarget branch: ${env.CHANGE_TARGET}\nAuthor: ${env.CHANGE_AUTHOR} (${env.CHANGE_AUTHOR_EMAIL})"

		config.git_sha = resolveGitSha(config.repository_root, "refs/remotes/origin/PR-${env.CHANGE_ID}")
		preRegisterBuildStatuses(config)
	} else {
		config.is_pull_request = false
		echo "Building branch: ${env.BRANCH_NAME}"

		config.git_sha = resolveGitSha(config.repository_root, "refs/remotes/origin/${env.BRANCH_NAME}")
	}

	try {
		ansiColor('xterm') {
			if (config.is_pull_request) {
				def butler = load("${config.repository_root}.ci/butler.groovy")
				stage('Butler') {
					butler(config)
				}
			}

			stage('Preparation') {
				def hwRepos = [
					[url: 'https://github.com/mysensors/ArduinoHwAVR.git',  dir: 'hardware/MySensors/avr'],
					[url: 'https://github.com/mysensors/ArduinoHwSAMD.git', dir: 'hardware/MySensors/samd'],
					[url: 'https://github.com/mysensors/ArduinoHwNRF5.git', dir: 'hardware/MySensors/nRF5']
				]

				hwRepos.each { repo ->
					checkout(
						changelog: false,
						poll: false,
						scm: [
							$class: 'GitSCM',
							branches: [[name: '*/master']],
							extensions: [
								[$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: true],
								[$class: 'RelativeTargetDirectory', relativeTargetDir: repo.dir]
							],
							userRemoteConfigs: [[url: repo.url]]
						]
					)
				}

				config.tests_fast = findFiles(glob: "${config.library_root}tests/fast/**/*.ino")
				config.tests_nightly = findFiles(glob: "${config.library_root}tests/nightly/**/*.ino")
				config.examples = findFiles(glob: "${config.library_root}examples/**/*.ino")
			}

			parallel Doxygen: {
				if (!config.nightly_arduino_ide) {
					stage('Doxygen') {
						def doxygen = load("${config.repository_root}.ci/doxygen.groovy")
						doxygen(config)
					}
				}
			}, CodeAnalysis: {
				if (!config.nightly_arduino_ide && config.is_pull_request) {
					def analysis = load("${config.repository_root}.ci/static_analysis.groovy")
					stage('Cppcheck') {
						analysis.cppCheck(config)
					}
				}
			}, LinuxBuilds: {
				if (!config.nightly_arduino_ide) {
					stage('LinuxGwSerial') {
						linux.buildSerial(config)
					}
					stage('LinuxGwEthernet') {
						linux.buildEthernet(config)
					}
					stage('LinuxGwMQTT') {
						linux.buildMQTT(config)
					}
				}
			}, ArduinoBuilds: {
				stage('ArduinoBuilds') {
					// Run all Arduino builds in parallel
					def arduinoBranches = [:]

					arduinoBranches['ArduinoUno (Tests)'] = {
						stage('ArduinoUno (Tests)') {
							arduino.buildArduinoUno(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['ArduinoUno (Examples)'] = {
						stage('ArduinoUno (Examples)') {
							arduino.buildArduinoUno(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['nRF52 (Tests)'] = {
						stage('nRF52 (Tests)') {
							arduino.buildnRF52(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['STM32F1 (Tests)'] = {
						stage('STM32F1 (Tests)') {
							arduino.buildSTM32F1(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['STM32F4 (Tests)'] = {
						stage('STM32F4 (Tests)') {
							arduino.buildSTM32F4(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['ESP32 (Tests)'] = {
						stage('ESP32 (Tests)') {
							arduino.buildESP32(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['ESP8266 (Tests)'] = {
						stage('ESP8266 (Tests)') {
							arduino.buildESP8266(config, config.tests_fast, 'Tests')
						}
					}

					/*
					arduinoBranches['MySensorsMicro (tests)'] = {
						stage('MySensorsMicro (tests)') {
							arduino.buildMySensorsMicro(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['MySensorsGW (tests)'] = {
						stage('MySensorsGW (tests)') {
							arduino.buildMySensorsGw(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['nRF51822 (tests)'] = {
						stage('nRF51822 (tests)') {
							arduino.buildnRF51822(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['ArduinoMega (tests)'] = {
						stage('ArduinoMega (tests)') {
							arduino.buildArduinoMega(config, config.tests_fast, 'Tests')
						}
					}
					arduinoBranches['MySensorsMicro (examples)'] = {
						stage('MySensorsMicro (examples)') {
							arduino.buildMySensorsMicro(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['MySensorsGW (examples)'] = {
						stage('MySensorsGW (examples)') {
							arduino.buildMySensorsGw(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['nRF52832 (examples)'] = {
						stage('nRF52832 (examples)') {
							arduino.buildnRF52832(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['nRF51822 (examples)'] = {
						stage('nRF51822 (examples)') {
							arduino.buildnRF51822(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['nRF5 (examples)'] = {
						stage('nRF5 (examples)') {
							arduino.buildnRF5(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['ESP8266 (examples)'] = {
						stage('ESP8266 (examples)') {
							arduino.buildESP8266(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['ESP32 (examples)'] = {
						stage('ESP32 (examples)') {
							arduino.buildESP32(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['STM32F1 (Examples)'] = {
						stage('STM32F1 (Examples)') {
							arduino.buildSTM32F1(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['STM32F4 (Examples)'] = {
						stage('STM32F4 (Examples)') {
							arduino.buildSTM32F4(config, config.examples, 'Examples')
						}
					}
					arduinoBranches['ArduinoMega (examples)'] = {
						stage('ArduinoMega (examples)') {
							arduino.buildArduinoMega(config, config.examples, 'Examples')
						}
					}
					*/

					parallel arduinoBranches
				}
			}, failFast: true
		}
	} finally {
		if (currentBuild.currentResult != 'SUCCESS') {
			config.pr.setBuildStatus(config, 'ERROR', 'Toll gate', 'Failed', '${BUILD_URL}flowGraphTable/')

			if (config.is_pull_request) {
				sendPrFailureNotifications(config)
			} else {
				sendBranchFailureNotifications(config)
			}
		} else {
			config.pr.setBuildStatus(config, 'SUCCESS', 'Toll gate', 'Pass', '')
		}
	}
}

return this
