(function(exports){
	'use strict'
	
	// Положение секций в прошивке
	var FLASH_PAGE_SIZE = 128;
	var FLASH_ERASED = 0;
	var CONFIG_AT = 0xDD00;
	var ARH_5MIN_AT = 0xDE00;
	var ARH_5MIN_PAGES = (0x1400 / FLASH_PAGE_SIZE);
	
	
	exports.burn_config = function(firmware, fw_rev, type) {
		
		if ( (type!='rm') && (type!='wdm') )
			throw new Error("bad type");
		
		ui.progress(0);
		
		// Запрашиваем серийный номер
		var serial = ui.askNumber("Введите серийный номер:", 0, 999999999999999999);
		if (serial === undefined) return "Отменено";
		
		// Запрашиваем начальное значение счётчика
		var initial_value = ui.askNumber("Введите начальное показание (л):", 0, 999999999);
		if (initial_value === undefined) return "Отменено";
		var initial_value_erp = initial_value;
		
		// Максимально возможное значение (имеет значение только для РМ)
		var maximum_value=99999999;
		
		// Сколько импульсов на 1м3 (статически)
		var pulses_per_1m3 = (type=='rm') ? 1000 : ~~(46.5 * 2 * 1000);
		
		if (type=='rm')
		{
			// RM хранит показания в тиках x4
			initial_value *= 4 * pulses_per_1m3 / 1000;
			maximum_value *= 4 * pulses_per_1m3 / 1000;
		}
		
		// Читаем файл с прошивкой
		var bin = fs.readBinary(firmware);
		
		// Делаем размер прошивки 64кб, чтобы стереть старый архив
		if (bin.length < 65536)
			bin[65535]=0;
		
		
		// Создаем конфигурацию
		if (type == 'rm')
		{
			// У РМ есть еще максимальное значение, чтобы соответствовать счётному механизму
			pack.u64(serial, bin, CONFIG_AT+0);
			pack.u32(initial_value, bin, CONFIG_AT+8);
			pack.u32(maximum_value, bin, CONFIG_AT+12);
			pack.u32(pulses_per_1m3, bin, CONFIG_AT+16);
			pack.u16(crc.crc16(bin, CONFIG_AT, 20), bin, CONFIG_AT+20);
		} else
		{
			pack.u64(serial, bin, CONFIG_AT+0);
			pack.u32(initial_value, bin, CONFIG_AT+8);
			pack.u32(pulses_per_1m3, bin, CONFIG_AT+12);
			pack.u16(crc.crc16(bin, CONFIG_AT, 16), bin, CONFIG_AT+16);
		}
		
		// Создаем первую архивную запись, чтобы установить дату и время
		var ut=(new Date()).getTime()/1000 + 60;	// получаем время +1 минуту
		pack.u32(ut, bin, ARH_5MIN_AT+0);
		pack.u32(initial_value, bin, ARH_5MIN_AT+4);
		pack.u16(0, bin, ARH_5MIN_AT+8);	// расход
		pack.u8 (0, bin, ARH_5MIN_AT+10);	// батарейка
		pack.u8 (0, bin, ARH_5MIN_AT+11);	// флаги
		pack.u16(0, bin, ARH_5MIN_AT+12);	// резерв
		pack.u16(crc.crc16(bin, ARH_5MIN_AT, 14), bin, ARH_5MIN_AT+14);
		
		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/"+type+"_cmt.bin", bin);
		
		// Запускаем программирование
		var result="";
		while (true)
		{
			ui.progress(-1);
			if (stm32.write("tmp/"+type+"_cmt.bin", true)) break;
			
			if (ui.yesno("Прошивка неудачная ! Повторить ?", true)) continue; else
			{
				result="Ошибка записи в чип";
				break;
			}
		}
		
		// Удаляем временный файл
		fs.remove("tmp/"+type+"_cmt.bin");
		
		// Отправляем данные на ERP
		erp.meter((type=="rm") ? "RWCS-3915" : "RWCS-3920", (type=="rm") ? "PM-CMT" : "WDM", fw_rev, serial, initial_value_erp, result=="");
		
		return result;
	};
	
	
	exports.burn_only = function(firmware, fw_rev) {
		
		// Запускаем программирование
		ui.progress(-1);
		if (! stm32.write(firmware, true)) return "Ошибка записи в чип";
		
		// Успешно
		return "";
	};
	
	
	exports.config_only = function(firmware, fw_rev, type) {
		
		if ( (type!='rm') && (type!='wdm') )
			throw new Error("bad type");
		
		ui.progress(0);
		
		// Запрашиваем серийный номер
		var serial = ui.askNumber("Введите серийный номер:", 0, 999999999999999999);
		if (serial === undefined) return "Отменено";
		
		// Запрашиваем начальное значение счётчика
		var initial_value = ui.askNumber("Введите начальное показание (л):", 0, 999999999);
		if (initial_value === undefined) return "Отменено";
		var initial_value_erp = initial_value;
		
		// Максимально возможное значение (имеет значение только для РМ)
		var maximum_value=99999999;
		
		// Сколько импульсов на 1м3 (статически)
		var pulses_per_1m3 = (type=='rm') ? 1000 : ~~(46.5 * 2 * 1000);
		
		if (type=='rm')
		{
			// RM хранит показания в тиках x4
			initial_value *= 4 * pulses_per_1m3 / 1000;
			maximum_value *= 4 * pulses_per_1m3 / 1000;
		}
		
		// Создаем пустую прошивку
		var bin=[];
		bin[65535]=0;
		
		
		// Создаем конфигурацию
		if (type == 'rm')
		{
			// У РМ есть еще максимальное значение, чтобы соответствовать счётному механизму
			pack.u64(serial, bin, CONFIG_AT+0);
			pack.u32(initial_value, bin, CONFIG_AT+8);
			pack.u32(maximum_value, bin, CONFIG_AT+12);
			pack.u32(pulses_per_1m3, bin, CONFIG_AT+16);
			pack.u16(crc.crc16(bin, CONFIG_AT, 20), bin, CONFIG_AT+20);
		} else
		{
			pack.u64(serial, bin, CONFIG_AT+0);
			pack.u32(initial_value, bin, CONFIG_AT+8);
			pack.u32(pulses_per_1m3, bin, CONFIG_AT+12);
			pack.u16(crc.crc16(bin, CONFIG_AT, 16), bin, CONFIG_AT+16);
		}
		
		// Создаем первую архивную запись, чтобы установить дату и время
		var ut=(new Date()).getTime()/1000 + 60;	// получаем время +1 минуту
		pack.u32(ut, bin, ARH_5MIN_AT+0);
		pack.u32(initial_value, bin, ARH_5MIN_AT+4);
		pack.u16(0, bin, ARH_5MIN_AT+8);	// расход
		pack.u8 (0, bin, ARH_5MIN_AT+10);	// батарейка
		pack.u8 (0, bin, ARH_5MIN_AT+11);	// флаги
		pack.u16(0, bin, ARH_5MIN_AT+12);	// резерв
		pack.u16(crc.crc16(bin, ARH_5MIN_AT, 14), bin, ARH_5MIN_AT+14);
		
		// Получаем нужный кусок бинарника
		var bin2=[];
		system.arrayCopy(bin2, 0, bin, CONFIG_AT);
		
		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/"+type+"_cmt_config.bin", bin2);
		
		// Запускаем программирование
		var result="";
		while (true)
		{
			ui.progress(-1);
			if (stm32.write("tmp/"+type+"_cmt_config.bin", true, "0x"+Utils.toHex(0x08000000 | CONFIG_AT, 8))) break;
			
			if (ui.yesno("Прошивка неудачная ! Повторить ?", true)) continue; else
			{
				result="Ошибка записи в чип";
				break;
			}
		}
		
		// Удаляем временный файл
		fs.remove("tmp/"+type+"_cmt_config.bin");
		
		// Отправляем данные на ERP
		erp.meter((type=="rm") ? "RWCS-3915" : "RWCS-3920", (type=="rm") ? "PM-CMT" : "WDM", fw_rev, serial, initial_value_erp, result=="");
		
		return "";
	};
	
	
	exports.read = function(type) {
		
		if ( (type!='rm') && (type!='wdm') )
			throw new Error("bad type");
		
		var result="";
		
		ui.progress(0);
		
		// Читаем прошивку
		ui.progress(-1);
		if (! stm32.read("tmp/"+type+"_cmt.bin", "0x08000000", 65536, true)) return "Ошибка чтения из чипа";
		
		// Читаем прошивки из файлов и сравниваем
		var fw=fs.readBinary("tmp/"+type+"_cmt.bin");
		
		// Проверяем контрольку конфигурации
		var serial=0;
		var initial_value=0;
		var pulses_per_1m3 = (type=='rm') ? 1000 : ~~(46.5 * 2 * 1000);
		var config;
		if (crc.crc16(fw, CONFIG_AT, 18) == 0)
		{
			var serial=unpack.u64(fw, CONFIG_AT+0);
			var initial_value=unpack.u32(fw, CONFIG_AT+8);
			var pulses_per_1m3=unpack.u32(fw, CONFIG_AT+12);
			
			if (type=='rm') initial_value/=4;
			
			result+="Серийный номер: "+serial+"<br/>";
			result+="Начальное показание: "+initial_value+" л<br/>";
			result+="Импульсов на 1м3: "+pulses_per_1m3+"<br/>";
		} else
		{
			result+="Конфигурация отсутствует<br/>";
		}
		
		// Получаем последнюю запись из архива
		var arh=archive_init(fw, ARH_5MIN_AT, ARH_5MIN_PAGES);
		var last=archive_find_last(arh);
		if (last)
		{
			var ts=/^(.*)T(.*)\..*$/.exec( (new Date(last.ts*1000)).toString() );
			var counter_l;
			
			if (type=='rm')
			{
				counter_l=(last.counter / 4) * 1000 / pulses_per_1m3;
			} else
			{
				counter_l=last.counter;
			}
			
			result+="Дата: "+ts[1]+"<br/>";
			result+="Время: "+ts[2]+"<br/>";
			result+="Показание: "+counter_l.toFixed(0)+" л<br/>";
			result+="Батарейка: "+(last.Vbat / 10).toFixed(1)+" В<br/>";
		} else
		{
			result+="Последнее показание отсутствует<br/>";
		}
		
		// Удаляем временный файл
		fs.remove("tmp/"+type+"_cmt.bin");
		
		// Выводим инфу
		ui.progress(0);
		ui.message(result);
		
		// Успешно
		return "";
	};
	
	
	var arh_record_size = 16;
	var records_per_page = ~~(FLASH_PAGE_SIZE / arh_record_size);
	
	
	function archive_init(fw, addr, n_pages)
	{
		var arh={
			fw: fw,
			addr: addr,
			n_pages: n_pages,
			size: n_pages * records_per_page
		};
		
		// Ищем последнюю страницу
		for (var page=0; page<arh.n_pages; page++)
		{
			// Определяем занятость страницы
			var free_at;
			for (free_at=FLASH_PAGE_SIZE; (free_at > 0) && (arh.fw[arh.addr + free_at - 1] == FLASH_ERASED); free_at--);
			
			// Проверим, есть ли в странице место хотя бы под одну запись
			if (free_at <= (records_per_page-1)*arh_record_size)
			{
				arh.next_rec=page * records_per_page + ~~((free_at + arh_record_size-1) / arh_record_size);
				return arh;
			}
		}
		
		// Ошибка
		arh.next_rec=0;
		
		return arh;
	}
	
	
	function archive_read(arh, n)
	{
		if (n >= arh.size-1) return null;
		
		// Получаем индекс
		var idx=(arh.next_rec + arh.size - 1 - n) % arh.size;
		
		// Получаем номер страницы и смещение внутри страницы
		var page=~~(idx / records_per_page);
		var offset=(idx % records_per_page) * arh_record_size;
		
		// Получаем адрес записи
		var rec_addr=arh.addr + page * FLASH_PAGE_SIZE + offset;
		
		// Проверяем контрольку записи
		if (crc.crc16(arh.fw, rec_addr, arh_record_size) != 0) return null;
		
		// Получаем запись
		var rec={
			ts: unpack.u32(arh.fw, rec_addr+0),
			counter: unpack.i32(arh.fw, rec_addr+4),
			flow: unpack.i16(arh.fw, rec_addr+8),
			Vbat: unpack.u8(arh.fw, rec_addr+10),
			flags: unpack.u8(arh.fw, rec_addr+11),
		};
		
		// Разбираем флаги
		rec.Vbat_low=(rec.flags & 0x01) != 0;
		rec.tamper=(rec.flags & 0x02) != 0;
		rec.magnet=(rec.flags & 0x04) != 0;
		delete rec.flags;
		
		return rec;
	}
	
	
	function archive_find_last(arh)
	{
		for (var i=0; i<arh.size; i++)
		{
			var rec=archive_read(arh, i);
			if (rec) return rec;
		}
		return null;
	}
	
})(typeof exports === 'undefined' ? this['_rm_wdm_cmt_js']={} : exports);
