/*******************************************************************************
+
+  LEDA 3.5
+
+  _g_inout.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.
+ 
*******************************************************************************/

//------------------------------------------------------------------------------
// graph i/o
//
// S. Naeher  (last modified: December 1996)
//------------------------------------------------------------------------------


#include <LEDA/graph.h>
#include <LEDA/stream.h>
#include <ctype.h>
#include <string.h>

const char delim = '|';

void graph::write(string file_name) const
{ ofstream out(file_name);
  if (out.fail()) 
    error_handler(1,string("graph::write() cannot open file %s",file_name));
  write(out);
}


void graph::write(ostream& out) const
{
  int* A = new int[max_n_index+2];

  // nodes get numbers from 1 to |V|

  int count = 1;

  out << "LEDA.GRAPH" << endl;
  out << node_type()  << endl;
  out << edge_type()  << endl;

  out << v_list.length() << endl;

  node v;
  forall_nodes(v,*this)
  { out << delim << '{';
    write_node_entry(out,v->data[0]);
    out << '}' << delim << endl;
    A[index(v)] = count++;
   }

  out << number_of_edges() << endl;

  forall_nodes(v,*this)
  { edge e;
    int s = A[index(v)];
    forall_adj_edges(e,v)
    { if (source(e) != v) continue; // necessary for ugraphs
      out << s << " " << A[index(target(e))] << " " << delim << '{';
      write_edge_entry(out,e->data[0]);
      out << '}' << delim << endl;
     }
   }

 delete[] A;

}

int graph::read(string file_name)
{ ifstream in(file_name);
  if (in.fail())  return 1;
  return read(in);
}


static void read_data_entry(istream& in, char* buf, int buf_sz)
{ char* p = buf-1;
  char  c;

  do in.get(c); while (isspace(c));
  if (c != delim)
  { in.putback(c);
    string line = read_line(in);
    strcpy(buf,line.cstring());
    return;
   }

  in.get(c);
  if (c != '{')  
    error_handler(1,"graph::read(): error in graph format.");

  int nested = 1;

  while (nested)
  { in.get(c);
    if (c == delim && p >= buf) 
    { if (*p == '}') 
         nested--;
      else 
       { if (buf_sz-- <= 0) 
            error_handler(1,"graph::read: data overflow");
         *++p = c;
         in.get(c);
         if (c == '{') nested++;
        }
     }

    if (nested)
    { if (buf_sz-- <= 0) 
         error_handler(1,"graph::read: data overflow");
      *++p = c;
     }
   }

  *p = '\0';
}


int graph::read(istream& in)
{ 

  clear();

  int result = 0;
  edge e;
  int n,i,v,w;


  string this_n_type = node_type();
  string this_e_type = edge_type();

  string d_type,n_type,e_type;

  in >> d_type;
  in >> n_type; 
  in >> e_type;
  in >> n;

  if (d_type != "LEDA.GRAPH") return 3;

  read_line(in);

  node* A = new node[n+1];

  char data_str[1024];

  if (this_n_type == "void" || n_type != this_n_type)  // do not read node info
    { for (i=1; i<=n; i++)
      { A[i] = new_node();
        read_data_entry(in,data_str,1024);
       }
      if (n_type != this_n_type) result = 2;   // incompatible node types
     }
  else
    if (this_n_type == "string")
      for (i=1; i<=n; i++)
      { A[i] = new_node(0);
        read_data_entry(in,data_str,1024);
        A[i]->data[0] = leda_copy(string(data_str));
       }
    else
      for (i=1; i<=n; i++)
      { A[i] = new_node(0);
        read_data_entry(in,data_str,1024);
        istrstream str_in(data_str,strlen(data_str));
        read_node_entry(str_in,A[i]->data[0]);
       }

  in >> n;       // number of edges

  if (this_e_type == "void" || e_type != this_e_type) // do not read edge info
    { if (e_type != this_e_type) result = 2;   // incompatible edge types
      while (n--) 
      { in >> v >> w;
        e = new_edge(A[v],A[w]);
        read_data_entry(in,data_str,1024);
      }
     }
  else
    if (this_e_type == "string")
      while (n--) 
      { in >> v >> w;
        e = new_edge(A[v],A[w],GenPtr(0));
        read_data_entry(in,data_str,1024);
        e->data[0] = leda_copy(string(data_str));
       }
    else
      while (n--) 
      { in >> v >> w;
        e = new_edge(A[v],A[w],GenPtr(0));
        read_data_entry(in,data_str,1024);
        istrstream str_in(data_str,strlen(data_str));
        read_edge_entry(str_in,e->data[0]);
       }

  delete[] A;

  return result;
}


void graph::print_node(node v,ostream& o) const
{ if (super() != 0)
     super()->print_node(node(graph::inf(v)),o);
  else
     { o << "[" << index(v) <<"]" ;
       print_node_entry(o,v->data[0]);
      }
}

void graph::print_edge(edge e,ostream& o) const
{ if (super() != 0)
     super()->print_edge(edge(graph::inf(e)),o);
  else
     { o <<   "[" << index(source(e)) << "]";
       o <<  ((undirected) ? "==" : "--");
       print_edge_entry(o,e->data[0]);
       o <<  ((undirected) ? "==" : "-->");
       o << "[" << index(target(e)) << "]";
      }
}


void graph::print(string s, ostream& out) const
{ node v;
  edge e;
  out << s << endl;
  forall_nodes(v,*this)
  { print_node(v,out);
    out << " : ";
    forall_adj_edges(e,v) print_edge(e,out);
    out << endl;
   }
  out << endl;
}



// convert node and edge entries into a string and vice versa

string graph::get_node_entry_string(node v) const
{ ostrstream out;
  write_node_entry(out,v->data[0]);
  out << ends;
  char* p = out.str();
  string s(p);
  delete[] p;
  return s;
 }

string graph::get_edge_entry_string(edge e) const
{ ostrstream out;
  write_edge_entry(out,e->data[0]);
  out << ends;
  char* p = out.str();
  string s(p);
  delete[] p;
  return s;
 }


void graph::set_node_entry(node v, string s)
{ clear_node_entry(v->data[0]);
  if (strcmp(node_type(),"string") == 0)
     v->data[0] = leda_copy(s);
  else
    { istrstream in(s.cstring());
      read_node_entry(in,v->data[0]);
     }
 }

void graph::set_edge_entry(edge e, string s)
{ clear_edge_entry(e->data[0]);
  if (strcmp(edge_type(),"string") == 0)
     e->data[0] = leda_copy(s);
  else
    { istrstream in(s.cstring());
      read_edge_entry(in,e->data[0]);
     }
 }

