/* $Id$ */

#include <cstdlib>
#include <cstring>
#include "throw.h"
#include "chan.h"
#include "util.h"

std::string Chan::GetName(const uint8_t *data)
{
	std::string rs;
	size_t i;

	for (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;
}

void Chan::SetName(uint8_t *out, const std::string &in)
{
	memset(out, 0xFF, 6);

	for (size_t i(0); i < in.size(); ++i)
	{
		if (i == 6)
			Throw("Chan name %s too long (6 ch max)", in.c_str());

		if (in[i] >= '0' && in[i] <= '9')
			out[i] = in[i] - '0';
		else if (in[i] >= 'A' && in[i] <= 'Z')
			out[i] = in[i] - 'A' + 0x0A;
		else
			Throw("Invalid character in chan name %s, only uppercase letters and digits allowed", in.c_str());
	}
}

std::string Chan::GetFreq(const uint8_t *data)
{
	if (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF)
		return "0";

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

	std::string rs;
	size_t si(0);
	for (size_t i(0); i < 9; ++i)
		rs += std::string(1, (i == 3) ? '.' : s[si++]);

	return rs;
}

void Chan::SetFreq(uint8_t *out, const std::string &in)
{
	if (in == "" || in == "0")
	{
		memset(out, 0xFF, 4);
		return;
	}

	if (in.size() != 9 || in[3] != '.')
		Throw("Invalid frequency %s", in.c_str());

	std::string hexstr(in.substr(0, 3) + in.substr(4, 5));
	out[3] = Util::FromHex(hexstr.c_str() + 0);
	out[2] = Util::FromHex(hexstr.c_str() + 2);
	out[1] = Util::FromHex(hexstr.c_str() + 4);
	out[0] = Util::FromHex(hexstr.c_str() + 6);
}

std::string Chan::GetCTCSS(const uint8_t *data)
{
	if (data[0] == 0xFF && data[1] == 0xFF)
		return "0";

	long value((data[1] << 8) | data[0]);

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

void Chan::SetCTCSS(uint8_t *out, const std::string &in)
{
	if (in == "" || in == "0")
	{
		memset(out, 0xFF, 2);
		return;
	}

	std::string s;
	for (std::string::const_iterator i(in.begin()); i != in.end(); ++i)
		if (*i != '.')
			s += std::string(1, *i);

	unsigned v(strtol(s.c_str(), NULL, 10));

	out[1] = v >> 8;
	out[0] = v & 0xFF;
}

std::string Chan::GetBusyLock(const uint8_t &data)
{
	// nie dekodujemy na razie

/*
	if (data == 0 || data == 0xF7)
		return "off";

	if (data == 8 || data == 0xFF)
		return "carrier";
*/

	char buf[4];
	snprintf(buf, sizeof(buf), "%02X", data);
	return buf;
}

void Chan::SetBusyLock(uint8_t *out, const std::string &in)
{
	*out = Util::FromHex(in.c_str());
}

std::string Chan::GetTXMode(const uint8_t &data)
{
	// bit 5: power (1 - high, 0 - low)
	// bit 4: width (1 - wide, 0 - narrow)

	// nie dekodujemy tego teraz

	char buf[4];
	snprintf(buf, sizeof(buf), "%02X", data);
	return buf;
}

void Chan::SetTXMode(uint8_t *out, const std::string &in)
{
	*out = Util::FromHex(in.c_str());
}

std::string Chan::GetDunno(const uint8_t *data)
{
	char buf[5];
	snprintf(buf, sizeof(buf), "%02X%02X", data[1], data[0]);
	return buf;
}

void Chan::SetDunno(uint8_t *out, const std::string &in)
{
	out[1] = Util::FromHex(in.c_str());
	out[0] = Util::FromHex(in.c_str() + 2);
}

void Chan::BufToBin(SBin &bin, const unsigned char *data)
{
	memcpy(&bin.rfreq, data + 0x00, 4);
	memcpy(&bin.tfreq, data + 0x04, 4);
	memcpy(&bin.rctc, data + 0x08, 2);
	memcpy(&bin.tctc, data + 0x0A, 2);
	bin.busylock = data[0x0C];
	bin.txmode = data[0x0D];
	memcpy(&bin.dunno, data + 0x0E, 2);
	memcpy(&bin.name, data + 0x1000, 6);
}

void Chan::BinToCsv(SCsv &csv, const SBin &bin)
{
	csv.name = GetName(bin.name);
	csv.rfreq = GetFreq(bin.rfreq);
	csv.tfreq = GetFreq(bin.tfreq);
	csv.rctc = GetCTCSS(bin.rctc);
	csv.tctc = GetCTCSS(bin.tctc);
	csv.busylock = GetBusyLock(bin.busylock);
	csv.txmode = GetTXMode(bin.txmode);
	csv.dunno = GetDunno(bin.dunno);
}

void Chan::CsvToString(std::string &s, const SCsv &csv, size_t i)
{
	char buf[8];
	snprintf(buf, sizeof(buf), "%u", i + 1);

	s = buf;
	s += "," + csv.name;
	s += "," + csv.rfreq;
	s += "," + csv.tfreq;
	s += "," + csv.rctc;
	s += "," + csv.tctc;
	s += "," + csv.busylock;
	s += "," + csv.txmode;
	s += "," + csv.dunno;
}

void Chan::StringToCsv(SCsv &csv, const std::string &s)
{
	std::vector<std::string> v;
	Util::TokenizeCSV(v, s);

	if (v.size() != 9)
		Throw("Invalid or incomplete CSV line: %s", s.c_str());

	csv.name = v[1];
	csv.rfreq = v[2];
	csv.tfreq = v[3];
	csv.rctc = v[4];
	csv.tctc = v[5];
	csv.busylock = v[6];
	csv.txmode = v[7];
	csv.dunno = v[8];
}

void Chan::CsvToBin(SBin &bin, const SCsv &csv)
{
	SetName(bin.name, csv.name);
	SetFreq(bin.rfreq, csv.rfreq);
	SetFreq(bin.tfreq, csv.tfreq);
	SetCTCSS(bin.rctc, csv.rctc);
	SetCTCSS(bin.tctc, csv.tctc);
	SetBusyLock(&bin.busylock, csv.busylock);
	SetTXMode(&bin.txmode, csv.txmode);
	SetDunno(bin.dunno, csv.dunno);
}

void Chan::BinToBuf(unsigned char *buf, const SBin &bin)
{
	memcpy(buf + 0x00, &bin.rfreq, 4);
	memcpy(buf + 0x04, &bin.tfreq, 4);
	memcpy(buf + 0x08, &bin.rctc, 2);
	memcpy(buf + 0x0A, &bin.tctc, 2);
	buf[0x0C] = bin.busylock;
	buf[0x0D] = bin.txmode;
	memcpy(buf + 0x0E, &bin.dunno, 2);
	memcpy(buf + 0x1000, &bin.name, 6);
}
