// FileHead.cpp: implementation of the FileHead class.
//
//////////////////////////////////////////////////////////////////////

#include "FileHead.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/* When a FileHead is constructed, it immediately populates itself with
 * all the necessary FileRev objects to make it a complete representation
 * of this file.  Integration records are not evaluated at this time. */
FileHead::FileHead(StrBuf path, FileLogCache* newparent)
{
	file = path; //This is the path to the file.
	next = NULL;
	head = NULL;
	tail = NULL;

	parent = newparent; //The FileLogCache that created this FileHead.
	
	ClientLogUser ui;
	ui.working = this; //Tell the ClientLogUser to add revisions to this FileHead.
	ClientApi client;
	if (P4PORT.Length()) client.SetPort(P4PORT.Text());
	Error e;
	StrBuf msg;
	
	/* Initialize the client/server connection.  Any error at this point is the
	 * result of a bad connection between client and server. */
	client.Init(&e);
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		ALERT(at_console, msg.Text());
		return;
	}

	char* args[1]; //An array that will hold the arguments to the p4 command.
	args[0] = path.Text(); //Path to the file - this will be the arg to p4 filelog.
	
	client.SetArgv(1, args); //Set the argument list to be the file path.
	client.Run( "filelog", &ui ); //Run "p4 filelog <file>".

	/* At this point, the ClientLogUser takes care of creating FileRevs and
	 * arranging them.  By the time the filelog command finishes executing, it's
	 * all done. */
	
	/* Close the connection and report errors to the console. */
	client.Final (&e);
	if (e.GetSeverity()) {
		e.Fmt(&msg);
		ALERT(at_console, msg.Text());
	}
}

/* The destructor - deletes all of its FileRevs as well as its neighboring FileHeads. */
FileHead::~FileHead()
{
	if (head != NULL) {
		delete head; //deleting the head FileRev deletes them all.
	}
	if (next != NULL) {
		delete next; //pass the deletion message on to the next FileHead.
	}
}

/* addRev is called from within ClientLogUser each time a new revision is given in
 * the filelog.  It adds a revision to the tail of the chain of FileRevs. */
void FileHead::addRev(StrBuf newrev, StrBuf newchange, short type, bool istext)
{
	if (head != NULL) //if we already have one or more FileRevs here
	{
		tail->next = new FileRev(newrev, this, newchange, type, istext); //tack this on the end
		tail->next->prev = tail; //set its prev pointer

		/* add appropriate "edit" FileRevArrow */
		tail->next->AddFromArrow(tail, edit);

		tail = tail->next; //update the tail pointer to reflect the addition
	}
	else //this is the first revision we're adding
	{
		head = new FileRev(newrev, this, newchange, type, istext); //make it the head
		tail = head; //and make it the tail also
	}
}

/* See the header file - scanInto is called when another FileHead has determined that
 * this one is its descendant.  The first argument indicates at what point this FileHead
 * received content from the donor FileHead, and the second argument indicates what specific
 * revision that integration was from. 
 * Note that scanInto operates on all the revisions from startrev-head.  Note also that
 * a FileHead's revisions start with the head and work backwards, so we'll be starting with
 * the head revision and working down until we hit startrev - when we hit startrev, we point it
 * at the "caller" FileRev and return. */
void FileHead::scanInto(StrBuf startrev, FileRev* caller, ArrowType atype)
{
	bool credit = false; //Don't start looking yet.
	FileRev* foo = tail; //foo is the FileRev we're looking at.
	FileTextArrow* text;

	/* First, precache all of the descendant branches. */
	
	while (foo != NULL) //Unless we hit the end of the line...
	{
		if (foo->rev == startrev) { //if this is the startrev
			credit = true; //begin precaching
		}
		if (credit)
		{
			if (foo->intocheck) 
			{				//Have we already checked this rev?  If so, don't do it again.
				foo = foo->prev; //move up to the next FileRev.
				continue; //Next loop.
			}
			text = foo->intotexts;
			//If no integ descendants, this will be null.
			while (text)
			{
				text->fhead = parent->Get(text->file, false, this); //get the FileHead
				text = text->next; //next textarrow
			}
		}
		foo = foo->prev; //Move on to the next FileRev.
	}

	/* Now we scanInto all those precached FileHeads. */
	credit = false;
	foo = tail;
	while (foo != NULL) //Unless we hit the end of the line...
	{
		if (foo->rev == startrev) { //if this is the startrev
			credit = true; //begin doing scanIntos
			foo->AddFromArrow(caller, atype); //Set a from pointer to the caller FileRev.
		}
		if (credit)
		{
			if (foo->intocheck) 
			{				//Have we already checked this rev?  If so, don't do it again.
				foo = foo->prev; //move up to the next FileRev.
				continue; //Next loop.
			}
			/* If we reach this point, this rev hasn't been checked yet. */
			text = foo->intotexts;
			/* If foo doesn't have any integ children, these will be empty StrBufs. */
			while (text)
			{
				/* Here we find the FileHead representing the file from the "into"
				 * integ record.  We then scanInto it, giving the file that we're
				 * looking at right now as the "caller" arg, and the revision from
				 * the integ record as the startrev. */
				text->fhead->scanInto(text->rev, foo, text->type);
				text = text->next;
			}
			foo->intocheck = true; //Record the fact that this FileRev has been checked.
			parent->changes->AddChange(foo->change); //add this change to the sorted list
		}
		foo = foo->prev; //Move on to the next FileRev.
	}
}

/* scanFrom returns a FileRev* , which is then used to fill the "from" pointer
 * of its caller.  The endrev argument indicates the end of the revision range -
 * in scanFrom, we start at that rev and then work our way down to the earliest
 * revision. */
FileRev* FileHead::scanFrom(StrBuf endrev)
{
	FileTextArrow* text;
	FileRev* foo = tail; //foo is the rev we're currently looking at.
	while (foo != NULL) //for each FileRev...
	{
		if (foo->fromcheck) //If this rev has already been checked
		{
			if (foo->rev == endrev) break; //Is it the last one?
			else foo = foo->prev; //No?  Skip over it and continue.
			continue;
		}
		text = foo->fromtexts;
		while (text)
		{
			text->fhead = parent->Get(text->file, true, this); //precache the associated FileHead
			text = text->next;
		}
		if (foo->fromtexts == NULL)
		{
			foo->fromcheck = true; //no need to try to scan it later
		}
		parent->changes->AddChange(foo->change); //add this change to the list
		if (foo->rev == endrev) break; //we're done precaching
		else foo = foo->prev; //otherwise, next FileRev
	}

	/* Now the actual scans. */

	foo = tail;
	while (foo != NULL) //for each FileRev...
	{
		if (foo->fromcheck) //If this rev has already been checked
		{
			if (foo->rev == endrev) return foo; //Is it the one we want?
			else foo = foo->prev; //No?  Skip over it and continue.
			continue;
		}
		text = foo->fromtexts;
		while (text)
		{
			foo->AddFromArrow(text->fhead->scanFrom(text->rev), text->type);
			text = text->next;
		}
		foo->fromcheck = true; //mark this FileRev as having been scanned
		if (foo->rev == endrev) return foo; //all done!  Return it.
		else foo = foo->prev; //otherwise, next FileRev
	}
	return foo; //this line should never be executed.
}

void FileHead::scanMain() 
{
	FileTextArrow* text;
	FileRev* foo = tail; //foo is the FileRev we're looking at.

	/* In order to help make a nicely-arranged branch graph, we're going to
	 * precache all of this file's immediate relatives before tracing them
	 * to find the "extended family". */
	while (foo != NULL) //Unless we hit the end of the line...
	{
		if (!(foo->fromcheck)) 
		{
			text = foo->fromtexts;
			while (text) 
			{
				text->fhead = parent->Get(text->file, true, this); //cache the associated FileHead
				text = text->next;
			}
			parent->changes->AddChange(foo->change); //add this change to the list
		}
		if (!(foo->intocheck)) 
		{
			text = foo->intotexts;
			/* If foo doesn't have any integ children, this is null. */
			while (text) //For all files in the "intofiles" list.
			{
				text->fhead = parent->Get(text->file, false, this); //cache the FileHead
				text = text->next; //next file
			}
			parent->changes->AddChange(foo->change); //add this change to the sorted list
		}
		foo = foo->prev;
	}

	/* Now we finish it up by actually going through and scanning the immediate
	 * relatives. */
	foo = tail;
	while (foo != NULL)
	{
		if (!(foo->fromcheck))
		{
			foo->fromcheck = true;
			text = foo->fromtexts;
			while (text)
			{
				foo->AddFromArrow(text->fhead->scanFrom(text->rev), text->type);
				text = text->next;
			}
		}
		if (!(foo->intocheck))
		{
			foo->intocheck = true;
			text = foo->intotexts;
			while (text)
			{
				/* Here we find the FileHead representing the file from the "into"
				 * integ record.  We then scanInto it, giving the filerev that we're
				 * looking at right now as the "caller" arg, and the revision from
				 * the integ record as the startrev. */
				text->fhead->scanInto(text->rev, foo, text->type);
				text = text->next;
			}
		}
		foo = foo->prev;
	}
}