/*******************************************************************************
+
+  LEDA 3.5
+
+  io.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 <fstream.h>
#include "../../src/graphwin/ps.h" 


#if defined(__win32__)
#include <windows.h>
#include <direct.h>
#if defined(_MSC_VER)
#pragma warning(disable: 4237)
#endif
#endif

#if defined(unix)
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif


//----------------------------------------------------------------------------
// postscript output
//----------------------------------------------------------------------------

static void gw_print_ps(ostream& o, GraphWin& gw) 
{
  PostScript ps;

  const graph& G = gw.get_graph();

  double x0,y0,x1,y1;
  gw.get_bounding_box(x0,y0,x1,y1);

/*
  double gw_dx   = gw.get_xmax() - gw.get_xmin();
  double factor  = 0.7*ps.get_x_width()/gw_dx;
  ps.scale(factor,factor);
  ps.set_origin(-gw.get_xmin()+0.2*gw_dx,-gw.get_ymin()+0.2*gw_dx);
*/

  double gw_dx   = x1-x0;
  double factor  = 0.7*ps.get_x_width()/gw_dx;
  ps.scale(factor,factor);
  ps.set_origin(-x0+0.2*gw_dx,-y0+0.2*gw_dx);


  ps.set_line_width(1);

  int fsize = 9;
 
  ps.set_font("Helvetica",fsize);


  gw_edge_style style = gw.get_style(G.first_edge());
  int           width = gw.get_width(G.first_edge());
  bool       directed = gw.get_directed();

  ps.set_line_style((line_style) style);
  ps.set_line_width(width);

  edge e;
  forall_edges(e,G) 
  { if (width != gw.get_width(e)) 
    { width=gw.get_width(e);
      ps.set_line_width(width);
     }

    if (style != gw.get_style(e)) 
    { style=gw.get_style(e);
      ps.set_line_style((line_style) style);
     }

    const list<point>& P=gw.get_edge_points(e);
    color c=gw.get_color(e);

    if (directed) 
       ps.draw_polygon_arrow(P,c);
    else 
       ps.draw_polygon_edge(P,c);
  }
  
    
  node v;
  forall_nodes(v,G) 
  { point  p  = gw.get_position(v);
    double x  = p.xcoord();
    double y  = p.ycoord();
    double r1 = gw.get_radius1(v);
    double r2 = gw.get_radius2(v);
    color  c  = gw.get_color(v);
    color  bc = gw.get_border_color(v);
    
    switch(gw.get_shape(v)) {

      case circle_node:
        ps.draw_circle_node(x,y,r1,c);
        //ps.draw_circle(x,y,r1,bc);
        break;
        
      case ellipse_node:
        ps.draw_ellipse_node(x,y,r1,r2,c);
        ps.draw_ellipse(x,y,r1,r2,bc);
        break;
        
      case square_node:
        r2=r1;

      case rectangle_node:
        ps.draw_rectangle_node(x-r1,y-r2,x+r1,y+r2,c);
        ps.draw_rectangle(x-r1,y-r2,x+r1,y+r2,bc);
        break;
    }

    // if (gw.get_node_label_type() == no_label) continue;

/*
    double dx=20;
    double dy=10;

    gw_position label_pos=gw.get_label_pos(v);

    if (label_pos == central_pos) {
      ps.draw_ctext(p,gw.get_label(v));
      continue;
    }


    if (label_pos==east_pos || label_pos==northeast_pos || label_pos==southeast_pos)
      x+=r1+factor;
    else {
      if (label_pos==west_pos || label_pos==northwest_pos || label_pos==southwest_pos) 
        x-=r1+dx+factor;
      else x-=dx/2;
    }

    if (label_pos==north_pos || label_pos==northwest_pos || label_pos==northeast_pos)
      y+=r2+dy+factor;
    else {
      if (label_pos==south_pos || label_pos==southwest_pos || label_pos==southeast_pos)
        y-=r2+factor;
      else y+=dy/2;
    }
*/

    ps.draw_ctext(point(x,y),gw.get_label(v));
        
  }
 
  o << ps;
}


bool GraphWin::save_ps(string fname) {

  ofstream check(fname,ios::nocreate|ios::app);

  if (check.good()) 
  { panel P;
    P.buttons_per_line(2);
    P.text_item(string("File %s exists.",~fname)); 
    P.text_item("");
    P.button("overwrite",0);
    P.button("cancel",1);
    if (P.open(*win_p) == 1) return true;
   }
  ofstream o(fname,ios::out);
  if (!o) return false;
  gw_print_ps(o,*this);
  if (o.fail()) return false;
  o.close();
  return true;
}

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

static ostream& operator << (ostream& o, const node_info& inf) {
  o << inf.pos.xcoord()   << ' ';
  o << inf.pos.ycoord()   << ' ';
  o << int(inf.shape)     << ' ';
  o << int(inf.border_clr)<< ' ';
  o << inf.r1             << ' ';
  o << inf.r2             << ' ';
  o << int(inf.clr)       << ' ';
  o << int(inf.label_t)   << ' ';
  o << int(inf.label_clr) << ' ';
  o << int(inf.label_pos) << ' ';
  o << inf.label;
  return o;
}

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

static istream& operator >> (istream& i, node_info& inf) {
  double x,y,r1,r2; 
  int label_t,clr,label_clr,shape,border_clr,label_pos;
  i >> x;
  i >> y;
  i >> shape;
  i >> border_clr;
  i >> r1;
  i >> r2;
  i >> clr;
  i >> label_t;
  i >> label_clr;
  i >> label_pos;
  i.ignore(); // single space
  inf.label.read_line(i);

  inf.pos       = point(x,y);
  inf.clr       = clr;
  inf.r1        = r1;
  inf.r2        = r2;
  inf.shape     = (gw_node_shape)shape;
  inf.border_clr= border_clr;
  inf.label_t   = (gw_label_type)label_t;
  inf.label_pos = (gw_position)label_pos;
  inf.label_clr = label_clr;
  inf.selected  = 0;
  return i;
}

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

static void print_polygon(ostream& o, const list<point>& P) {
  point p;
  o << P.size() << ' ';
  forall(p,P) o << p.xcoord() << ' ' << p.ycoord() << ' ';
}

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

static void read_polygon(istream& i, list<point>& P) {
  P.clear();
  int k,n;
  double x,y;
  i >> n;
  for(k=0;k<n;k++) {
    i >> x >> y;
    P.append(point(x,y));
  } 
}

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

static ostream& operator << (ostream& o, const edge_info& inf) {
  o << inf.width          << ' ';
  o << int(inf.clr)       << ' ';
  o << int(inf.style)     << ' ';
  o << int(inf.label_t)   << ' ';
  o << int(inf.label_clr) << ' ';
  o << int(inf.label_pos) << ' ';
  print_polygon(o,inf.p);
  o << inf.label; 
  return o;
}

//----------------------------------------------------------------------------  
                                                                              
static istream& operator >> (istream& i, edge_info& inf) {   

  int width,label_t,clr,label_clr,style,label_pos;
  i >> width;
  i >> clr;
  i >> style;
  i >> label_t;
  i >> label_clr;
  i >> label_pos;
  read_polygon(i,inf.p);
  i.ignore();
  inf.label.read_line(i);

  inf.width     = width;
  inf.clr       = clr;
  inf.style     = (gw_edge_style)style;
  inf.label_t   = (gw_label_type)label_t;
  inf.label_pos = (gw_position)label_pos;
  inf.selected  = 0;

  return i;
}                                                                              
                                                                              
//----------------------------------------------------------------------------

bool GraphWin::save(string fname) {

  ofstream check(fname,ios::nocreate|ios::app);

  if (check.good()) 
  { panel P;
    P.buttons_per_line(2);
    P.text_item(string("File %s exists.",~fname)); 
    P.text_item("");
    P.button("overwrite",0);
    P.button("cancel",1);
    if (P.open(*win_p) == 1) return true;
   }
  
  ofstream out(fname);

  if (out.fail()) return false;

  update_graph();

  gr_p->write(out);

  // scaling factor
  out << win_p->scale() << endl;

  node v;
  forall_nodes(v,*gr_p) out << n_info[v] << endl;

  edge e;
  forall_nodes(v,*gr_p) forall_adj_edges(e,v) out << e_info[e] << endl;
 
  return !out.fail();
}  


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

int GraphWin::read(string fname) {

  bool old_flush=set_flush(false);

  ifstream in(fname);

  if (in.fail())  
  { // cannot open file
    if (old_flush) 
    { embed_edges();
      redraw();
     }
    set_flush(old_flush);
    return 1;             
  }

  graph G;

  int err= G.read(in);


  if (err == 3) 
  { // no LEDA-graph
    redraw();
    set_flush(old_flush);
    return 2;
  }

  clear_graph();

  *gr_p = G;

  edges_embedded=false;
  
  init_graph();

  double old_scale; 

  in >> old_scale;

  node v;
  forall_nodes(v,*gr_p) 
  { node_info ni;
    if (in >> ni) 
    { ni.r1 = (ni.r1*old_scale)/win_p->scale();
      ni.r2 = (ni.r2*old_scale)/win_p->scale();
      n_info[v] = ni;
     }
    else break;
   }

  if (in.fail()) 
  { // illegal node infos
    if (old_flush) {
      embed_edges();
      redraw();
    }
    set_flush(old_flush);
    return 3;
  }


  edge e;
  forall_edges(e,*gr_p)
  { edge_info ei;
    if (in >> ei) e_info[e] = ei;
    else break;
   }

  if (in.fail()) 
  { //  illegal edge infos
    if (old_flush) {
      embed_edges();
      redraw();
    }
    set_flush(old_flush);
    return 4;
  }


  //redraw();
  fill_window();

  set_flush(old_flush);
 
  return 0;
}  


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

enum { file_load, file_save, file_save_ps, file_all, file_cancel };

static panel* file_panel; 
static string dir_name;
static string filename;
static string suffix;
static list<string> dir_list;
static list<string> file_list;
static panel_item dir_item;
static panel_item file_item;

void change_dir(char* dname) 
{ 
  chdir(dname);
  char dir_buf[256];
  getcwd(dir_buf,256);
  dir_name = dir_buf;

  dir_list.clear();

  file_list.clear();
  file_list.append(filename);

#if defined(unix)
  DIR* dir_p = opendir(".");
  dirent* dir_e;
  while ( (dir_e = readdir(dir_p)) != NULL )
  { string fname = dir_e->d_name;
    struct stat stat_buf;
    stat(fname,&stat_buf);
    if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) 
       dir_list.append(fname);
    else
     { int pos = fname.pos(".");
       int len = fname.length();
       if (suffix == "" || 
           fname != filename && pos > 0 && fname(pos,len-1) == suffix) 
         file_list.append(fname);
      }
   }
  closedir(dir_p);
#endif

#if defined(__win32__)
  WIN32_FIND_DATA fd;
  HANDLE ha = FindFirstFile("*",&fd);
  if (ha) 
  do { string fname = fd.cFileName;
       if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          dir_list.append(fname);
       else
        { int pos = fname.pos(".");
          int len = fname.length();
          if (suffix == "" || 
              fname != filename && pos > 0 && fname(pos,len-1) == suffix) 
             file_list.append(fname);
         }
   } while (FindNextFile(ha,&fd));
  FindClose(ha);
#endif

 if (dir_list.head() == ".") dir_list.pop();

 file_list.sort();
 dir_list.sort();

 file_panel->add_menu(dir_item,dir_list);
 file_panel->add_menu(file_item,file_list);
 file_panel->redraw_panel();

}



void GraphWin::file_handler(int what)
{
  bool old_flush=set_flush(true);

  window& W=get_window();

  W.disable_panel();

  suffix = "";

/*
  if (what == file_save_ps)
     suffix = ".ps";
  else
     suffix = ".gw";
*/

  panel P;
  file_panel = &P;

  P.buttons_per_line(4);

  switch (what) {

  case file_all:  P.text_item("\\bf File Panel");
                  break;

  case file_load: P.text_item("\\bf Load Graph");
                  filename = get_filename();
                  break;

  case file_save: P.text_item("\\bf Save Graph");
                  filename = get_filename();
                  break;

  case file_save_ps: 
                  P.text_item("\\bf Write Postscript File");
                  filename = get_ps_filename();
                  break;
  }

  dir_list.append(".");
  file_list.append(filename);

  dir_item  = P.string_item("directory",dir_name,dir_list,change_dir);
  file_item = P.string_item("file name",filename,file_list);

  change_dir(".");

  switch (what) {

  case file_all:  P.button("load",file_load);
                  P.button("save",file_save);
                  P.button("save ps",file_save_ps);
                  break;

  case file_load: P.button("load",file_load);
                  break;

  case file_save: P.button("save",file_save);
                  break;

  case file_save_ps: 
                  P.button("write ps",file_save_ps);
                  break;
  }

  P.button("cancel",file_cancel);

  P.display(W,window::center,window::center);

  int but = P.read_mouse();
    
  P.close();

  switch(but) {
  
    case file_load : {
      if (!check_init_graph_handler()) break;
      switch (read(filename)) {
       case 1 : acknowledge(string("Cannot not open file %s",~filename));
                break;
       case 2 : acknowledge("File does not contain a graph.");
                break;
       case 3 : acknowledge("No valid node infos found."); 
                break;
       case 4 : acknowledge("No valid edge infos found.");
                break;
      }
      set_filename(filename);
      call_init_graph_handler();
      break;
    }
      

    case file_save : 
      if (!save(filename)) acknowledge("Cannot save graph!");
      set_filename(filename);
      break;
      

    case file_save_ps :
      save_ps(filename);
      set_ps_filename(filename);
      break;
      

    case file_cancel :
      break;
  }

  set_flush(old_flush);

  W.enable_panel();
}


void gw_load_handler(GraphWin& gw)    { gw.file_handler(file_load);    }
void gw_save_handler(GraphWin& gw)    { gw.file_handler(file_save);    }
void gw_save_ps_handler(GraphWin& gw) { gw.file_handler(file_save_ps); }
void gw_file_handler(GraphWin& gw)    { gw.file_handler(file_all);     }
