//
// This file is part of the aMule Project.
//
// Copyright (c) 2006-2009 aMule Team ( admin@amule.org / http://www.amule.org )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include "TextFile.h"
#include "Path.h"

#include <wx/textbuf.h>


//! The maximum number of chars read at once in GetNextLine
const size_t TXTBUF_SIZE = 1024;


CTextFile::CTextFile()
{
}


bool CTextFile::Open(const wxString& path, EOpenMode mode)
{
	return Open(CPath(path), mode);
}


bool CTextFile::Open(const CPath& path, EOpenMode mode)
{
	// wxFFile doesn't call close itself, but asserts instead.
	Close();

	m_mode = mode;
	
	if (mode == read) {
		if (path.FileExists()) {
			m_file.Open(path.GetRaw(), wxT("r"));
		}
	} else if (mode == write) {
		m_file.Open(path.GetRaw(), wxT("w"));
	} else {
		wxASSERT(0);
	}

	return IsOpened();
}


CTextFile::~CTextFile()
{
}


bool CTextFile::IsOpened() const
{
	return m_file.IsOpened();
}


bool CTextFile::Eof() const
{
	// This is needed because feof will crash if the
	// underlying FILE pointer is NULL, as is the 
	// case when the file is closed.
	return m_file.IsOpened() ? m_file.Eof() : true;
}


bool CTextFile::Close()
{
	return m_file.Close();
}


wxString CTextFile::GetNextLine(const wxMBConv& conv)
{
	wxCHECK_MSG(m_file.IsOpened(), wxEmptyString, wxT("Trying to read from closed file."));
	wxCHECK_MSG(!m_file.Eof(), wxEmptyString, wxT("Trying to read past EOF"));
	wxCHECK_MSG((m_mode == read), wxEmptyString, wxT("Trying to read from non-readable file."));


	wxString line;
	char buffer[TXTBUF_SIZE];

	// Loop until EOF (fgets will then return NULL) or a newline is read.
	while (fgets(buffer, TXTBUF_SIZE, m_file.fp())) {
		// NB: The majority of the time spent by this function is
		//     spent converting the multibyte string to wide-char.
		line += conv.cMB2WC(buffer);

		// Remove any newlines, carriage returns, etc.
		if (line.Length() && (line.Last() == wxT('\n'))) {
			if ((line.Length() > 1)) {
				if (line[line.Length() - 2] == wxT('\r')) {
					// Carriage return + newline
					line.RemoveLast(2);
				} else {
					// Only a newline.
					line.RemoveLast(1);					
				}
			} else {
				// Empty line
				line.Clear();
			}

			// We've read an entire line.
			break;
		}
	}

	return line;
}


bool CTextFile::WriteLine(const wxString& line, const wxMBConv& conv)
{
	wxCHECK_MSG(m_file.IsOpened(), false, wxT("Trying to read from closed file."));
	wxCHECK_MSG((m_mode == write), false, wxT("Trying to read from non-readable file."));

	// Ensures that use of newlines/carriage-returns matches the OS
	wxString result = wxTextBuffer::Translate(line);
	
	// Only add line-breaks between lines, as otherwise the number of
	// lines would grow as the result of the addition of an empty line,
	// at the end of the file.
	if (m_file.Tell() > 0) {
		result = wxTextBuffer::GetEOL() + result;
	}

	wxCharBuffer strBuffer = conv.cWC2MB(result);
	if (strBuffer) {
		const size_t length = strlen(strBuffer);

		return (m_file.Write(strBuffer, length) == length);
	}
	
	return false;
}


wxArrayString CTextFile::ReadLines(EReadTextFile flags, const wxMBConv& conv)
{
	wxArrayString lines;

	while (!Eof()) {
		wxString line = GetNextLine(conv);

		if (flags & txtStripWhitespace) {
			line = line.Strip(wxString::both);
		}

		if (flags & txtIgnoreEmptyLines) {
			if (line.IsEmpty()) {
				continue;
			}
		}
		
		if (flags & txtIgnoreComments) {
			if (flags & txtStripWhitespace) {
				if (line.StartsWith(wxT("#"))) {
					continue;
				}
			} else if (line.Strip(wxString::leading).StartsWith(wxT("#"))) {
				continue;
			}
		}

		lines.Add(line);
	}

	return lines;
}


bool CTextFile::WriteLines(const wxArrayString& lines, const wxMBConv& conv)
{
	bool result = true;

	for (size_t i = 0; i < lines.Count(); ++i) {
		result &= WriteLine(lines[i], conv);
	}

	return result;
}

