// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Jonathan Berry

// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.

// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.

// Last File Update: 31-Jul-1996
// 

#include<iostream.h>
#include<strstream.h>
#include<LINK/algorithm/Algorithms.h>
#include<LINK/graph/Vertex.h>
#include<LINK/graph/Edge.h>
#include<LINK/graph/Attribute.h>
#include<LINK/basic/DList.h>

#ifndef STK_GUI   // won't link with STk - animateVertex just updates attr

template <class Item> 
void animateVertex(Vertex* v, char *view_name, char *attr_name, 
		   char *stk_slot_name, int init_step, const Item& value)
{
	setAttribute((GraphObject*)v, attr_name, value);
}

template <class Item> 
void animateEdge(Edge* e, char *view_name, char *attr_name, 
		   char *stk_slot_name, int init_step, const Item& value)
{
	setAttribute((GraphObject*)e, attr_name, value);
}

#else

#include<stk.h>
#include<LINK/stkWrapper/LINK_STk.h>
#include<LINK/algorithm/AnimationData.h>

static List<AnimationData*> Command_Sets;
static int Animation_Id;
static int Current_Animation;

char * getViewName(SCM gv)
{
	if (gv == UNBOUND)
		return (char *) 0;
	STk_eval_C_string("(require \"graph-view\")", NIL);
	SCM name = STk_slot_ref(gv, Intern("name"));
	if (!name)
		error("getViewName: <graph-view> object expected");
	char *str = SYMBOL(name);
	return str;
}

int initAnimation()
{
	Current_Animation = Animation_Id;
	Command_Sets.insert(new AnimationData(Current_Animation));
	return Animation_Id++;
}

void animationWindows(int k, char *view_name)
{
	char *cmd;
	ostrstream cmd_oss;

	cmd_oss << "(let* ((gv (hash-table-get *graph-view-table* '" << 
							view_name << "))";
	cmd_oss << "\n     (t (slot-ref gv 'graph-toplevel))";

	cmd_oss << "\n     (f (make <Frame> :parent t))";
	cmd_oss << "\n     (l1(make <Label> :parent f :text ";
	cmd_oss << "           \"Animation Control\"";
        cmd_oss << "\n         :relief 'raised :expand #t :fill \"x\"))";
	cmd_oss << "\n    (b0(make <Button> :parent f :text \"Reset Graphics\"";
	cmd_oss << "\n     	:command (lambda () (reset-graphics gv))))";
	cmd_oss << "\n     (b1 (make <Button> :parent f :text \"Run\"";
	cmd_oss << "\n     	:command (lambda () (animation-run "<<k<<"))))";
	cmd_oss << "\n     (b2 (make <Button> :parent f :text \"Step\"";
	cmd_oss << "\n     	:command (lambda()(animation-step "<<k<<"))))";
	cmd_oss << "\n     (b3 (make <Button> :parent f :text \"Backup\"";
	cmd_oss << "\n         :command (lambda()(animation-backup "<<k<<"))))";
	cmd_oss << "\n     (b4 (make <Button> :parent f :text \"Break\"";
	cmd_oss << "\n     	:command (lambda()(animation-break "<<k<<"))))";
	cmd_oss << "\n     (b5 (make <Button> :parent f :text \"Clear Breaks\"";
	cmd_oss << "\n     	:command (lambda()(animation-clear-breaks "
								<<k<<"))))";
	cmd_oss << "\n     (b6 (make <Button> :parent f :text \"Continue\"";
	cmd_oss << "\n       :command (lambda()(animation-continue "<<k<<"))))";
	cmd_oss << "\n     (b7 (make <Button> :parent f :text \"Done\"";
	cmd_oss << "\n          :command (lambda ()(destroy f)";
	cmd_oss << "\n                           (animation-done "<<k<<")))))";
	cmd_oss << "\n          (pack l1 :side 'top :expand #f :fill \"x\")";
	cmd_oss << "\n		(pack b0 b1 b2 b3 b4 b5 b6 b7  :side 'left" ; 
	cmd_oss << "\n              :expand #f :fill \"x\" :padx 0 :pady 0)";

	cmd_oss << "\n          (pack f :fill \"x\"))" << ends;

	cmd = cmd_oss.str();

	STk_eval_C_string(cmd, NIL);

	delete cmd;
}

static AnimationData *getAnimation(int id)
{
	AnimationData dummy(id);
	return Command_Sets.info(Command_Sets.search(&dummy));
}

void animationDone(int id)
{
	AnimationData *finished = getAnimation(id);
	if (finished) {
		Command_Sets.remove(finished);
		delete finished;
	}
}

void animationRun(int id)
{
	AnimationData *to_run = getAnimation(id);

	to_run->resetCmds();
	//to_run->debug();
	String cmd=to_run->currentCmd();
	while (cmd) {
		if (to_run->currentMarkedQ())
			break;
		STk_eval_C_string(cmd, NIL);
		cmd = to_run->nextCmd();
	}
}

void animationContinue(int id)
{
	AnimationData *to_run = getAnimation(id);

	String cmd = to_run->currentCmd();
	while (cmd) {
		STk_eval_C_string(cmd, NIL);
		if (to_run->direction() == FORWARD)
			cmd = to_run->nextCmd();
		else
			cmd = to_run->prevUndoCmd();
		if (to_run->currentMarkedQ())
			break;
	}
}

void animationStep(int id)
{
	AnimationData *to_run = getAnimation(id);

	to_run->setDirection(FORWARD);
	String cmd=to_run->currentCmd();
	while (cmd && to_run->currentInitQ()) {
		STk_eval_C_string(cmd, NIL);
		cmd = to_run->nextCmd();
	}
	if (cmd) {
		STk_eval_C_string(cmd, NIL);
		cmd = to_run->nextCmd();
	}
}

void animationBackup(int id)
{
	AnimationData *to_run = getAnimation(id);

	to_run->setDirection(BACKWARD);
	String cmd=to_run->currentCmd();
	while (cmd && to_run->currentInitQ()) {
		STk_eval_C_string(cmd, NIL);
		to_run->prevUndoCmd();
	}
	if (cmd) {
		STk_eval_C_string(cmd, NIL);
		to_run->prevUndoCmd();
	}
}

void animationClearBreaks(int id)
{
	AnimationData *to_run = getAnimation(id);
	to_run->clearMarks();
}

void animationBreak(int id)
{
	AnimationData *to_run = getAnimation(id);

	String cmd=to_run->currentCmd();
	if (cmd) 
		to_run->markCurrent(); 
}

static char *generateVertexCmd(Vertex *v, char *view_name, char *stk_slot_name,
			   char *value, int init_flag)
{
	char *vtx_name;
	ostrstream vtx_oss;
	char *cmd;
	ostrstream cmd_oss;

	vtx_oss << v->name() << ends;
	vtx_name = vtx_oss.str();
	
	cmd_oss << "(let* ((stub (make <label-object>"
		   "\n                      :objlabel \""<<vtx_name<<"\")) ";
	cmd_oss << "\n     (gv (hash-table-get *graph-view-table* '" << 
							view_name << "))";
	cmd_oss << "\n     (vtab (slot-ref gv 'vertex-table))";
	cmd_oss << "\n     (vg (hash-table-get vtab stub)))";
	cmd_oss << "\n                   (flash vg)";
	cmd_oss << "\n                   (set! (" << stk_slot_name << " vg)";
	cmd_oss <<                           value << ")";
	cmd_oss << "\n                       (update))"; 
	if (init_flag)
		cmd_oss << "\n                       (update))#t  " << ends; 
	else
		cmd_oss << "\n                       (update))#f  " << ends; 
	// *****************************************************^^********
	// *NOTE: these two extra chars. are used for marking commands ***
	// ***************************************************************
	cmd = cmd_oss.str();

	delete vtx_name;
	return cmd;
}

static char *generateEdgeCmd(Edge *e, char *view_name, char *stk_slot_name,
			   char *value, int init_flag)
{
	char *edg_name;
	ostrstream edg_oss;
	char *cmd;
	ostrstream cmd_oss;

	edg_oss << e->name() << ends;
	edg_name = edg_oss.str();
	
	cmd_oss << "(let* ((stub (make <label-object>"
		   "\n                       :objlabel \""<<edg_name<<"\")) ";
	cmd_oss << "\n     (gv (hash-table-get *graph-view-table* '" << 
							view_name << "))";
	cmd_oss << "\n     (etab (slot-ref gv 'edge-table))";
	cmd_oss << "\n     (eg (hash-table-get etab stub)))";
	cmd_oss << "\n                   (flash eg)";
	cmd_oss << "\n                   (set! (" << stk_slot_name << " eg)";
	cmd_oss <<                           value << ")";
	if (init_flag)
		cmd_oss << "\n                       (update))#t  " << ends; 
	else
		cmd_oss << "\n                       (update))#f  " << ends; 
	// *****************************************************^^********
	// *NOTE: these two extra spaces are used for marking commands ***
	// ***************************************************************
	cmd = cmd_oss.str();

	delete edg_name;
	return cmd;
}

template <class Item> 
void animateVertex(Vertex* v, char *view_name, char *attr_name, 
		   char *stk_slot_name,
		   int init_step, // if just an initialization step, dont
				  // make user step over it (e.g. changing
				  // labels to show partitions at begining
				  // of partitioning alg.
		   const Item& value)
{
	char *ucmd;
	if (view_name) {
		Attribute<Item>* attribute = 
				(Attribute<Item>*) v->findAttr(attr_name);
    		if (!attribute) {
        		error(LINK_GET_ATTR_FAIL);
    		}
		ostrstream current_value;
		current_value << *attribute << ends;
		char *curval = current_value.str();
		ucmd=generateVertexCmd(v,view_name, stk_slot_name, curval,
				       init_step);
	}
	setAttribute((GraphObject*)v, attr_name, value);
	if (view_name) {
		Attribute<Item>* attribute = 
			(Attribute<Item>*) v->findAttr(attr_name);
    		if (!attribute) {
            			error(LINK_GET_ATTR_FAIL);
        	}
		ostrstream new_value;
		new_value << *attribute << ends;
		char *curval = new_value.str();
		char *cmd=generateVertexCmd(v,view_name,stk_slot_name,curval,
					    init_step);
		AnimationData *ad = getAnimation(Current_Animation);	
		ad->appendCmd(cmd, ucmd);
	}
}

template <class Item> 
void animateEdge(Edge* e, char *view_name, char *attr_name, 
		   char *stk_slot_name,
		   int init_step, // if just an initialization step, dont
				  // make user step over it (e.g. changing
				  // edge labels to show weights at begining
				  // of spanning tree alg.
		   const Item& value)
{
	char *ucmd;
	if (view_name) {
		Attribute<Item>* attribute = 
			(Attribute<Item>*) e->findAttr(attr_name);
    		if (!attribute) {
            			error(LINK_GET_ATTR_FAIL);
        	}
		ostrstream current_value;
		current_value << *attribute << ends;
		char *curval = current_value.str();
		// the following command ignored init_step for some reason..
		ucmd=generateEdgeCmd(e, view_name, stk_slot_name, curval, init_step);
	}
	setAttribute((GraphObject*)e, attr_name, value);

	if (view_name) {
		Attribute<Item>* attribute = 
			(Attribute<Item>*) e->findAttr(attr_name);
    		if (!attribute) {
            			error(LINK_GET_ATTR_FAIL);
        	}
		ostrstream new_value;
		new_value << *attribute << ends;
		char *curval = new_value.str();
		// (see last comment)
		char *cmd=generateEdgeCmd(e, view_name, stk_slot_name,curval,init_step);

		AnimationData *ad = getAnimation(Current_Animation);	
		ad->appendCmd(cmd, ucmd);
	}
}

/* untested
void moveStarEdge(Edge *e, double world_x, double world_y, char *view_name)
{
	char *edg_name;
	ostrstream edg_oss;
	char *cmd;
	ostrstream cmd_oss;

	edg_oss << e->name() << ends;
	edg_name = edg_oss.str();
	
	cmd_oss << "(let* ((stub (make <label-object>"
		   "\n                       :objlabel \""<<edg_name<<"\"))) ";
	cmd_oss << "\n                  (layout-edge stub " << world_x;
	cmd_oss <<                      " " << world_y << " " << world_y;
	cmd_oss <<                      " " << view_name << "))";
	cmd_oss << ends;
	cmd = cmd_oss.str();

	STk_eval_C_string(cmd, NIL);
	delete cmd;
	delete edg_name;
}
*/

void starDrawGraph(char *view_name)
{
        char *cmd;
        ostrstream cmd_oss;

        cmd_oss << "(star-draw " << view_name << ")";
        cmd = cmd_oss.str();

        STk_eval_C_string(cmd, NIL);
        delete cmd;
}


#endif


//  these should be generated by instantiate eventually.
#ifdef __GNUC__
template void  animateVertex(Vertex*,char*,char*,char*, int, const int&);
template void  animateVertex(Vertex*,char*,char*,char*, int, const double&);
template void  animateVertex(Vertex*,char*,char*,char*, int, const String&);
template void  animateEdge(Edge*,char*,char*,char*, int, const int&);
template void  animateEdge(Edge*,char*,char*,char*, int, const double&);
template void  animateEdge(Edge*,char*,char*,char*, int, const String&);
#endif

