/* $Id$ */

#include <assert.h>
#include "throw.h"
#include "intl.h"
#include "util.h"
#include "file.h"
#include "csv.h"

#define MAX_SIZE 65536

CCsv::CCsv(unsigned rows, unsigned cols): m_rows(rows), m_cols(cols)
{
	m_data.resize(rows * cols);
}

size_t CCsv::Index(unsigned row, unsigned col) const
{
	assert(row < m_rows);
	assert(col < m_cols);

	return row * m_cols + col;
}

const std::string &CCsv::Read(unsigned row, unsigned col) const
{
	return m_data[Index(row, col)];
}

void CCsv::Write(unsigned row, unsigned col, const std::string &s)
{
	m_data[Index(row, col)] = s;
}

void CCsv::Load(const std::string &path)
{
	CFileRead f;
	f.Open(path.c_str());

	size_t sz(f.Size());
	if (sz > MAX_SIZE)
		Throw(_("CSV file too large"));

	char buf[sz];
	if (f.Read(buf, sz) != sz)
		Throw(_("Cannot read CSV file"));

	f.Close();

	Explode(std::string(buf, sz));
}

void CCsv::Save(const std::string &path)
{
	std::string s(Implode());

	CFileWrite f;
	f.Open(path.c_str());
	f.Write(s.data(), s.size());
	f.Close();
}

void CCsv::Explode(std::string s)
{
	m_data.clear();
	m_data.resize(m_rows * m_cols);

	for (unsigned row(0); row < m_rows; ++row)
	{
		if (s.empty())
			Throw(_("CSV file too short, only %u rows read"), row);

		std::string line(Util::NextToken(s, "\n"));
		if (line.size() >= 1 && line[line.size() - 1] == '\r')
			line.erase(line.size() - 1);

		for (unsigned col(0); col < m_cols; ++col)
		{
			// simplified parsing because a comma cannot occur anyway

			std::string token(Util::NextToken(line, ",;"));

			if (token.size() >= 2 && token[0] == '"' && token[token.size() - 1] == '"')
				token = token.substr(1, token.size() - 2);

			// xxx desanitize token - replace "" with "

			Write(row, col, token);
		}
	}
}

std::string CCsv::Implode()
{
	std::string rs;

	for (unsigned row(0); row < m_rows; ++row)
	{
		for (unsigned col(0); col < m_cols; ++col)
		{
			if (col)
				rs += std::string(1, ',');

			rs += std::string(1, '"');
			rs += Util::Sanitize(Read(row, col));
			rs += std::string(1, '"');
		}

		rs += "\r\n";
	}

	return rs;
}
