/**************************************************************

file_classes.h (C-Munipack project)
Classes for various file formats
Copyright (C) 2008 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#ifndef MUNIWIN_FILE_CLASSES_H
#define MUNIWIN_FILE_CLASSES_H

#include <stdint.h>
#include <stdio.h>

//
// dBase file parser
//
class CDBFReader
{
public:
	// Constructor
	CDBFReader(FILE *f);

	// Destructor
	virtual ~CDBFReader();

	// Get number of header columns
	int Columns(void) const
	{ return m_ColCount; }

	// Read next record
	bool Next();

	// Get field name
	const char *FieldName(int col) const;

	// Get field data
	bool GetField(int index, char *buf, int buflen);

private:
	// File header
	struct tDBFHeader
	{
		uint8_t		version, year, month, day;
		uint32_t	count;
		uint16_t	head_len, rec_length;
		int8_t		reserved[20];
	};

	// Field descriptor
	struct tDBFField
	{
		char		name[16];
		int16_t		start, length;
	};

	FILE		*m_File;
	tDBFField	*m_Cols;
	char		*m_Record;
	int			m_ColCount;
	tDBFHeader	m_Header;

	// Disable copy constructor
	CDBFReader(const CDBFReader&);
};

//
// Text file parser (fixed positions)
//
class CTextReader
{
public:
	// Constructor
	CTextReader(FILE *f);

	// Destructor
	virtual ~CTextReader();

	// Get next line
	bool ReadLine();

	// Get field value
	bool GetField(int start, int length, char *buf, int buflen) const;

	// Get line length
	size_t Length(void) const
	{ return m_LineLength; }

	// Get character at specified position
	char Col(int index) const
	{ return m_Line[index]; }

private:
	class CFileReader *m_Reader;
	char *m_Line;
	size_t m_LineLength;

	// Disable copy constructor
	CTextReader(const CTextReader&);
};

//
// CSV File base class (RFC 4180 compliant)
//
class CCSVFileBase
{
public:
	enum tFlags {
		SkipFieldNames = (1<<0),	// Skip field names
		FieldsWithHash = (1<<1)		// Start line with field names with hash (#)
	};

	// Constructor
	CCSVFileBase(char field_sep, char quote_char);
	virtual ~CCSVFileBase();

protected:
	struct tCSVRecord {
		int count, capacity;
		char **list;
	};

	struct tCSVField {
		char *field;
		char *defValue;
	};

	struct tCSVHeader {
		int count, capacity;
		tCSVField *list;
	};

	char		m_FieldSep, m_QuoteChar;
	tCSVHeader	m_Header;

	// Returns true if the buffer contains valid header or data line
	bool IsValid(const char *buf) const;

	// Parse table header and fill the header structure
	void ParseHeader(const char *buf, tCSVHeader &header);

	// Split line in buffer into fields. The separator characters
	// are replaced by nul terminators. Returns number of fields found.
	void Explode(const char *buf, tCSVRecord &data);

	// Make record line in the m_Line buffer
	char *Implode(const tCSVRecord &data, const char *startWith = NULL);

	// Set value in data record
	static void SetData(tCSVRecord &data, int column, char *buf);

	// Free allocated memory
	static void ClearData(tCSVRecord &data);

	// Free allocated memory
	static void ClearHeader(tCSVHeader &data);

	// Quote a string if necessary (it contains a quote character or a field separator)
	// Quotes inside a quoted string are replaced by two subsequent quotes
	char *StrQuote(const char *str, bool quote_always);

	// If the string is quoted (first non-ws character is quote), remove quotes
	// Two subsequent quotes in a quoted string are replaced by a single quote
	char *StrUnquote(const char *str);
};

//
// CSV file reader
//
class CCSVReader:public CCSVFileBase
{
public:
	// Constructor
	CCSVReader(FILE *f, char field_sep = ',', char quote_char = '"');

	// Destructor
	virtual ~CCSVReader();

	// Get next line
	bool ReadLine(void);

	// Get number of header columns
	int Columns(void) const
	{ return m_Header.count; }

	// Get column name
	const char *ColumnName(int column) const;

	// Get field value
	const char *GetStr(int column, const char *defval = NULL) const;
	bool GetInt(int column, int *value) const;
	int GetInt(int column, int defval = 0) const;
	bool GetDbl(int column, double *value) const;
	double GetDbl(int column, double defval = 0.0) const;
	bool GetBool(int column, bool *value) const;
	bool GetBool(int column, bool defval = false) const;

private:
	class CFileReader *m_Reader;
	tCSVRecord	m_Rec;

	// Disable copy constructor
	CCSVReader(const CCSVReader&);
};

class CCSVWriter:public CCSVFileBase
{
public:
	// Constructor
	CCSVWriter(FILE *f, char field_sep = ',', char quote_char = '"', unsigned flags = 0);

	// Destructor
	virtual ~CCSVWriter();

	// Set format of metadata
	void SetMetaFormat(char prefix, char keysep);

	// Append a line with metadata in format #Key=Value
	void AddMeta(const char *key, const char *value);

	// Append a single line
	void AddLine(const char *line = 0);

	// Append a new column. Returns column index 
	int AddColumn(const char *name, const char *defvalue = 0);

	// Add a new record at the end of the file
	void Append(void);

	// Flush unsaved data and close the file
	void Close(void);

	// Set field value
	void SetStr(int column, const char *value);
	void SetInt(int column, int value);
	void SetDbl(int column, double value, int prec = 5);
	void SetBool(int column, bool value);

private:
	enum tState { EMPTY_FILE, HEADER, EMPTY_DATA, DATA };

	FILE		*m_File;
	unsigned	m_Flags;
	tCSVRecord	m_Rec;
	tState		m_State;
	char		m_MetaPrefix, m_MetaKeySep;

	// Make table header from the header structure
	char *MakeHeader(const tCSVHeader &header);

	// Flush unsaved data to a file
	void Flush(void);

	// Disable copy constructor
	CCSVWriter(const CCSVWriter&);
};

//
// XML parser
//
class CXMLParser
{
public:
	// Callback prototypes
	typedef void StartElementHandler(const char *element, const char **attr, void *user_data);
	typedef void EndElementHandler(const char *element, void *user_data);
	typedef void CharacterDataHandler(const char *data, int len, void *user_data);

public:
	// Constructor
	CXMLParser(FILE *f);

	// Destructor
	virtual ~CXMLParser();

	// Parse
	void Parse(StartElementHandler *start_proc, EndElementHandler *end_proc,
		CharacterDataHandler *cdata_proc, void *user_data);

private:
	StartElementHandler		*m_StartCB;
	EndElementHandler		*m_EndCB;
	CharacterDataHandler	*m_CDataCB;
	void					*m_UserData;
	FILE					*m_File;

	// Disable copy constructor
	CXMLParser(const CXMLParser&);
};

#endif
