(function(exports){
	'use strict'
	// Положение секций в прошивке
	var FLASH_PAGE_SIZE = 512;
	var FLASH_ERASED = 0xFF;
	var CONFIG_AT = 0xFE00;
	var ARH_5MIN_AT = 0x7600;
	var ARH_5MIN_PAGES = 10;

	exports.burn_config = function(firmware, type, inlet_id, min_serial, max_serial) {
		ui.progress(0);
		// Запрашиваем серийный номер
		var serial = ui.askNumber("Введите серийный номер:", 0, 999999999999999999);
		if (serial === undefined) return "Отменено";
		if (serial < min_serial || serial > max_serial)
			return "Неверный серийный номер";
		// Запрашиваем начальное значение счётчика
		var initial_value = ui.askNumber("Введите начальное показание (л):", 0, 999999999);
		if (initial_value === undefined) return "Отменено";
		// Максимально возможное значение (имеет значение только для РМ)
		var maximum_value=99999999;
		// Сколько импульсов на 1м3 (статически)
		var pulses_per_1m3 = 1000;
		// RM хранит показания в тиках x4
		initial_value *= 4 * pulses_per_1m3 / 1000;
		maximum_value *= 4 * pulses_per_1m3 / 1000;
		// Читаем файл с прошивкой
		var bin=[26];
		// Создаем конфигурацию
		pack.u64(serial, bin, 0);
		pack.u32(initial_value, bin, 8);
		pack.u32(maximum_value, bin, 12);
		pack.u32(pulses_per_1m3, bin, 16);
		pack.u16(inlet_id, bin, 20);
		pack.u16(0, bin, 22);
		pack.u16(crc.crc16(bin, 0, 24), bin, 24);
        // Создаем начальный 5-минутный архив
		var bin_arch=[];
		// Создаем первую архивную запись, чтобы установить дату и время
		var ut=(new Date()).getTime()/1000 + 60 - (new Date()).getTimezoneOffset() * 60; // получаем время +1 минуту
		pack.u32(ut, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 0);
		pack.u32(initial_value, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 4);
		pack.u8 (0, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 8);	// батарейка
		pack.u8 (0, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 9);	// флаги
		pack.u16(crc.crc16(bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 0, 10), bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 10);

		// Запускаем программирование
		ui.progress(-1);
		while (true)
		{
			if (stnflash.write(firmware, true)) break;
			if (ui.yesno("Прошивка неудачная ! Повторить ?", true)) continue; else
			{
				return "Ошибка записи в чип";
			}
		}

		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/"+type+"_config.bin", bin);
		fs.writeBinary("tmp/"+type+"_arch.bin", bin_arch);

		var result="";
		while (true)
		{
			ui.progress(-1);
			if (stnflash.write("tmp/"+type+"_config.bin", true, "0x"+Utils.toHex(0x08000000 | CONFIG_AT, 8)) &&
				stnflash.write("tmp/"+type+"_arch.bin",   true, "0x"+Utils.toHex(0x08000000 | ARH_5MIN_AT, 8))) break;
			if (ui.yesno("Прошивка конфигурации неудачная ! Повторить ?", true)) continue; else
			{
				result="Ошибка записи в чип";
				break;
			}
		}
		// Удаляем временный файл
		fs.remove("tmp/"+type+"_config.bin");
		fs.remove("tmp/"+type+"_arch.bin");
		return result;
	};
	
	exports.burn_only = function(firmware) {
		// Запускаем программирование
		ui.progress(-1);
		if (! stnflash.write(firmware, true)) return "Ошибка записи в чип";
		// Успешно
		return "";
	};
	
	exports.config_only = function(type, inlet_id, min_serial, max_serial) {
		ui.progress(0);
		// Запрашиваем серийный номер
		var serial = ui.askNumber("Введите серийный номер:", 0, 999999999999999999);
		if (serial === undefined) return "Отменено";
		if (serial < min_serial || serial > max_serial)
			return "Неверный серийный номер";
		// Запрашиваем начальное значение счётчика
		var initial_value = ui.askNumber("Введите начальное показание (л):", 0, 999999999);
		if (initial_value === undefined) return "Отменено";
		// Максимально возможное значение (имеет значение только для РМ)
		var maximum_value=99999999;
		// Сколько импульсов на 1м3 (статически)
		var pulses_per_1m3 = 1000;
		// RM хранит показания в тиках x4
		initial_value *= 4;
		maximum_value *= 4;
		// Создаем пустую прошивку
		var bin=[26];
		// Создаем конфигурацию
		pack.u64(serial, bin, 0);
		pack.u32(initial_value, bin, 8);
		pack.u32(maximum_value, bin, 12);
		pack.u32(pulses_per_1m3, bin, 16);
		pack.u16(inlet_id, bin, 20);
		pack.u16(0, bin, 22);
		pack.u16(crc.crc16(bin, 0, 24), bin, 24);
		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/"+type+"_config.bin", bin);
        // Создаем начальный 5-минутный архив
		var bin_arch=[];
		// Создаем первую архивную запись, чтобы установить дату и время
		var ut=(new Date()).getTime()/1000 + 60 - (new Date()).getTimezoneOffset() * 60; // получаем время +1 минуту
		pack.u32(ut, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 0);
		pack.u32(initial_value, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 4);
		pack.u8 (0, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 8);	// батарейка
		pack.u8 (0, bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 9);	// флаги
		pack.u16(crc.crc16(bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 0, 10), bin_arch, (FLASH_PAGE_SIZE * (ARH_5MIN_PAGES - 1)) + 10);
		// Сохраняем файл для прошивки
		fs.writeBinary("tmp/"+type+"_arch.bin", bin_arch);

		// Запускаем программирование
		var result="";
		while (true)
		{
			ui.progress(-1);
			if (stnflash.write("tmp/"+type+"_config.bin", true, "0x"+Utils.toHex(0x08000000 | CONFIG_AT, 8)) &&
				stnflash.write("tmp/"+type+"_arch.bin",   true, "0x"+Utils.toHex(0x08000000 | ARH_5MIN_AT, 8))) break;
			
			if (ui.yesno("Прошивка неудачная ! Повторить ?", true)) continue; else
			{
				result="Ошибка записи в чип";
				break;
			}
		}
		// Удаляем временный файл
		fs.remove("tmp/"+type+"_config.bin");
		fs.remove("tmp/"+type+"_arch.bin");
		return result;
	};
	
	exports.read = function(type) {
		var result="";
		ui.progress(0);
		// Читаем конфигурацию
		ui.progress(-1);
		if (! stnflash.read("tmp/"+type+".bin", "0x"+Utils.toHex(0x08000000 | CONFIG_AT, 8), 26, true)) return "Ошибка чтения из чипа";
		var fw=fs.readBinary("tmp/"+type+".bin");
		// Проверяем контрольку конфигурации
		var serial=0;
		var initial_value=0;
		var pulses_per_1m3 = 1000;
		var config;
		if (crc.crc16(fw, 0, 26) == 0)
		{
			var serial=unpack.u64(fw, 0);
			var initial_value=unpack.u32(fw, 8);
    		var maximum_value=unpack.u32(fw, 12);
			var pulses_per_1m3=unpack.u32(fw, 16);
			var inlet_id=unpack.u16(fw, 20);
			initial_value/=4;
            maximum_value/=4;
			result+="Серийный номер: "+serial+"<br/>";
			result+="Начальное показание: "+initial_value+" л<br/>";
			result+="Максимальные показание: "+maximum_value+" л<br/>";
			result+="Импульсов на 1м3: "+pulses_per_1m3+"<br/>";
			result+="Идентификатор проливной части: ";
			if (inlet_id == 0) {
				result+="Не определен";
			} else if (inlet_id == 1) {
				result+="Арзамас";
			} else if (inlet_id == 2) {
				result+="Водомер";
			} else if (inlet_id == 3) {
				result+="Пульс";
			} else {
				result+=inlet_id;
            }
			result+="<br/>";
		} else
		{
			result+="Конфигурация отсутствует<br/>";
		}

		// Читаем архив
		ui.progress(-1);
		if (! stnflash.read("tmp/"+type+"_arch.bin", "0x"+Utils.toHex(0x08000000 | ARH_5MIN_AT, 8), FLASH_PAGE_SIZE * ARH_5MIN_PAGES, true)) return "Ошибка чтения из чипа";
		var fw_arch=fs.readBinary("tmp/"+type+"_arch.bin");
		// Получаем последнюю запись из архива
		var arh=archive_init(fw_arch, 0, ARH_5MIN_PAGES);
		var last=archive_find_last(arh);
		if (last)
		{
			var ts=/^(.*)T(.*)\..*$/.exec( (new Date((last.ts + (new Date()).getTimezoneOffset() * 60)*1000)).toString() );
			var counter_l=(last.counter / 4) * 1000 / pulses_per_1m3;
			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+".bin");
		fs.remove("tmp/"+type+"_arch.bin");
		// Выводим инфу
		ui.progress(0);
		ui.message(result);
		// Успешно
		return "";
	};

	var arh_record_size = 12;
	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.u32(arh.fw, rec_addr+4),
			Vbat: unpack.u8(arh.fw, rec_addr+8),
			flags: unpack.u8(arh.fw, rec_addr+9),
		};
		
		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_rs_js']={} : exports);
