/*
** 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- March 1998
 */

/*
 * LogParse.cpp : parse the cvs log output
 */

#include "stdafx.h"

#include <errno.h>
#include <stdio.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef WIN32
#	include "wincvs.h"
#	include "GraphDoc.h"
#endif /* WIN32 */

#ifdef TARGET_OS_MAC
#	include <unistd.h>
#	include "MacCvsApp.h"
#	include "CTreeView.h"
#endif /* TARGET_OS_MAC */

#if qUnix
#	include "UCvsDialogs.h"
#	include "UCvsGraph.h"
#endif

#include "LogParse.h"
#include "CvsArgs.h"
#include "CvsLog.h"
#include "AppGlue.h"
#include "AppConsole.h"
#include "CPStr.h"
#include "getline.h"

#ifdef WIN32
#	ifdef _DEBUG
#	define new DEBUG_NEW
#	undef THIS_FILE
	static char THIS_FILE[] = __FILE__;
#	endif
#endif /* WIN32 */

static void print(CColorConsole & out, const CRevFile & rev)
{
	out << kBlue << "----------------------------" << kNormal << kNL;
	out << "Revision : " << kBold << kRed << rev.RevNum().c_str() << kNormal << kNL;
	if(!rev.Locker().empty())
		out << "Locked by : '" << kBold << rev.Locker() << kNormal << '\'' << kNL;
	out << "Date : " << kBold <<
		(rev.RevTime().tm_year + 1900) << '/' <<
		rev.RevTime().tm_mon << '/' <<
		rev.RevTime().tm_mday << ' ' <<
		rev.RevTime().tm_hour << ':' <<
		rev.RevTime().tm_min << ':' <<
		rev.RevTime().tm_sec << kNormal << kNL;
	out << "Author : '" << kBold << rev.Author() << kNormal << '\'' << kNL;
	out << "State : '" << kBold << rev.State() << kNormal << '\'' << kNL;
	out << "Lines : +" << kBold << rev.ChgPos() << ' ' << rev.ChgNeg() << kNormal << kNL;

	if(!rev.BranchesList().empty())
	{
		out << "Branches :" << kNL;
		std::vector<CRevNumber>::const_iterator s;
		for(s = rev.BranchesList().begin(); s != rev.BranchesList().end(); ++s)
		{
			const CRevNumber & branch = *s;
			out << '\t' << kBold << branch.c_str() << kNormal << kNL;
		}
	}

	out << "Description :" << kNL << kBrown << rev.DescLog() << kNormal << kNL;
}

static void print(CColorConsole & out, const CRcsFile & rcs, bool recurse = true)
{
	out << "Rcs file : '" << kRed << rcs.RcsFile() << kNormal << '\'' << kNL;
	out << "Working file : '" << kBold << rcs.WorkingFile() << kNormal << '\'' << kNL;
	out << "Head revision : " << kBold << kRed << rcs.HeadRev().c_str() << kNormal << kNL;
	out << "Branch revision : " << kBold << rcs.BranchRev().c_str() << kNormal << kNL;

	out << "Locks :" << kBold << (rcs.LockStrict() ? " strict" : "") << kNormal << kNL;
	std::vector<CRevNumber>::const_iterator s;
	for(s = rcs.LocksList().begin(); s != rcs.LocksList().end(); ++s)
	{
		const CRevNumber & lock = *s;
		out << '\t' << kBold << lock.c_str() << " : '" << lock.Tag() << '\'' << kNormal << kNL;
	}

	out << "Access :" << kNL;
	std::vector<CLogStr>::const_iterator a;
	for(a = rcs.AccessList().begin(); a != rcs.AccessList().end(); ++a)
	{
		out << "\t'" << kBold << *a << kNormal << '\'' << kNL;
	}

	if(!rcs.SymbolicList().empty())
	{
		out << "Symbolic names :" << kNL;
		std::vector<CRevNumber>::const_iterator n;
		for(n = rcs.SymbolicList().begin(); n != rcs.SymbolicList().end(); ++n)
		{
			const CRevNumber & symb = *n;
			out << '\t' << kBold << symb.c_str() << " : '" << symb.Tag() << '\'' << kNormal << kNL;
		}
	}

	out << "Keyword substitution : '" << kBold << rcs.KeywordSubst() << kNormal << '\'' << kNL;
	out << "Total revisions : " << kBold << rcs.TotRevisions() << kNormal << kNL;
	out << "Selected revisions : " << kBold << rcs.SelRevisions() << kNormal << kNL;
	out << "Description :" << kNL << kBrown << rcs.DescLog() << kNormal << kNL;

	std::vector<CRevFile>::const_iterator i;
	if(recurse) for(i = rcs.AllRevs().begin(); i != rcs.AllRevs().end(); ++i)
	{
		print(out, *i);
	}

	out << kGreen << "===============================================" << kNormal << kNL;
}

static void print(CColorConsole & out, const CLogNode * node, CStr & offset)
{
	out << offset;
	switch(node->GetType())
	{
		case kNodeHeader :
		{
			CLogNodeHeader *header = (CLogNodeHeader *)node;
			out << offset << "Rcs file : '" << kRed << (**header).RcsFile() << kNormal << '\'' << kNL;
			break;
		}
		case kNodeBranch :
			break;
		case kNodeRev :
		{
			CLogNodeRev *rev = (CLogNodeRev *)node;
			out << offset << kBold << (**rev).RevNum().c_str() << kNormal << kNL;
			break;
		}
		case kNodeTag :
			break;
	}

	CStr newOffset;
	newOffset = offset;
	newOffset << "    ";
	std::vector<CLogNode *>::const_iterator i;
	for(i = node->Childs().begin(); i != node->Childs().end(); ++i)
	{
		print(out, *i, newOffset);
		newOffset << ' ';
	}

	if(node->Next() != 0L)
	{
		print(out, node->Next(), offset);
	}
}

void CvsLogOutput(CColorConsole & out, CLogNode *node)
{
	switch(node->GetType())
	{
		case kNodeHeader :
		{
			CRcsFile & rcs = **(CLogNodeHeader *)node;
			print(out, rcs, false);
			break;
		}
		case kNodeBranch :
			break;
		case kNodeRev :
		{
			CRevFile & rev = **(CLogNodeRev *)node;
			print(out, rev);
			break;
		}
		case kNodeTag :
			break;
	}
}

void CvsLogParse(const char *dir, const CvsArgs & args, bool outGraph, void *defWnd)
{
	// make a stream of the cvs log output
	CStr tmpFile;
	FILE *output;
	output = launchCVS(dir, args, tmpFile);
	if(output == 0L)
		return;

	// parse
	std::vector<CRcsFile> & allRcs = CvsLogParse(output);

	// maybe something went wrong or may be the user
	// asked to print only the RCS names, so print the
	// output directly whithout parsing../
	if(allRcs.empty())
	{
		char *line = 0L;
		size_t line_chars_allocated = 0;
		int line_length;
		fseek(output, SEEK_SET, 0);
		while ((line_length = getline (&line, &line_chars_allocated, output)) > 0)
		{
			cvs_outstr(line, line_length);
			cvs_outstr("\n", 1);
		}

		if (line_length < 0 && !feof (output))
			cvs_err("Cannot read output file (error %d)\n", errno);

		if(line != 0L)
			free (line);
	}

	// close the temp file
	fclose(output);
	if(unlink(tmpFile) != 0)
	{
		cvs_err("Impossible to delete the file '%s' (error %d)",
			(const char *)tmpFile, errno);
	}

	if(allRcs.empty())
		return;

	// output the result
	CColorConsole out;
	std::vector<CRcsFile>::iterator i;
	for(i = allRcs.begin(); i != allRcs.end(); ++i)
	{
		CRcsFile & rcs = *i;
		if(!outGraph)
			print(out, rcs);
		else
		{
			CLogNode *node = CvsLogGraph(rcs);
			if(node == 0L)
			{
				cvs_err("Impossible to create a graph for %s\n",
					(const char *)rcs.WorkingFile());
			}

#ifdef WIN32
			CWincvsApp* app = (CWincvsApp *)AfxGetApp();
			CGraphDoc *doc;
			if(defWnd != 0L)
				doc = (CGraphDoc *)defWnd;
			else
				doc = (CGraphDoc*)app->gLogGraph->OpenDocumentFile(NULL);
			doc->SetNode(node, dir);
#elif qMacCvsPP
			if(defWnd != 0L)
				((CTreeView *)defWnd)->SetNode(node, dir);
			else
				CMacCvsApp::gApp->NewLogTree(node, dir);
#elif qUnix
			UCvsGraph * graph = new UCvsGraph(node, dir);
			void *win = UCreate_GraphWindow();
			UEventSendMessage(graph->GetWidID(), EV_INIT_WIDGET, kUMainWidget, win);
			UEventSendMessage(graph->GetWidID(), EV_SHOW_WIDGET, UMAKEINT(kUMainWidget, 1), 0L);
#else
			CStr offset;
			print(out, node, offset);
			delete node;
#endif
		}
	}

	// reset the parser
	CvsLogReset();
}
