/*
 * Network model with Editor layout
 *
 */

#include <stdlib.h>
#include <math.h>
#include <float.h>

#include "random.h"
#include "view.h"
#include "netview.h"
#include "animation.h"
#include "queue.h"
#include "edge.h"
#include "node.h"
#include "agent.h"
#include "sincos.h"
#include "state.h"
#include "packet.h"
#include "enetmodel.h"

class EditorNetworkModelClass : public TclClass {
public:
	EditorNetworkModelClass() : TclClass("NetworkModel/Editor") {}
	TclObject* create(int argc, const char*const* argv) {
		if (argc < 5) 
			return 0;
		return (new EditorNetModel(argv[4]));
	}
} editornetworkmodel_class;

EditorNetModel::EditorNetModel(const char *editor) : NetModel(editor)
{
	nID = 0;

	bind("Wpxmin_", &pxmin_);
        bind("Wpymin_", &pymin_);
	bind("Wpxmax_", &pxmax_);
        bind("Wpymax_", &pymax_);

}

EditorNetModel::~EditorNetModel()
{
}

void EditorNetModel::BoundingBox(BBox& bb)
{                       

	// by default, 800X1000 internal drawing area
        bb.xmin = pxmin_;
	bb.ymin = pymin_;
        bb.xmax = pxmax_;
        bb.ymax = pymax_;

        for (Animation* a = drawables_; a != 0; a = a->next())
                a->merge(bb);

	pxmin_ = bb.xmin;
	pymin_ = bb.ymin;
	pxmax_ = bb.xmax;
	pymax_ = bb.ymax;
}

int EditorNetModel::addNode(float cx, float cy)
{

       /*
        * <net> node <name> <shape> <color> <addr> [<size>]
        * Create a node using the specified name
        * and the default size and insert it into this
        * NetModel's list of drawables.
        */
	 Node *n;
	 static char name[TRACE_LINE_MAXLEN];

	 if (nID == 0 ) {

	     for (n = nodes_; n != 0; n = n->next_) {

	   	if((n->num())>nID) nID = n->num();	 
	     }

	     if (nID > 0) nID ++;

	 }
	 
	 sprintf(name, "%d", nID);

         double size = 30;

         n = new CircleNode(name,size);     
	 nID ++;

         //n->setaddr(addr);
         //addAddress(n->num(), addr);
         n->place(cx,cy);
         n->next_ = nodes_;
         nodes_ = n;
         n->insert(&drawables_);
         return (TCL_OK);
}

int EditorNetModel::addLink(Node *src, Node *dst)
{

	
	double bw = 1000000.0;
	double delay = 0.002;
	double length = 0;
	double angle = 1.0;

	Edge *e = new Edge(src, dst, 10,
                             bw, delay, length, angle);
        e->init_color("black");
        enterEdge(e);
        e->insert(&drawables_);
        src->add_link(e);
//        tcl.resultf("%g", delay);

	// duplex link by default according to nam

	e = new Edge(dst, src, 10,  
                             bw, delay, length, angle);

	e->init_color("black");
	enterEdge(e);
	e->insert(&drawables_);
        dst->add_link(e);
	
        return (TCL_OK);
}

void EditorNetModel::setNodeProperty(int id, char* pv, int pn)
{
	Node *n = lookupNode(id);
	if (n == NULL) {
            fprintf(stderr, "Nonexisting node %d.\n", id);
	    return;
        }

	switch (pn) {
	    case 0:
	    {
		double s = atof(pv);
		n->size(s);
		break;
	    }
	    case 1:	//color
	    {
		//int color = atoi(pv);
		n->init_color(pv);
		break;
	    }
	    case 2:	//label
	    {
		//int color = atoi(pv);
		n->dlabel(pv);
		break;
	    }
	}


}

void EditorNetModel::setLinkProperty(int sid,int did, char* pv, int pn)
{
	EdgeHashNode *h = lookupEdge(sid, did);
	EdgeHashNode *g = lookupEdge(did, sid);

        if (h == NULL || g == NULL ) {
            fprintf(stderr, "Nonexisting link %d.%d\n", sid, did);
            return;
        }

        switch (pn) {
            case 0: //bandwidth
            {
                double s = atof(pv);
		(h->edge) -> setBW(s);
		(g->edge) -> setBW(s);
                break; 
            }
            case 1:     //color
            {
                //int color = atoi(pv);
                (h->edge)->init_color(pv);
                (g->edge)->init_color(pv);
                break;
            }
            case 2:     //delay
            {
                double s = atof(pv);
		(h->edge) -> setDelay(s);
		(g->edge) -> setDelay(s);
                break;
            }
        }


}

int EditorNetModel::command(int argc, const char *const *argv)
{               
        if (argc == 2) {
		if (strcmp(argv[1], "layout") == 0) {
			layout();
			return (TCL_OK);
	        }
	}
        if (argc == 3) {
                if (strcmp(argv[1], "saveasenam") == 0) {
                        /*
                         * <net> saveasenam filehandler
                         */
                        saveAsEnam(argv[2]);
                        return (TCL_OK);
                }
                if (strcmp(argv[1], "saveasns") == 0) {
                        /*
                         * <net> saveasns filehandler
                         */
                        saveAsNs(argv[2]);
                        return (TCL_OK);
                }
        }       
                   
        return (NetModel::command(argc, argv));
}                  

void EditorNetModel::layout()
{

        Node *n;
        for (n = nodes_; n != 0; n = n->next_)
                for (Edge* e = n->links(); e != 0; e = e->next_)
                        placeEdge(e, n);

}

// XXX losing some editor's info

int EditorNetModel::saveAsNs(const char *filename)
{

  FILE *file;
  Node *n;
  Edge *e;      
  int ret;
  file=fopen(filename, "w");
  if (file==0) 
  {     
    fprintf(stderr, "nam: Couldn't open file: %s\n", filename);
    return -1;
  }

  fprintf(file, "#create ns\n");
  fprintf(file, "set ns [new Simulator]\n");
  fprintf(file, "$ns namtrace-all [open out.nam w]\n");

  // save node info
  fprintf(file, "#node creation\n");

    for (n = nodes_; n != 0; n = n->next_)
  {
    ret = n->saveAsNs(file);
    if (ret!=0) {
      fclose(file);
      return -1;
    }
  }

  // save link info
  fprintf(file, "#link creation\n");
  for (n = nodes_; n != 0; n = n->next_)
    for(e= n->links(); e !=0; e = e->next_)
    {
      ret = e->saveAsNs(file);
      if (ret!=0) {
        fclose(file);
        return -1;
      }
    }

  fprintf(file, "$ns run\n");
  return(fclose(file));

}

int EditorNetModel::saveAsEnam(const char *filename)
{

  FILE *file;   
  Node *n;
  Edge *e;      
  int ret;       
  file=fopen(filename, "w");
  if (file==0)   
  {
    fprintf(stderr, "nam: Couldn't open file: %s\n", filename);
    return -1;  
  }             

  // save version info

  fprintf(file, "V -t * -v 1.0a7 -a 0\n");

  // save page size

  fprintf(file, "W -t * -x %f -y %f -X %f -Y %f\n",pxmin_, 
	pymin_, pxmax_, pymax_);

  for (n = nodes_; n != 0; n = n->next_)
  {   
    ret = n->saveAsEnam(file); 
    if (ret!=0) {
      fclose(file);
      return -1;
    }           
  }
  for (n = nodes_; n != 0; n = n->next_)
    for(e= n->links(); e !=0; e = e->next_)
    {           
      ret = e->save(file);
      if (ret!=0) {
        fclose(file);   
        return -1;
      }
    } 
  return(fclose(file));



}

void EditorNetModel::removeNode(Node *n)
{
                Node *p,*q;
                p = nodes_;     
                for (q = nodes_; q != 0; q = q->next_) {
                   if( n->num() == q->num() ) {
                        p->next_ = q->next_; 
                        break;
                   } 
                   p = q;
                }
                q->detach();  

                // delete edge on the node_

                for(Edge *e= q->links(); e !=0; e = e->next_) 
                    removeLink(e);

		delete q;

}

void EditorNetModel::removeLink(Edge *e)
{
                    int src = e->src();
                    int dst = e->dst();

                    EdgeHashNode *h = lookupEdge(src, dst);
                    EdgeHashNode *g = lookupEdge(dst, src);
                    if (h == 0 || g == 0) {
        
                      // h,g = 0 or h,g !=0
                        return;

                     } 

                    Edge* e1 = h->edge;
                    Edge* e2 = g->edge;
 
                    removeEdge(e1);
                    removeEdge(e2);
 
                    e1->detach();
                    e2->detach();
 
                    Node* srcnode = e1->start();
                    Node* dstnode = e2->start();

                    // it is a duplex by default
                    srcnode->delete_link(e1);
                    dstnode->delete_link(e2); 

                    delete e1;
                    delete e2;

}

