(function(exports){
	'use strict'

	// "Class" for calculating CRC8 checksums...
	function CRC8(polynomial, initial_value) { // constructor takes an optional polynomial type from CRC8.POLY
		if (polynomial == null) polynomial = CRC8.POLY.CRC8_CCITT
		this.table = CRC8.generateTable(polynomial);
		this.initial_value = initial_value;
	}
	
	// Returns the 8-bit checksum given an array of byte-sized numbers
	CRC8.prototype.checksum = function(byte_array) {
		var c = this.initial_value;
	
		for (var i = 0; i < byte_array.length; i++ ) 
		c = this.table[(c ^ byte_array[i]) % 256] 
	
		return c;
	} 
	
	// returns a lookup table byte array given one of the values from CRC8.POLY 
	CRC8.generateTable =function(polynomial)
	{
		var csTable = [] // 256 max len byte array
		
		for ( var i = 0; i < 256; ++i ) {
		var curr = i
		for ( var j = 0; j < 8; ++j ) {
			if ((curr & 0x80) !== 0) {
			curr = ((curr << 1) ^ polynomial) % 256
			} else {
			curr = (curr << 1) % 256
			}
		}
		csTable[i] = curr 
		}
		
		return csTable
	}
	
	// This "enum" can be used to indicate what kind of CRC8 checksum you will be calculating
	CRC8.POLY = {
		CRC8 : 0xd5,
		CRC8_CCITT : 0x07,
		CRC8_DALLAS_MAXIM : 0x31,
		CRC8_SAE_J1850 : 0x1D,
		CRC_8_WCDMA : 0x9b,
	}

	function toHexString(byteArray) {
		var s = '';
		byteArray.reverse().forEach(function(byte) {
			s += ('0' + (byte & 0xFF).toString(16)).slice(-2);
		});
		return s.toUpperCase();
	};

	// Положение конфигурации
	var SERIAL_AT = 0xE000;

	exports.burn = function(firmware) {
		
		ui.progress(0);
		
		// Считываем сохранённый серийный номер
		stm32.read("tmp/uspd_serial.bin", "0x0800E000", 8, true);
		var uspdSerial=parseInt(toHexString(fs.readBinary("tmp/uspd_serial.bin")), 16);

		// Запрашиваем серийный номер
		var serial = ui.askNumber("Введите серийный номер:", 0, 9999999999999999);
		if (serial === undefined) return "Отменено";
		
		var deviceType;

		// Проверка корректности введённого серийного номера
		// Есть возможность ввести серийник старого формата (генерируемый из CPU ID). Для него проверка не проводится
		if (Utils.toHex(serial, 8) != cpuId.stm32f2xx_serial32()) { 
			if (serial.toString().length != 16)
			{
				return "Неверная длина серийного номера!"
			}
			if (serial.toString().slice(0, 1) == 3) {
				deviceType = serial.toString().slice(1, 2);
				if (deviceType == 1 || deviceType == 5) {
					var serialCrcIn = serial % 1000;
					var serialArray = serial.toString().slice(0, -3).split('').map(function(x){return x.charCodeAt(0);});
					var crc8 = new CRC8(CRC8.POLY.CRC8_CCITT);
					var serialCrcCalc = crc8.checksum(serialArray);
					if (serialCrcIn != serialCrcCalc) {
						return "Проверьте верность серийного номера. CRC не сходятся! Введённый CRC = "+serialCrcIn+". Вычисленный CRC = "+serialCrcCalc;
					}
				}
				else 
				{
					return "Серийный номер не соответствует устройству УСПД"
				}
			} else if (serial.toString().slice(0, 1) == 1){
				deviceType = serial.toString().slice(3, 6)
				if (deviceType != 8 && deviceType != 9) {
					return "Серийный номер не соответствует устройству УСПД"
				}
			}
			else
			{
				return "Неверный формат серийного номера"
			}
		}

		// Читаем файл с прошивкой
		var bin = fs.readBinary(firmware);
				
		if (uspdSerial == 0xFFFFFFFFFFFFFFFF || (uspdSerial != serial && ui.yesno("Введённый серийный номер "+serial+".\nСохраннённый серийный номер "+uspdSerial+".\nПерезаписать?"))) {
			// Добавляем серийник
			pack.u64(serial, bin, SERIAL_AT+0);
		}
		
		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/uspd.bin", bin);
				
		// Запускаем программирование
		ui.progress(-1);
		if (! stm32.write("tmp/uspd.bin")) return "Ошибка записи в чип";
		
		// Удаляем временный файл
		fs.remove("tmp/uspd.bin");
		
		// Успешно
		return "";
	};
	
})(typeof exports === 'undefined' ? this['_uspd_js']={} : exports);
