/* 
 *	Copyright (C) 2003-2006 Gabest
 *	http://www.gabest.org
 *
 *  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, 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "stdafx.h"
#include <atlbase.h>
#include <afxinet.h>
#include "TextFile.h"

CTextFile::CTextFile(enc e)
{
	m_encoding = m_defaultencoding = e;
	m_offset = 0;
}

bool CTextFile::Open(LPCTSTR lpszFileName)
{
	if(!__super::Open(lpszFileName, modeRead|typeBinary|shareDenyWrite))
		return(false);

	m_encoding = m_defaultencoding;
	m_offset = 0;

	if(__super::GetLength() >= 2)
	{
		WORD w;
		if(sizeof(w) != Read(&w, sizeof(w)))
			return Close(), false;

		if(w == 0xfeff)
		{
			m_encoding = LE16;
			m_offset = 2;
		}
		else if(w == 0xfffe)
		{
			m_encoding = BE16;
			m_offset = 2;
		}
		else if(w == 0xbbef && __super::GetLength() >= 3)
		{
			BYTE b;
			if(sizeof(b) != Read(&b, sizeof(b)))
				return Close(), false;

			if(b == 0xbf)
			{
				m_encoding = UTF8;
				m_offset = 3;
			}
		}
	}

	if(m_encoding == m_defaultencoding)
	{
		__super::Close(); // CWebTextFile::Close() would delete the temp file if we called it...
		if(!__super::Open(lpszFileName, modeRead|typeText|shareDenyWrite))
			return(false);
	}

	return(true);
}

bool CTextFile::Save(LPCTSTR lpszFileName, enc e)
{
	if(!__super::Open(lpszFileName, modeCreate|modeWrite|shareDenyWrite|(e==ASCII?typeText:typeBinary)))
		return(false);

	if(e == UTF8)
	{
		BYTE b[3] = {0xef,0xbb,0xbf};
		Write(b, sizeof(b));
	}
	else if(e == LE16)
	{
		BYTE b[2] = {0xff,0xfe};
		Write(b, sizeof(b));
	}
	else if(e == BE16)
	{
		BYTE b[2] = {0xfe,0xff};
		Write(b, sizeof(b));
	}

	m_encoding = e;

	return true;
}

void CTextFile::SetEncoding(enc e)
{
	m_encoding = e;
}

CTextFile::enc CTextFile::GetEncoding()
{
	return m_encoding;
}

bool CTextFile::IsUnicode()
{
	return m_encoding == UTF8 || m_encoding == LE16 || m_encoding == BE16;
}

// CFile

CString CTextFile::GetFilePath() const
{
	// to avoid a CException coming from CTime
	return m_strFileName; // __super::GetFilePath();
}

// CStdioFile

ULONGLONG CTextFile::GetPosition() const
{
	return(CStdioFile::GetPosition() - m_offset);
}

ULONGLONG CTextFile::GetLength() const
{
	return(CStdioFile::GetLength() - m_offset);
}

ULONGLONG CTextFile::Seek(LONGLONG lOff, UINT nFrom)
{
	ULONGLONG pos = GetPosition();
	ULONGLONG len = GetLength();

	switch(nFrom)
	{
	default:
	case begin: lOff = lOff; break;
	case current: lOff = pos + lOff; break;
	case end: lOff = len - lOff; break;
	}

	lOff = max(min(lOff, len), 0) + m_offset;

	pos = CStdioFile::Seek(lOff, begin) - m_offset;

	return(pos);
}

void CTextFile::WriteString(LPCSTR lpsz/*CStringA str*/)
{
	CStringA str(lpsz);

	if(m_encoding == ASCII)
	{
		__super::WriteString(AToT(str));
	}
	else if(m_encoding == ANSI)
	{
		str.Replace("\n", "\r\n");
		Write((LPCSTR)str, str.GetLength());
	}
	else if(m_encoding == UTF8)
	{
		WriteString(AToW(str));
	}
	else if(m_encoding == LE16)
	{
		WriteString(AToW(str));
	}
	else if(m_encoding == BE16)
	{
		WriteString(AToW(str));
	}
}

void CTextFile::WriteString(LPCWSTR lpsz/*CStringW str*/)
{
	CStringW str(lpsz);

	if(m_encoding == ASCII)
	{
		__super::WriteString(WToT(str));
	}
	else if(m_encoding == ANSI)
	{
		str.Replace(L"\n", L"\r\n");
		CStringA stra = CStringA(CString(str)); // TODO: codepage
		Write((LPCSTR)stra, stra.GetLength());
	}
	else if(m_encoding == UTF8)
	{
		str.Replace(L"\n", L"\r\n");
		for(int i = 0; i < str.GetLength(); i++)
		{
			DWORD c = (WORD)str[i];

			if(0 <= c && c < 0x80) // 0xxxxxxx
			{
				Write(&c, 1);
			}
			else if(0x80 <= c && c < 0x800) // 110xxxxx 10xxxxxx
			{
				c = 0xc080|((c<<2)&0x1f00)|(c&0x003f);
				Write((BYTE*)&c+1, 1);
				Write(&c, 1);
			}
			else if(0x800 <= c && c < 0xFFFF) // 1110xxxx 10xxxxxx 10xxxxxx
			{
				c = 0xe08080|((c<<4)&0x0f0000)|((c<<2)&0x3f00)|(c&0x003f);
				Write((BYTE*)&c+2, 1);
				Write((BYTE*)&c+1, 1);
				Write(&c, 1);
			}
			else
			{
				c = '?';
				Write(&c, 1);
			}
		}
	}
	else if(m_encoding == LE16)
	{
		str.Replace(L"\n", L"\r\n");
		Write((LPCWSTR)str, str.GetLength()*2);
	}
	else if(m_encoding == BE16)
	{
		str.Replace(L"\n", L"\r\n");
		for(int i = 0; i < str.GetLength(); i++)
			str.SetAt(i, ((str[i]>>8)&0x00ff)|((str[i]<<8)&0xff00));
		Write((LPCWSTR)str, str.GetLength()*2);
	}
}

BOOL CTextFile::ReadString(CStringA& str)
{
	bool fEOF = true;

	str.Empty();

	if(m_encoding == ASCII)
	{
		CString s;
		fEOF = !__super::ReadString(s);
		str = TToA(s);
	}
	else if(m_encoding == ANSI)
	{
		char c;
		while(Read(&c, sizeof(c)) == sizeof(c))
		{
			fEOF = false;
			if(c == '\r') continue;
			if(c == '\n') break;
			str += c;
		}
	}
	else if(m_encoding == UTF8)
	{
		BYTE b;
		while(Read(&b, sizeof(b)) == sizeof(b))
		{
			fEOF = false;
			char c = '?';
			if(!(b&0x80)) // 0xxxxxxx
			{
				c = b&0x7f;
			}
			else if((b&0xe0) == 0xc0) // 110xxxxx 10xxxxxx
			{
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
			}
			else if((b&0xf0) == 0xe0) // 1110xxxx 10xxxxxx 10xxxxxx
			{
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
			}
			if(c == '\r') continue;
			if(c == '\n') break;
			str += c;
		}
	}
	else if(m_encoding == LE16)
	{
		WORD w;
		while(Read(&w, sizeof(w)) == sizeof(w))
		{
			fEOF = false;
			char c = '?';
			if(!(w&0xff00)) c = w&0xff;
			if(c == '\r') continue;
			if(c == '\n') break;
			str += c;
		}
	}
	else if(m_encoding == BE16)
	{
		WORD w;
		while(Read(&w, sizeof(w)) == sizeof(w))
		{
			fEOF = false;
			char c = '?';
			if(!(w&0xff)) c = w>>8;
			if(c == '\r') continue;
			if(c == '\n') break;
			str += c;
		}
	}

	return(!fEOF);
}

BOOL CTextFile::ReadString(CStringW& str)
{
	bool fEOF = true;

	str.Empty();

	if(m_encoding == ASCII)
	{
		CString s;
		fEOF = !__super::ReadString(s);
		str = TToW(s);
	}
	else if(m_encoding == ANSI)
	{
		CStringA stra;
		char c;
		while(Read(&c, sizeof(c)) == sizeof(c))
		{
			fEOF = false;
			if(c == '\r') continue;
			if(c == '\n') break;
			stra += c;
		}
		str = CStringW(CString(stra)); // TODO: codepage
	}
	else if(m_encoding == UTF8)
	{
		BYTE b;
		while(Read(&b, sizeof(b)) == sizeof(b))
		{
			fEOF = false;
			WCHAR c = '?';
			if(!(b&0x80)) // 0xxxxxxx
			{
				c = b&0x7f;
			}
			else if((b&0xe0) == 0xc0) // 110xxxxx 10xxxxxx
			{
				c = (b&0x1f)<<6;
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
				c |= (b&0x3f);
			}
			else if((b&0xf0) == 0xe0) // 1110xxxx 10xxxxxx 10xxxxxx
			{
				c = (b&0x0f)<<12;
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
				c |= (b&0x3f)<<6;
				if(Read(&b, sizeof(b)) != sizeof(b)) break;
				c |= (b&0x3f);
			}
			if(c == '\r') continue;
			if(c == '\n') break;
			str += c;
		}
	}
	else if(m_encoding == LE16)
	{
		WCHAR wc;
		while(Read(&wc, sizeof(wc)) == sizeof(wc))
		{
			fEOF = false;
			if(wc == '\r') continue;
			if(wc == '\n') break;
			str += wc;
		}
	}
	else if(m_encoding == BE16)
	{
		WCHAR wc;
		while(Read(&wc, sizeof(wc)) == sizeof(wc))
		{
			fEOF = false;
			wc = ((wc>>8)&0x00ff)|((wc<<8)&0xff00);
			if(wc == '\r') continue;
			if(wc == '\n') break;
			str += wc;
		}
	}

	return(!fEOF);
}

//
// CWebTextFile
//

CWebTextFile::CWebTextFile(LONGLONG llMaxSize)
	: m_llMaxSize(llMaxSize)
{
}

bool CWebTextFile::Open(LPCTSTR lpszFileName)
{
	CString fn(lpszFileName);

	if(fn.Find(_T("http://")) != 0)
		return __super::Open(lpszFileName);

	try
	{
		CInternetSession is;

		CAutoPtr<CStdioFile> f(is.OpenURL(fn, 1, INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_EXISTING_CONNECT));
		if(!f) return(false);

		TCHAR path[MAX_PATH];
		GetTempPath(MAX_PATH, path);

		fn = path + fn.Mid(fn.ReverseFind('/')+1);
		int i = fn.Find(_T("?"));
		if(i > 0) fn = fn.Left(i);
		CFile temp;
		if(!temp.Open(fn, modeCreate|modeWrite|typeBinary|shareDenyWrite))
		{
			f->Close();
			return(false);
		}

		BYTE buff[1024];
		int len, total = 0;
		while((len = f->Read(buff, 1024)) == 1024 && (m_llMaxSize < 0 || (total+=1024) < m_llMaxSize))
			temp.Write(buff, len);
		if(len > 0) temp.Write(buff, len);

		m_tempfn = fn;

		f->Close(); // must close it because the desctructor doesn't seem to do it and we will get an exception when "is" is destroying
	}
	catch(CInternetException* ie)
	{
		ie->Delete();
		return(false);
	}

	return __super::Open(m_tempfn);
}

bool CWebTextFile::Save(LPCTSTR lpszFileName, enc e)
{
	// CWebTextFile is read-only...
	ASSERT(0);
	return(false);
}

void CWebTextFile::Close()
{
	__super::Close();

	if(!m_tempfn.IsEmpty())
	{
		_tremove(m_tempfn);
		m_tempfn.Empty();
	}
}

///////////////////////////////////////////////////////////////

CStringW AToW(CStringA str)
{
	CStringW ret;
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (WCHAR)(BYTE)str[i];
	return(ret);
}

CStringA WToA(CStringW str)
{
	CStringA ret;
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (CHAR)(WORD)str[i];
	return(ret);
}

CString AToT(CStringA str)
{
	CString ret;
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (TCHAR)(BYTE)str[i];
	return(ret);
}

CString WToT(CStringW str)
{
	CString ret;
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (TCHAR)(WORD)str[i];
	return(ret);
}

CStringA TToA(CString str)
{
	CStringA ret;
#ifdef UNICODE
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (CHAR)(BYTE)str[i];
#else
	ret = str;
#endif
	return(ret);
}

CStringW TToW(CString str)
{
	CStringW ret;
#ifdef UNICODE
	ret = str;
#else
	for(int i = 0, j = str.GetLength(); i < j; i++)
		ret += (WCHAR)(BYTE)str[i];
#endif
	return(ret);
}