/*
 *
 * Description  : Support class for files writing
 * Author       : Michael Smutny smutnm2@fel.cvut.cz, Antonín Míšek antonin.misek@skoda-auto.cz
 *
 * Purpose      : Support class for files writing

 * Example for write back
	wxFileOutputStream sceneStream(wxT("test.txt"));
	if (sceneStream.IsOk())
	{
		wxBufferedOutputStream sceneStream2(sceneStream, 1000000);
		TextWriter tw(&sceneStream2);
		size_t pos=tw.Write(wxT("a"))->FlushBuffer()->Flushable(false)->GetIntPosition();//write first character, flush buffer, disable flushing and store current position
		size_t pos1=tw.Write(wxT("bcd"))->GetIntPosition();//write next characters and store end position
		tw.SetIntPosition(pos)->Write(wxT("01"))->SetIntPosition(pos1)->Write(wxT("efgh"))->Flushable(true);//go to position and rewrite characters, go to end position and write rest of characters, enable flushing
		sceneStream2.Sync();
		return true;
	}
 */

#ifndef __TEXTWRITER__H__
#define __TEXTWRITER__H__

#include <wx/stream.h>
#include "common.h"

class TextWriter
{
private:
	wxBufferedOutputStream * outputStream;

public:
	TextWriter(wxBufferedOutputStream * _outputStream) : outputStream(_outputStream) {}
	
	~TextWriter()
	{
		Flushable(true);
	}

	void Send(void)
	{
	}

	unsigned char GetCompressionLevel(void)
	{
		return 0;
	}

	///send whole buffer to the disc
	TextWriter* FlushBuffer()
	{
		Flushable(true);
		outputStream->GetOutputStreamBuffer()->FlushBuffer();
		return this;
	}

	///set flushable and fixed property of the output buffer
	///it is useful for writing behind actual position
	TextWriter* Flushable(bool flushable)
	{
		outputStream->GetOutputStreamBuffer()->Flushable(flushable);
		outputStream->GetOutputStreamBuffer()->Fixed(flushable);
		return this;
	}

	///gets the actual position in output buffer
	size_t GetIntPosition()
	{
		return outputStream->GetOutputStreamBuffer()->GetIntPosition();
	}
	
	///sets the actual position in output buffer
	TextWriter* SetIntPosition(size_t pos)
	{
		outputStream->GetOutputStreamBuffer()->SetIntPosition(pos);
		return this;
	}

	///Write NULL terminated character string to output stream
	TextWriter* Write(const char *str)
	{
		outputStream->Write(str, strlen(str));
		return this;
	}
	
	///Write character string to output stream
	TextWriter* Write(const char *str, size_t size)
	{
		outputStream->Write(str, size);
		return this;
	}

	///Write character string to output stream
	TextWriter* Write(const std::string &str)
	{
		outputStream->Write(str.c_str(), str.size());
		return this;
	}

	///Write float to output stream
	TextWriter* Write(const float &val)
	{
		if (fabs(val) <= 3.4e-038)
			return Write("0", 1);
		if (_finite(val)==0)
		{
			if (_isnan(val))
				return Write("1.#IND000", 9);
			if (val<0)
				return Write("-1.#INF000", 10);
			else
				return Write("1.#INF000", 9);
		}
		char tmp[30];
		char *index=tmp+29;
		*index=0;
		int lg=log10l(fabs(val));
		int lgr=lg;
		float rpart=val;
		if ((lg<-2) || (lg>7))
		{
			if (lg<0)
				lg--;
			rpart/=pow(10.0f, lg);
			lgr=1;
			int lgtmp=abs(lg);
			do {
				index--;
				*index = (char)(lgtmp%10) + '0';
			} while (lgtmp /= 10);
			if (lg<0)
			{
				index--;
				*index='-';
			}
			index--;
			*index='e';
		}
		int ipart = rpart;
		rpart=fabs(rpart-ipart);
		int count=7-lgr;
		if (ipart==0)
			count++;
		long ripart = rpart*pow(10.0f, count)/*+0.5f*/;
		if (ripart!=0)
		{
			while (ripart%10==0)
			{
				ripart/=10;
				count--;
			}
			while (count--)
			{
				index--;
				*index = (char)(ripart%10) + '0';
				ripart/=10;
			}
			index--;
			*index='.';
		}
		ipart=abs(ipart);
		do {
			index--;
			*index = (char)(ipart%10) + '0';
		} while (ipart /= 10);
		if (val<0)
		{
			index--;
			*index='-';
		}
		return Write(index, tmp+29-index);
	}
	
	///Write integer to output stream
	TextWriter* Write(const int &val)
	{
		if (val == 0)
			return Write("0", 1);
		char tmp[30];
		char *index=tmp+29;
		*index=0;
		int numLeft = abs(val);
		do {
			index--;
			*index = (char)(numLeft%10) + '0';
		} while (numLeft /= 10);
		if (val < 0)
		{
			index--;
			*index = '-';
		}
		return Write(index, tmp+29-index);
	}
	
	///Write unsigned integer to output stream
	TextWriter* Write(const unsigned int &val)
	{
		return Write((int)val);
	}

	///Write long to output stream
	TextWriter* Write(const long &val)
	{
		if (val == 0)
			return Write("0", 1);
		char tmp[30];
		char *index=tmp+29;
		*index=0;
		long numLeft = (val>0)?val:-val;
		do {
			index--;
			*index = (char)(numLeft%10) + '0';
			
		} while (numLeft /= 10);
		if (val < 0)
		{
			index--;
			*index = '-';
		}
		return Write(index, tmp+29-index);
	}

/*  This method is disabled because:
		1. it will not be possible to do it such way in wxWidgets 3.0
		2. for correct support of ASCII/UTF8 in IO modules is necessary to decide conversion directly in appropriate IO module
	Please use for wxString variables:
	for ASCII: str.char_str(wxConvISO8859_1)
	for UTF8: str.utf8_str()
	///Write \c wxString to output stream
	TextWriter* Write(const wxString &str)
	{
		return Write(str.mb_str(wxConvISO8859_1), str.size());
	}//*/

	///Write float array to output stream, put delimiter after every value
	TextWriter* Write(const float *values, int num, const char *delimiter)
	{
		for (int i=0; i<num; i++)
		{
			if (i)
				Write(delimiter);
			Write(values[i]);
		}
		return this;
	}

	///Write float array to output stream, binary version
	TextWriter* WriteBin(unsigned long val, int numBytes)
	{
		for (int i=0; i<numBytes; i++)
		{
			unsigned char tmp=val;
			outputStream->Write(&tmp, 1);
			val = (val>>8);
		}
		return this;
	}

	///Write float array to output stream, binary version
	TextWriter* WriteBin(const float *values, int num)
	{
		for (int i=0; i<num; i++)
			outputStream->Write(&values[i], sizeof(float));
		return this;
	}

	///Write size_t to wxString
	static wxString WriteToWxStringSize_t(size_t val)
	{
		if (val==0)
			return wxT("0");
		char tmp[30];
		char *index=tmp+29;
		*index=0;
		do {
			index--;
			*index = (char)(val%10) + '0';
		} while (val /= 10);
		return wxString::FromAscii(index);
	}

	///Write float to wxString
	static wxString WriteToWxString(const float &val)
	{
		if (fabs(val) <= 3.4e-038)
			return wxT("0");
		if (_finite(val)==0)
		{
			if (_isnan(val))
				return wxT("1.#IND000");
			if (val<0)
				return wxT("-1.#INF000");
			else
				return wxT("1.#INF000");
		}
		char tmp[30];
		char *index=tmp+29;
		*index=0;
		int lg=log10l(fabs(val));
		int lgr=lg;
		float rpart=val;
		if ((lg<-2) || (lg>7))
		{
			if (lg<0)
				lg--;
			rpart/=pow(10.0f, lg);
			lgr=1;
			int lgtmp=abs(lg);
			do {
				index--;
				*index = (char)(lgtmp%10) + '0';
			} while (lgtmp /= 10);
			if (lg<0)
			{
				index--;
				*index='-';
			}
			index--;
			*index='e';
		}
		int ipart = rpart;
		rpart=fabs(rpart-ipart);
		int count=7-lgr;
		if (ipart==0)
			count++;
		long ripart = rpart*pow(10.0f, count)/*+0.5f*/;
		if (ripart!=0)
		{
			while (ripart%10==0)
			{
				ripart/=10;
				count--;
			}
			while (count--)
			{
				index--;
				*index = (char)(ripart%10) + '0';
				ripart/=10;
			}
			index--;
			*index='.';
		}
		ipart=abs(ipart);
		do {
			index--;
			*index = (char)(ipart%10) + '0';
		} while (ipart /= 10);
		if (val<0)
		{
			index--;
			*index='-';
		}
		return wxString::FromAscii(index);
	}

	///Write float to wxString
	static wxString WriteToWxString(const float *values, int num, const wxString &delim)
	{
		wxString res;
		for (int i=0; i<num; i++)
		{
			if (i!=0)
				res+=delim;
			res+=WriteToWxString(*values);
			values++;
		}
		return res;
	}

	//make relative path
	//TODO: current implementation is trivial, enhance it
	static wxString GetRelativePath(const wxString &basePath, wxString path)
	{
		if (path.substr(0, basePath.size())==basePath)
			path=path.substr(basePath.size());
		return path;
	}
};


#endif
