/*  $Id: dcc_download_generic.cpp,v 1.5 2003/09/24 17:53:20 mbn Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This library 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
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "dcc_download_generic.h"
#include "API/Core/IOData/outputsource_provider.h"
#include "API/Core/IOData/outputsource_file.h"
#include "API/Core/System/system.h"
#include "API/Core/System/event_listener.h"
#include "API/Core/System/error.h"
#include "API/Core/System/log.h"
#ifdef WIN32
#include "winsock.h"
#else
#include <netinet/in.h>
#endif

/////////////////////////////////////////////////////////////////////////////
// CL_DCCDownload_Generic Construction:

CL_DCCDownload_Generic::CL_DCCDownload_Generic(
	const std::string &server,
	const std::string &port,
	const std::string &filename,
	int total_size,
	CL_OutputSourceProvider *provider,
	bool delete_provider)
	:
	server(server), port(port), filename(filename), total_size(total_size),
	provider(provider), delete_provider(delete_provider), status(CL_DCCDownload::connecting),
	send_signal(no_signal)
{
	thread = CL_Thread(this);
	thread.start();
}

CL_DCCDownload_Generic::~CL_DCCDownload_Generic()
{
	shutdown_trigger.set_flag();
	thread.wait();
	if (delete_provider) delete provider;
}

/////////////////////////////////////////////////////////////////////////////
// CL_DCCDownload_Generic Attributes:

/////////////////////////////////////////////////////////////////////////////
// CL_DCCDownload_Generic Operations:

void CL_DCCDownload_Generic::add_ref()
{
	ref_count++;
}

void CL_DCCDownload_Generic::release_ref()
{
	ref_count--;
	if (ref_count == 0) delete this;
}

/////////////////////////////////////////////////////////////////////////////
// CL_DCCDownload_Generic Implementation:

void CL_DCCDownload_Generic::run()
{
	while (shutdown_trigger.get_flag() == false)
	{
		CL_MutexSection mutex_lock(&mutex);
		if (status != CL_DCCDownload::connecting)
		{
			CL_System::sleep(100);
			continue;
		}

		try
		{
			status = CL_DCCDownload::connecting;
			mutex_lock.leave();
		
			CL_Socket sock(CL_Socket::tcp);
			sock.connect(CL_IPAddress(server, port));
			sock.set_nodelay();
			
			mutex_lock.enter();
			status = CL_DCCDownload::downloading;
			mutex_lock.leave();
			
			int total_read = 0;

			CL_OutputSource_File outputfile(filename);
			char buffer[64*1024];
			while (true)
			{
				CL_EventListener listener;
				listener.add_trigger(sock.get_read_trigger());
				listener.add_trigger(&shutdown_trigger);
				listener.add_trigger(&reconnect_trigger);
				listener.wait();
				if (shutdown_trigger.get_flag()) break;
				if (reconnect_trigger.get_flag())
				{
					reconnect_trigger.reset();
					mutex_lock.enter();
					status = CL_DCCDownload::connecting;
					mutex_lock.leave();
					sock = CL_Socket(CL_Socket::tcp);
					sock.connect(CL_IPAddress(server, port));
					continue;
				}

				int data_read = sock.input.read(buffer, 64*1024);
				if (data_read == 0) break;
				total_read += data_read;
				outputfile.write(buffer, data_read);
				unsigned int total_read_network_order = htonl(total_read);
				sock.output.write(&total_read_network_order, 4);
			}
			
			mutex_lock.enter();
			status = CL_DCCDownload::finished;
			send_signal = complete_signal;
			mutex_lock.leave();
			break;
		}
		catch (CL_Error err)
		{
			mutex_lock.enter();
			status = CL_DCCDownload::connection_lost;
			send_signal = lost_signal;
			error = err.message;
			mutex_lock.leave();
		}
	}
}
	
void CL_DCCDownload_Generic::keep_alive()
{
	CL_MutexSection mutex_lock(&mutex);
	if (send_signal == complete_signal)
	{
		send_signal = no_signal;
		mutex_lock.leave();
		sig_download_complete();
	}
	else if (send_signal == lost_signal)
	{
		std::string err = error;
		send_signal = no_signal;
		mutex_lock.leave();
		sig_connection_lost(err);
	}
}
