// 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: 21-Jul-1997
// 
#include<stdio.h> 
#include<iostream.h>
#include<string.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>

char *RandomTkColor()
{
	int i, hex_val;
	static int blue_component;
	static int green_component;
	static int red_component;
	char next;
	char *color = new char[14];
	color[0] = '#';
	int high_taken = 0; 
	if ((Link_randomLong() % 3) == 0) {
		red_component = (Link_randomLong() % 56) + 200;
		high_taken = 1;
	} else
		red_component = Link_randomLong() % 256;
	if (!high_taken && (Link_randomLong() % 2) == 0) {
		green_component = (Link_randomLong() % 56) + 200;
		high_taken = 1;
	} else
		green_component = Link_randomLong() % 256;
	if (!high_taken) {
		blue_component = (Link_randomLong() % 56) + 200;
	} else
		blue_component = Link_randomLong() % 256;
	sprintf(color, "#%2x%2x%2x", red_component, green_component,
				  blue_component);
	for (i=0; i<strlen(color); i++) if (color[i]==' ') color[i]='0';
	return color;
}
		
#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,
		   int show_type, int show_iterations, int show_delay)
{
	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,
		   int show_type, int show_iterations, int show_delay)
{
	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 = 0;
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));

	// ****************************************************************
	// When we switched to implicit template instantiation, we
	// had trouble convincing the linker to read the file
	// src/userobj/userobjSpecializations.cpp, which contains
	// a specialization necessary to compare AnimationData* when
	// that type is used as the element type of a Container. 
	// The following call serves as a request to the linker. JWB 1/2000
	// ****************************************************************
	//extern void lion();
	//lion();
	//AnimationData *p;
	//ElementOps<AnimationData* >::compareItems(p, p);
	// ****************************************************************

	return Animation_Id++;
}
void animationWindows(int k, char *view_name)
{
	char *cmd;
	ostrstream cmd_oss;
	cmd_oss << "(let* ((gv (hash-table-get *link: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);
	ContainerNode *cp = Command_Sets.search(&dummy);
	if (cp == 0) cout << "getAnimation: no entry!" << endl;
	AnimationData *ad = Command_Sets.info(cp);
	if (ad == 0) cout << "getAnimation: empty entry!" << endl;
	return ad;
}
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);
		cmd = to_run->prevUndoCmd();
	}
	if (cmd) {
		STk_eval_C_string(cmd, NIL);
		cmd = 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, int show_type=FLASH, 
			   int show_iterations=4, int show_delay=20)
{
	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 *link:graph-view-table* '" << 
							view_name << "))";
	cmd_oss << "\n     (vtab (slot-ref gv 'vertex-table))";
	cmd_oss << "\n     (vg (hash-table-get vtab stub)))";
	if (show_type == FLASH)
	   cmd_oss << "\n                   (flash vg " << show_iterations 
					      << " " << show_delay << ")";
	else
	   cmd_oss << "\n                   (jiggle vg " <<show_iterations 
					      << " " << show_delay << ")";
	cmd_oss << "\n                   (set! (" << stk_slot_name << " vg)";
	cmd_oss <<                           value << "))";
	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;   // LEAK!!!
	return cmd;
}
static char *generateEdgeCmd(Edge *e, char *view_name, char *stk_slot_name,
			   char *value, int init_flag, int show_type,
			   int show_iterations, int show_delay)
{
	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 *link:graph-view-table* '" << 
							view_name << "))";
	cmd_oss << "\n     (etab (slot-ref gv 'edge-table))";
	cmd_oss << "\n     (eg (hash-table-get etab stub)))";
	if (show_type == FLASH)
	   cmd_oss << "\n                   (flash eg " << show_iterations 
						<< " " << show_delay << ")";
	else
	   cmd_oss << "\n                   (jiggle eg " << show_iterations 
						<< " " << show_delay << ")";
	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;	// LEAK!!!
	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,
		   int show_type, int show_iterations, int show_delay)
{
	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();
		//cout << "Animation.cpp: gen. backup: " << *v << endl;
		//cout << "Animation.cpp: gen. backup: " << attr_name 
                //     << " " << *attribute << endl;
		//cout << "Animation.cpp: gen. backup: " << curval << endl; 
		ucmd=generateVertexCmd(v,view_name, stk_slot_name, curval,
				       init_step, show_type, show_iterations,
				       show_delay);
	}
	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, show_type, 
					    show_iterations, show_delay);
		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,
		   int show_type, int show_iterations, int show_delay)
{
	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, show_type, show_iterations,
				     show_delay);
	}
	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 ostrstream;
		*new_value << *attribute << ends;
		char *curval = new_value->str();
		// (see last comment)
		char *cmd=generateEdgeCmd(e, view_name, stk_slot_name,curval,
					  init_step, show_type, show_iterations,
					  show_delay);
		AnimationData *ad = getAnimation(Current_Animation);	
		ad->appendCmd(cmd, ucmd);
		delete new_value; 
	}
}
/* 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.
template void  animateVertex(Vertex*,char*,char*,char*, int, const int&,
					int, int, int);
template void  animateVertex(Vertex*,char*,char*,char*, int, const double&,
					int, int, int);
template void  animateVertex(Vertex*,char*,char*,char*, int, const String&,
					int, int, int);
template void  animateEdge(Edge*,char*,char*,char*, int, const int&,
					int, int, int);
template void  animateEdge(Edge*,char*,char*,char*, int, const double&,
					int, int, int);
template void  animateEdge(Edge*,char*,char*,char*, int, const String&,
					int, int, int);
