/* $Id$ */

#include <cassert>
#include <cstring>
#include <cstdio>
#include "owxendian.h"
#include "impexp.h"
#include "throw.h"
#include "util.h"
#include "intl.h"

static std::string ExportName(const unsigned char *data)
{
	std::string rs;

	for (size_t i(0); i < 6; ++i)
	{
		if (data[i] > ('Z' - 'A' + 0x0A))
			break;

		rs += std::string(1, data[i] + ((data[i] < 0x0A) ? '0' : ('A' - 0x0A)));
	}

	return rs;
}

static std::string ExportFreq(const unsigned char *data)
{
	if (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF)
		return "";

	char s[9];
	snprintf(s, sizeof(s), "%02X%02X%02X%02X", data[3], data[2], data[1], data[0]);

	std::string rs(s);
	return rs.substr(0, 3) + "." + rs.substr(3);
}

static std::string ExportCTCSS(const unsigned char *data)
{
	if (data[0] == 0xFF && data[1] == 0xFF)
		return "";

#ifdef OWX_LITTLE_ENDIAN
	unsigned short value((data[1] << 8) | data[0]);
#else
	unsigned short value((data[0] << 8) | data[1]);
#endif

	char buf[8];
	snprintf(buf, sizeof(buf), "%u.%u", value / 10, value % 10);
	return buf;
}

static std::string ExportDeviation(const unsigned char *data)
{
	return (*data & 0x10) ? "wide" : "narrow";
}

static std::string ExportTXP(const unsigned char *data)
{
	return (*data & 0x20) ? "high" : "low";
}

static std::string ExportScan(const unsigned char *data)
{
	return (*data & 0x40) ? "yes" : "no";
}

static std::string ExportBCL(const unsigned char *data)
{
	if (*data == 0x00 || *data == 0xF7)
		return "no";

	if (*data == 0x08 || *data == 0xFF)
		return "yes";

	char buf[5];
	snprintf(buf, sizeof(buf), "0x%02X", *data);
	return buf;
}

static void ExportOne(CCsv &csv, unsigned row, const unsigned char *data, bool withName)
{
	csv.Write(row, 0, Util::IntToStr(row));
	csv.Write(row, 2, ExportFreq(data + 0x00));
	csv.Write(row, 3, ExportFreq(data + 0x04));
	csv.Write(row, 4, ExportCTCSS(data + 0x08));
	csv.Write(row, 5, ExportCTCSS(data + 0x0A));
	csv.Write(row, 6, ExportDeviation(data + 0x0D));
	csv.Write(row, 7, ExportTXP(data + 0x0D));
	csv.Write(row, 8, ExportScan(data + 0x0D));
	csv.Write(row, 9, ExportBCL(data + 0x0C));

	if (withName)
		csv.Write(row, 1, ExportName(data + 0x1000));
}

static std::string ExportRangeEntry(const unsigned char *data)
{
	unsigned char values[4];
	static const unsigned char transtbl[] = { 2, 7, 5, 8, 10, 10, 10, 0, 10, 3, 1, 4, 10, 10, 6, 9 };

	values[0] = data[0] >> 4;
	values[1] = data[0] & 0x0F;
	values[2] = data[1] >> 4;
	values[3] = data[1] & 0x0F;

	unsigned short value(0);
	unsigned short multiplier(1000);

	for (size_t i(0); i < 4; ++i)
	{
		unsigned char v(transtbl[values[i]]);
		if (v == 10)
		{
			// fallback
			char buf[7];
			snprintf(buf, sizeof(buf), "0x%02X%02X", data[1], data[0]);
			return buf;
		}

		value += v * multiplier;
		multiplier /= 10;
	}

	char buf[6];
	snprintf(buf, sizeof(buf), "%u", value);
	return buf;
}

static std::string ExportRadio(const unsigned char *data)
{
	if (data[0] == 0xFF && data[1] == 0xFF)
		return "";

#ifdef OWX_LITTLE_ENDIAN
	unsigned short value((data[0] << 8) | data[1]);
#else
	unsigned short value((data[1] << 8) | data[0]);
#endif

	char buf[8];
	snprintf(buf, sizeof(buf), "%u.%u", value / 10 + 76, value % 10);
	return buf;
}

static std::string ExportWelcome(const unsigned char *data)
{
	std::string out;

	for (size_t i(0); i < 6; ++i)
	{
		if (data[i] == 0x00 || data[i] == 0xFF)
			break;

		if (data[i] < 0x20 || data[i] > 0x7F || data[i] == '"' || data[i] == '\\')
		{
			char buf[4];
			snprintf(buf, sizeof(buf), "\\%02X", data[i]);
			out += buf;
		}
		else
			out += std::string(1, data[i]);
	}

	return out;
}

void Export(CCsv &csv, const char *buf, size_t bufsz, const CModel &model)
{
	assert(bufsz == model.GetMemorySize());

	const unsigned char *p(reinterpret_cast<const unsigned char *>(buf));

	csv.Write(0, 0, _("CH"));
	csv.Write(0, 1, _("Name"));
	csv.Write(0, 2, _("RX Frequency"));
	csv.Write(0, 3, _("TX Frequency"));
	csv.Write(0, 4, _("RX CTCSS"));
	csv.Write(0, 5, _("TX CTCSS"));
	csv.Write(0, 6, _("Deviation"));
	csv.Write(0, 7, _("TX Power"));
	csv.Write(0, 8, _("Scan"));
	csv.Write(0, 9, _("BCL"));

	size_t i;
	for (i = 1; i <= model.GetChannelsCount(); ++i)
		ExportOne(csv, i, p + i * 0x10, model.UseChannelNames());

	unsigned offset(model.GetChannelsCount() + 2);

	if(model.AreRangesSupported()) {
		csv.Write(offset + 0, 0, _("VHF RX Range"));
		csv.Write(offset + 1, 0, _("VHF TX Range"));
		csv.Write(offset + 2, 0, _("UHF RX Range"));
		csv.Write(offset + 3, 0, _("UHF TX Range"));

		csv.Write(offset + 0, 1, ExportRangeEntry(p + model.GetRangesOffset() + 0x70));
		csv.Write(offset + 0, 2, ExportRangeEntry(p + model.GetRangesOffset() + 0x72));
		csv.Write(offset + 1, 1, ExportRangeEntry(p + model.GetRangesOffset() + 0x78));
		csv.Write(offset + 1, 2, ExportRangeEntry(p + model.GetRangesOffset() + 0x7A));
		csv.Write(offset + 2, 1, ExportRangeEntry(p + model.GetRangesOffset() + 0x74));
		csv.Write(offset + 2, 2, ExportRangeEntry(p + model.GetRangesOffset() + 0x76));
		csv.Write(offset + 3, 1, ExportRangeEntry(p + model.GetRangesOffset() + 0x7C));
		csv.Write(offset + 3, 2, ExportRangeEntry(p + model.GetRangesOffset() + 0x7E));

		offset += 5;
	}

	if (model.UseFmRadio())
	{
		for (i = 1; i < 10; ++i)
		{
			char buf[256];
			snprintf(buf, sizeof(buf), _("FM%u"), (unsigned) i);
			csv.Write(offset + i - 1, 0, buf);
			csv.Write(offset + i - 1, 1, ExportRadio(p + 0x840 + i * 2));
		}

		offset += 10;
	}

	if (model.GetModel() == CModel::MODEL_UVA1)
	{
		csv.Write(offset + 0, 0, _("UVA1 programmed button (1=scan 3=SOS 4=FM)"));
		csv.Write(offset + 0, 1, Util::Format("%X", p[0x63B]));

		csv.Write(offset + 1, 0, _("UVA1 VOX activation and level (0=off 1-10=level)"));
		csv.Write(offset + 1, 1, Util::Format("%d", p[0x627]));

		csv.Write(offset + 2, 0, _("UVA1 roger beep (0=off 1=begin 2=end 3=both)"));
		csv.Write(offset + 2, 1, Util::Format("%X", p[0x625]));

		csv.Write(offset + 3, 0, _("UVA1 battery save flag (0=off 1=on)"));
		csv.Write(offset + 3, 1, Util::Format("%X", p[0x623]));

		csv.Write(offset + 4, 0, _("UVA1 squelch level (0-9)"));
		csv.Write(offset + 4, 1, Util::Format("%X", p[0x622]));

		csv.Write(offset + 5, 0, _("UVA1 TOT (Time-Out Timer) in seconds (15s-600s in 15s increments)"));
		csv.Write(offset + 5, 1, Util::Format("%u", (p[0x626] + 1) * 15));

		csv.Write(offset + 6, 0, _("UVA1 TOA (Time-Out Pre-Alert) in seconds (0-10)"));
		csv.Write(offset + 6, 1, Util::Format("%u", p[0x636]));

		csv.Write(offset + 7, 0, _("UVA1 Voice Guide (0=off 1=chinese 2=english)"));
		csv.Write(offset + 7, 1, Util::Format("%X", p[0x62C]));

		csv.Write(offset + 8, 0, _("UVA1 Beep Flag (0=off 1=on)"));
		csv.Write(offset + 8, 1, Util::Format("%X", p[0x62D]));

		csv.Write(offset + 9, 0, _("UVA1 Priority Channel (0=off 1-16=channel)"));
		csv.Write(offset + 9, 1, Util::Format("%u", p[0x65E]));

		offset += 11;
	}

	if (model.UseWelcomeMessage())
	{
		csv.Write(offset, 0, _("UVD3 Welcome Message"));
		csv.Write(offset, 1, ExportWelcome(p + 0x1827));
		offset += 2;
	}
}
