/*******************************************************************************
+
+  LEDA 3.5
+
+  init.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
#include <LEDA/graphwin.h>
#include <LEDA/bitmaps/leda_icon.xpm>
#include "../../src/graphwin/local.h"

#include <LEDA/graph_alg.h>


//----------------------------------------------------------------------------


/*
void gw_error_handler(int i, const char* s)
{ 
  panel P("GraphWin Error Handler");

  if (i == 0)
    { P.text_item("\\bf Warning");
      P.text_item(string("%-64s",s));
      P.text_item("");
      P.button("continue");
      P.open();
     }
  else
    { P.text_item("\\bf Error");
      P.text_item(string("%-64s",s));
      P.text_item("");
      P.button("exit",0);
      P.button("core",1);
      if (P.open() == 0)
         quit_action(0);
      else 
         abort();
     }
 }
*/


//----------------------------------------------------------------------------


void GraphWin::init(graph& G, window& W) {

  gr_p  = &G;
  win_p = &W;

  win_p->set_inf(this);

  if (create_flag & cr_win) 
     menu_p = win_p;
  else
   { menu_p = new panel;
     menu_p->set_inf(this);
    }

  init_once();
  init_graph();

  leda_xpm = 0;
}


//----------------------------------------------------------------------------

void GraphWin::init_call_menu() {
  if (sub_menu_counter == 0)
  { call_entry_counter=0;
    sub_menus[0] = menu_p;
    sub_menu_counter = 1;
    init_menu();
   }
}

//----------------------------------------------------------------------------

void GraphWin::init_once() {

  sub_menu_counter = 0;

  pre_new_node_handler=NULL;
  pre_new_edge_handler=NULL;
  pre_move_node_handler=NULL;
  pre_del_node_handler=NULL;
  pre_del_edge_handler=NULL;
  pre_split_edge_handler=NULL;
  pre_move_component_handler=NULL;
  pre_init_graph_handler=NULL;
  pre_start_move_node_handler=NULL;
  pre_end_move_node_handler=NULL;

  post_new_node_handler=NULL;
  post_new_edge_handler=NULL;
  post_move_node_handler=NULL;
  post_del_node_handler=NULL;
  post_del_edge_handler=NULL;
  post_split_edge_handler=NULL;
  post_move_component_handler=NULL;
  post_init_graph_handler=NULL;
  post_start_move_node_handler=NULL;
  post_end_move_node_handler=NULL;

//  n_matrix=NULL;

  action=NULL;

  win_init_done=false;

  done_button=exit_button=-1;

  init_static();

  init_default();

  found_node = nil;
  found_edge = nil;
  current_node = nil;

}

//----------------------------------------------------------------------------

void GraphWin::init_default() {

  n_model.init=false;
  e_model.init=false;

  n_model.pos=DefNodePos;
  n_model.clr=DefNodeColor;
  n_model.shape=DefNodeShape;
  n_model.border_clr=DefNodeBorderColor;

  int w_width;

  if (win_p->is_closed()) {
    int win_h = (int)(0.9*window::screen_height());
    int win_w = (int)(0.95*win_h);
    if (win_w > DefWinSizeX) win_w = DefWinSizeX;
    if (win_h > DefWinSizeY) win_h = DefWinSizeY;
    w_width = win_w;
    x_min=DefXMin;
    x_max=DefXMax;
    y_min=DefYMin;
    y_max=win_p->height()*(x_max-x_min)/win_p->width()+y_min;
  }
  else {
    x_min=win_p->xmin();
    x_max=win_p->xmax();
    y_min=win_p->ymin();
    y_max=win_p->ymax();
    w_width = win_p->width();
  }

  zoom_x0 = x_min;
  zoom_y0 = y_min;
  zoom_x1 = x_max;
  zoom_y1 = y_max;
  
  n_model.r1=0.5*DefNodeWidth*(x_max-x_min)/w_width;
  n_model.r2=0.5*DefNodeHeight*(x_max-x_min)/w_width;

  n_model.label=DefNodeLabel;
  n_model.label_t=DefNodeLabelType;
  n_model.label_clr=DefNodeLabelClr;
  n_model.label_pos=DefNodeLabelPos;
  n_model.selected=DefNodeMarked;

  e_model.clr=DefEdgeColor;
  e_model.style=DefEdgeStyle;
  e_model.width=DefEdgeWidth;
  e_model.label=DefEdgeLabel;
  e_model.label_t=DefEdgeLabelType;
  e_model.label_clr=DefEdgeLabelClr;
  e_model.label_pos=DefEdgeLabelPos;
  e_model.selected=DefEdgeMarked;

  edge_distance=DefEdgeDist*(x_max-x_min)/win_p->width();

  node_move=DefAnimationMode;
  zoom_factor=DefZoomFactor;
  animation=DefAnimationSteps;
  grid=DefGridDist;
  constant_width=DefConstWidth;
  directed=DefDirected;
  bg_color=DefBgColor;

  show_status=DefShowStatus;

  ortho_mode = DefOrthoMode;

  gw_filename=DefGWFileName;
  ps_filename=DefPSFileName;

  flush=false;

  max_move_items=DefMaxMoveItems;

  units_per_pixel=1.0;
  
  buttons_per_line = (win_p == menu_p ? DefButtonsPerLine : DefExtraButtonsPerLine);
 
  gen_nodes=DefGenNodes;
  gen_edges=DefGenEdges;

  done_result=DefDoneResult;

  auto_create_target = DefAutoCreateTarget;

  menu_mask=M_COMPLETE;

  status_show_mask = DefShowMask;

  reset_actions();
}

//----------------------------------------------------------------------------

void GraphWin::init_matrix() {

  adj_matrix_first.init(*gr_p,nil);
  adj_matrix_succ.init(*gr_p,nil);

  edge e;    
  forall_edges(e,*gr_p) 
  { node v = source(e);
    node w = target(e);
    adj_matrix_succ[e] = adj_matrix_first(v,w);
    adj_matrix_first(v,w) = e;
   }
}


//----------------------------------------------------------------------------

static random_source ran;

void GraphWin::init_node(node v) {
  node_info &v_inf=n_info[v]; 

  v_inf.id = index(v);

  bool old_flush=set_flush(false);
  set_info(v,n_model,N_COMPLETE & ~N_POS);
  set_flush(old_flush);

  double x,y;
  ran >> x >> y;
  double xd=x_max-x_min;
  double yd=y_max-y_min;
  v_inf.pos=point(x_min+(0.05+0.9*x)*xd,y_min+(0.05+0.9*y)*yd);
  v_inf.init=true;
}

//----------------------------------------------------------------------------

void GraphWin::init_edge(edge e) {


  edge_info &e_inf=e_info[e];

  bool old_flush=set_flush(false);

  set_info(e,e_model,E_COMPLETE & ~E_POLYGON);

  set_flush(old_flush);

  list<point>& P=e_inf.p;
  P.clear();
  P.push(get_position(source(e)));
  P.append(get_position(target(e)));

  e_inf.init=true;

}

//----------------------------------------------------------------------------

void GraphWin::init_graph() {
  n_info.init(*gr_p,n_model);
  e_info.init(*gr_p,e_model);

  // id_queue.clear();
  // max_node_id=0;

  selected_nodes.clear();
  node v;
  forall_nodes(v,*gr_p) {
    init_node(v);
    if (is_selected(v)) selected_nodes.push(v);
  }
  edge e;
  forall_edges(e,*gr_p) init_edge(e);
  init_matrix();
  edges_embedded = (gr_p->number_of_edges()?false:true);
}

//----------------------------------------------------------------------------

void GraphWin::update_nodes() {
  node v;
  forall_nodes(v,*gr_p) if (!n_info[v].init) init_node(v);
  if (get_flush()) {
    embed_edges();
    redraw();
  }
}

//----------------------------------------------------------------------------

void GraphWin::update_edges() {
  init_matrix();
  edge e;

  forall_edges(e,*gr_p) if (!e_info[e].init) init_edge(e);

  if (get_flush()) {
    embed_edges();
    redraw();
  }
}

//----------------------------------------------------------------------------

void GraphWin::update_graph() {
  bool old_flush=set_flush(false);
  update_nodes();
  update_edges();
  set_flush(old_flush);
  if (old_flush) {
    embed_edges();
    redraw();
  }
}

//----------------------------------------------------------------------------

int GraphWin::test_update_edges() {
  edge e;
  node v,w;

  graph& G=get_graph();

  forall_nodes(v,G) {

    node_array<unsigned> out_edges(G,0);

    forall_adj_edges(e,v) {

      w=gr_p->target(e);

      // Test, ob e uninitialisiert
      if (!e_info[e].init) return 1;

      // Test, ob fuer e eine Liste in dyn. node_matrix existiert
      if (!adj_matrix_first.defined(v,w)) return 2; 

      // Test, ob e in dieser List ist      
      edge x = adj_matrix_first(v,w);
      while(x && x != e) x = adj_matrix_succ[x];
      if (x == nil) return 3;

      out_edges[w]++;
    }

    // vergleiche eben angelegte Listen mit vorhandenen incidence-listen

    forall_nodes(w,G)
    { int m = out_edges[w];
      if (adj_matrix_first.defined(v,w))
      { edge x = adj_matrix_first(v,w);
        while (x) 
        { m--;
          x = adj_matrix_succ[x];
         }
       }
      if (m > 0) return 4;
     }
    }

  return 0;
}

int GraphWin::test_update_nodes() {
  node v;
  forall_nodes(v,*gr_p) if (!n_info[v].init) return 1;
  return 0;
}

bool GraphWin::test_update_graph() {
  if (test_update_nodes()) return true;
  if (test_update_edges()) return true;
  return false;
}

//----------------------------------------------------------------------------

void GraphWin::win_init(double xmin, double xmax, double ymin) {
  if (constant_width) {
    if (xmax+x_min != x_max+xmin) {
      bool old_flush=set_flush(false);
      double scale=(xmax-xmin)/(x_max-x_min);
      node v;
      set_node_radius1(get_node_radius1()*scale);
      set_node_radius2(get_node_radius2()*scale);
      set_edge_distance(get_edge_distance()*scale);
      forall_nodes(v,*gr_p) {
        set_radius1(v,get_radius1(v)*scale);  
        set_radius2(v,get_radius2(v)*scale);
      }
      //embed_edges();
      set_flush(old_flush);
    }
  }

  win_p->start_buffering();
  win_p->init(xmin, xmax, ymin,grid);
  units_per_pixel=1.0/win_p->scale();
  update_win_bounds();
  draw_graph();
  win_p->flush_buffer();
  win_p->stop_buffering();

}

//----------------------------------------------------------------------------

void GraphWin::status_redraw(window* w)
{ 
  if (!w->is_open()) return;

  GraphWin* gw = (GraphWin*)w->get_inf();
  graph&     g = gw->get_graph();
  int n = g.number_of_nodes();
  int m = g.number_of_edges();
  int l = 0;

  edge e;
  forall_edges(e,g)
      if (source(e) == target(e)) l++;

  char* flush_str = gw->get_flush() ? " on" : "off";
  int   grid_dist = gw->get_grid_mode();


 string s;
 if (gw->status_show_mask & STAT_NODES) s += string("nodes: %3d    ",n);
 if (gw->status_show_mask & STAT_EDGES) s += string("edges: %3d    ",m);
 if (gw->status_show_mask & STAT_LOOPS) s += string("loops: %3d    ",l);
 if (gw->status_show_mask & STAT_PLANAR)
     if (PLANAR(g)) 
        s += "planar: yes ";
     else
        s += "planar: no  ";
 s += "        ";
 if (gw->status_show_mask & STAT_FLUSH) s += string("flush: %s    ",flush_str);
 if (gw->status_show_mask & STAT_GRID)  s += string("grid: %2d    ",grid_dist);


  double x0 = w->xmin();
  double y0 = w->ymin();
  double x1 = w->xmax();
  double y1 = w->ymax();

  double ys = y1 - w->pix_to_real(1);
  double xs = x0 + w->pix_to_real(6);

  if (gw->status_show_mask & STAT_COORD)
     w->draw_box(x0,y0,xs+w->text_width(s),y1,gw->status_win->get_bg_color());
  else
     w->clear();
  w->draw_text(xs,ys,s,black);
}


void GraphWin::coord_handler(window* win, double x, double y) 
{
  GraphWin* gw = (GraphWin*)win->get_inf();
  window* swin = gw->status_win;

  if (!(gw->status_show_mask & STAT_COORD) || !swin->is_open()) return;

  double x0 = swin->xmin();
  double y0 = swin->ymin();
  double x1 = swin->xmax();
  double y1 = swin->ymax();

  string coord_str("%8.2f %8.2f",x,y);
  double dy = swin->text_width(coord_str) + 2;

  double cy = y1 - swin->pix_to_real(1);
  double cx = x1 - dy;
  swin->draw_box(cx,y0,x1,y1,swin->get_bg_color());
  swin->draw_text(cx,cy,coord_str);
}
 



  




void GraphWin::init_window(int x, int y) {
  init_call_menu();
  if (x == MAXINT && y == MAXINT)
     win_p->display();
  else
     win_p->display(x,y);
  if (win_p != menu_p) menu_p->display(0,0);

  win_p->set_bg_color(bg_color);
  win_p->init(x_min,x_max,y_min,grid);
  win_p->set_show_cursor(false);
  win_p->set_redraw(GraphWin::s_redraw);
  win_p->set_mode(src_mode);
  win_p->set_show_coord_handler(coord_handler);
 

  units_per_pixel=1.0/win_p->scale();
  update_win_bounds();
  win_init_done=true;
  flush=DefFlush;

  status_win = new window(win_p->width()-2,18);
  status_win->set_inf(this);
  status_win->set_bg_color(grey1);
  status_win->set_redraw(status_redraw);

  if (show_status) open_status_win();

  msg_win = new window(win_p->width()-2,18);
  msg_win->set_inf(this);
  msg_win->set_bg_color(ivory);


  if (leda_xpm == 0) 
     leda_xpm = win_p->create_pixrect(leda_icon);

}


