/* === S Y N F I G ========================================================= */
/*!	\file cvs.cpp
**	\brief Template File
**
**	$Id$
**
**	\legal
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**
**	This package 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 package 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.
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include "cvs.h"
#include <ETL/stringf>
#include <fstream>
#include <iostream>
#include <cstdlib>


#include <sys/types.h>
#include <sys/stat.h>

#include <cassert>

#include <synfigapp/localization.h>

#endif

/* === U S I N G =========================================================== */

using namespace std;
using namespace etl;
using namespace synfig;
using namespace synfigapp;

/* === M A C R O S ========================================================= */

#define cvs_command		synfig::String("cvs -z4")

#ifndef _WIN32
#define HAVE_STRPTIME
#endif

#if defined(__APPLE__) || defined(__OpenBSD__)
time_t _daylight_() { time_t t(time(0)); return localtime(&t)->tm_gmtoff; }
#define daylight _daylight_()
#endif

/* === G L O B A L S ======================================================= */

/* === P R O C E D U R E S ================================================= */

/* === M E T H O D S ======================================================= */

CVSInfo::CVSInfo(const synfig::String& file_name)
{
	update_available_=false;
	set_file_name(file_name);
}

CVSInfo::CVSInfo():
	in_sandbox_(),
	in_repository_(),
	update_available_(false),
	original_timestamp_()
{ }

CVSInfo::~CVSInfo()
{
}

void
CVSInfo::set_file_name(const synfig::String& file_name)
{
	file_name_=file_name;

	std::ifstream file((dirname(file_name_)+"/CVS/Root").c_str());

	if(file)
	{
		in_sandbox_=true;
		calc_repository_info();
	}
	else
		in_sandbox_=false;
}

void
CVSInfo::calc_repository_info()
{
#ifdef _DEBUG
	synfig::info("in_sandbox() = %d",in_sandbox());
#endif

	if(!in_sandbox_)
		return;

	std::ifstream file((dirname(file_name_)+"/CVS/Entries").c_str());

	while(file)
	{
		String line;
		getline(file,line);
		if(line.find(basename(file_name_))!=String::npos)
		{
			in_repository_=true;
			String::size_type s,f;

			// Grab the version
			s=line.find('/',1);
			assert(s!=String::npos);
			s++;
			f=line.find('/',s+1);
			assert(f!=String::npos);
			cvs_version_=String(line,s,f-s);

			// Grab the time
#ifdef HAVE_STRPTIME
			s=f+1;
			f=line.find('/',s+1);
			assert(f!=String::npos);
			tm time_struct;
			strptime(String(line,s,f-s).c_str(),"%c",&time_struct);
			original_timestamp_=mktime(&time_struct);
#endif

			if(
				system(strprintf(
					"cd '%s' && cvs status '%s' | grep -q -e 'Needs Patch'",
					dirname(file_name_).c_str(),
					basename(file_name_).c_str()
				).c_str())==0
			)
			{
				synfig::info("UPDATE_AVAILABLE=TRUE");
				update_available_=true;
			}
			else
			{
				system(strprintf(
					"cd '%s' && cvs status '%s'",
					dirname(file_name_).c_str(),
					basename(file_name_).c_str()
				).c_str());
				synfig::info("UPDATE_AVAILABLE=FALSE");
				update_available_=false;
			}


#ifdef _DEBUG
			synfig::info("in_repository() = %d",in_repository());
			synfig::info("get_cvs_version() = %s",get_cvs_version().c_str());
			synfig::info("get_original_timestamp() = %s",ctime(&get_original_timestamp()));
			time_t t(get_current_timestamp());
			synfig::info("get_current_timestamp() = %s",ctime(&t));
			synfig::info("get_cvs_root() = %s",get_cvs_root().c_str());
			synfig::info("get_cvs_module() = %s",get_cvs_module().c_str());
#endif
			return;
		}
	}

	in_repository_=false;
	cvs_version_.clear();
	original_timestamp_=0;

#ifdef _DEBUG
	synfig::info("in_repository() = %d",in_repository());
#endif
}

bool
CVSInfo::in_sandbox()const
{
	return in_sandbox_;
}

bool
CVSInfo::in_repository()const
{
	if(!in_sandbox_)
		return false;
	return in_repository_;
}

bool
CVSInfo::is_modified()const
{
	if(!in_sandbox() || !in_repository())
		return false;
#ifdef _DEBUG
	synfig::info("%d-%d=%d",get_current_timestamp(),get_original_timestamp(),get_current_timestamp()-get_original_timestamp());
#endif
	return get_current_timestamp()!=get_original_timestamp() && abs(get_current_timestamp()-get_original_timestamp())!=3600;
}

bool
CVSInfo::is_updated()const
{
	return update_available_;
}

const synfig::String&
CVSInfo::get_cvs_version()const
{
	return cvs_version_;
}

const time_t&
CVSInfo::get_original_timestamp()const
{
	return original_timestamp_;
}

time_t
CVSInfo::get_current_timestamp()const
{
	struct stat st;
	if(stat(file_name_.c_str(),&st)<0)
	{
		synfig::error("Unable to get file stats");
		return false;
	}
	time_t ret((daylight-1)*3600);
	//ret+=timezone;
	ret+=st.st_mtime;
	return ret;
}

synfig::String
CVSInfo::get_cvs_root()const
{
	if(!in_sandbox_)
		return synfig::String();

	std::ifstream file((dirname(file_name_)+"/CVS/Root").c_str());

	if(file)
	{
		String ret;
		getline(file,ret);
		return ret;
	}

	return synfig::String();
}

synfig::String
CVSInfo::get_cvs_module()const
{
	if(!in_sandbox_)
		return synfig::String();

	std::ifstream file((dirname(file_name_)+"/CVS/Repository").c_str());

	if(file)
	{
		String ret;
		getline(file,ret);
		return ret;
	}

	return synfig::String();
}

// This function pre-processes the message so that we
// don't get any CVS syntax errors.
inline synfig::String fix_msg(const synfig::String& message)
{
	synfig::String ret;
	int i;
	for(i=0;i<(int)message.size();i++)
	{
		if(message[i]=='\'')
			ret+="'\"'\"'";
		else
			ret+=message[i];
	}
	return ret;
}

void
CVSInfo::cvs_add(const synfig::String& message)
{
	if(!in_sandbox_)
	{
		synfig::error("cvs_add(): Not in a sand box");
		throw int();
		return;
	}

	synfig::String command(strprintf("cd '%s' && %s add -m '%s' '%s'",dirname(file_name_).c_str(),cvs_command.c_str(),fix_msg(message).c_str(),basename(file_name_).c_str()));

	int ret(system(command.c_str()));

	calc_repository_info();

	switch(ret)
	{
	case 0:
		break;
	default:
		synfig::error("Unknown errorcode %d (\"%s\")",ret,command.c_str());
		throw(ret);
		break;
	}
}

void
CVSInfo::cvs_update()
{
	if(!in_sandbox_)
	{
		synfig::error("cvs_update(): Not in a sand box");
		throw int();
		return;
	}

	synfig::String command(strprintf("cd '%s' && %s update '%s'",dirname(file_name_).c_str(),cvs_command.c_str(),basename(file_name_).c_str()));

	int ret(system(command.c_str()));

	calc_repository_info();

	switch(ret)
	{
	case 0:
		break;
	default:
		synfig::error("Unknown errorcode %d (\"%s\")",ret,command.c_str());
		throw(ret);
		break;
	}
}

void
CVSInfo::cvs_commit(const synfig::String& message)
{
	if(!in_sandbox_)
	{
		synfig::error("cvs_commit(): Not in a sand box");
		throw int();
		return;
	}

	synfig::String command(strprintf("cd '%s' && %s commit -m '%s' '%s'",dirname(file_name_).c_str(),cvs_command.c_str(),fix_msg(message).c_str(),basename(file_name_).c_str()));

	int ret(system(command.c_str()));

	calc_repository_info();

	switch(ret)
	{
	case 0:
		break;
	default:
		synfig::error("Unknown errorcode %d (\"%s\")",ret,command.c_str());
		if(is_modified())
			throw(ret);
		break;
	}
}
