/***************************************************************************
 *   Copyright (C) 2005 by Joris Guisson                                   *
 *   joris.guisson@gmail.com                                               *
 *                                                                         *
 *   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 Street, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/

#include <kurl.h>
#include <kprocess.h>
#include <tdelocale.h>
#include <tqdatetime.h>
#include <tqtextstream.h>
#include <tqfile.h>
#include <tqptrlist.h>
#include <iostream>
#include <stdlib.h>
#include <torrent/globals.h>
#include <interfaces/logmonitorinterface.h>
#include <tqmutex.h> 
#include <util/fileops.h>
#include <stdlib.h>
#include "log.h"
#include "error.h"
#include "autorotatelogjob.h"

using namespace kt;

namespace bt
{
	const Uint32 MAX_LOG_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
	
	class Log::Private
	{
	public:
		Log* parent;
		TQTextStream* out;
		TQFile fptr;
		bool to_cout;
		TQPtrList<LogMonitorInterface> monitors;
		TQString tmp;
		TQMutex mutex;
		unsigned int m_filter;
		AutoRotateLogJob* rotate_job;
	public:
		Private(Log* parent) : parent(parent),out(0),to_cout(false),rotate_job(0)
		{
			out = new TQTextStream();
		}

		~Private()
		{
			delete out;
		}

		
		void setFilter(unsigned int filter)
		{
			m_filter = filter;
		}
		
		void rotateLogs(const TQString & file)
		{
			if (bt::Exists(file + "-10.gz"))
				bt::Delete(file + "-10.gz",true);
			
			// move all log files one up
			for (Uint32 i = 10;i > 1;i--)
			{
				TQString prev = TQString("%1-%2.gz").arg(file).arg(i - 1);
				TQString curr = TQString("%1-%2.gz").arg(file).arg(i);
				if (bt::Exists(prev))
					bt::Move(prev,curr,true);
			}
			
			// move current log to 1 and zip it
			bt::Move(file,file + "-1",true);
			system(TQString("gzip " + TDEProcess::quote(file + "-1")).local8Bit());
		}

		void setOutputFile(const TQString & file)
		{
			if (fptr.isOpen())
				fptr.close();
			
			if (bt::Exists(file))
				rotateLogs(file);

			fptr.setName(file);
			if (!fptr.open(IO_WriteOnly))
				throw Error(i18n("Cannot open log file %1 : %2").arg(file).arg(fptr.errorString()));

			out->setDevice(&fptr);
		}

		void write(const TQString & line)
		{
			tmp += line;
		}
		
		void finishLine()
		{
			// only add stuff when we are not rotating the logs
			// this could result in the loss of some messages
			if (!rotate_job) 
			{
				*out << TQDateTime::currentDateTime().toString() << ": " << tmp << ::endl;
				fptr.flush();
				if (to_cout)
					std::cout << TQString(tmp.local8Bit()) << std::endl;
				
				if (monitors.count() > 0)
				{
					TQPtrList<LogMonitorInterface>::iterator i = monitors.begin();
					while (i != monitors.end())
					{
						kt::LogMonitorInterface* lmi = *i;
						lmi->message(tmp,m_filter);
						i++;
					}
				}
			}
			tmp = "";
		}

		void endline()
		{
			finishLine();
			if (fptr.size() > MAX_LOG_FILE_SIZE && !rotate_job)
			{
				tmp = "Log larger then 10 MB, rotating";
				finishLine();
				TQString file = fptr.name();
				fptr.close(); // close the log file
				out->setDevice(0);		
				// start the rotate job
				rotate_job = new AutoRotateLogJob(file,parent); 
			}
		}
		
		void logRotateDone()
		{
			fptr.open(IO_WriteOnly);
			out->setDevice(&fptr);
			rotate_job = 0;
		}
	};
	
	Log::Log() 
	{
		priv = new Private(this);
	}
	
	
	Log::~Log()
	{
		delete priv;
	}
	
	
	void Log::setOutputFile(const TQString & file)
	{
		priv->setOutputFile(file);
	}

	void Log::addMonitor(kt::LogMonitorInterface* m)
	{
		priv->monitors.append(m);
	}

	void Log::removeMonitor(kt::LogMonitorInterface* m)
	{
		priv->monitors.remove(m);
	}

	void Log::setOutputToConsole(bool on)
	{
		priv->to_cout = on;
	}

	Log & endl(Log & lg)
	{
		lg.priv->endline();
		lg.priv->mutex.unlock(); // unlock after end of line
		return lg;
	}

	Log & Log::operator << (const KURL & url)
	{
		priv->write(url.prettyURL());
		return *this;
	}

	Log & Log::operator << (const TQString & s)
	{
		priv->write(s);
		return *this;
	}

	Log & Log::operator << (const char* s)
	{
		priv->write(s);
		return *this;
	}

	Log & Log::operator << (Uint64 v)
	{
		return operator << (TQString::number(v));
	}

	Log & Log::operator << (Int64 v)
	{
		return operator << (TQString::number(v));
	}

	void Log::setFilter(unsigned int filter)
	{
		priv->setFilter(filter);
	}

	void Log::lock()
	{
		priv->mutex.lock();
	}
	
	void Log::logRotateDone()
	{
		priv->logRotateDone();
	}
	
	Log & Out(unsigned int arg)
	{
		Log & lg = Globals::instance().getLog(arg);
		lg.lock();
		return lg;
	}
}