/* Copyright (c) 1997-2005
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ident "$Project: polymake $$Id: se_interactive.cc,v 1.5 2005/02/21 17:28:31 gawrilow Exp $"

#include <SimpleGeometryParser.h>
#include <SpringEmbedderWindow.h>
#include <Poly.h>
#include <socketstream.h>
#include <ListMatrix.h>
#include <unistd.h>

namespace polymake { namespace graph {

namespace {

enum { opt_read_edge_weights=SpringEmbedder::opt_next, opt_skip_node_attr, opt_seed, opt_max_iter, opt_step };

template <typename Traits>
bool parse(SimpleGeometryParser& p, std::basic_istream<char, Traits>& is, SpringEmbedderWindow& W)
{
   ListMatrix< Vector<double> > points(0,3);
   char command;
   while ((command=p.get_command(is)) != 0) {
      switch (command) {
      case 'n':
	 { // get the name: n <name>
	    break;
	 }
      case 'p': 
	 { // get a point: p <x> <y> <z>
	    points /= p.get_point().second;
	    break;
	 }
      case 's': 
	 { // get a parameter: s <param_name> <double_value>
	    std::string param;
	    double value;
	    p.line >> param >> value;
	    W.set_param(param,value);
	    break;
	 }
      case 'f': 
	 { // get a facet: f <v_1> ... <v_n>
	    W.set_facet(p.get_facet());
	    break;
	 }
      case 'x':
	 { // end of packet sent
	    if (W.get_param("restart") != 0) {
	       //reinit all!
	       W.reset();
	    } else if (points.rows() != 0) {
	       W.start_points(rows(points).begin());
	    }
	    W.adjust_params();
	    return true;
	 }
      default: 
	 throw std::runtime_error("SimpleGeometryParser: unknown command: " + command);
      }
   }
   return false;
}

} // end unnamed namespace

void se_interactive(Poly& p, const char* graph_section, std::socketstream& javaview, const argv_option *options)
{
   SpringEmbedder SE(options);
   SimpleGeometryParser parser;

   std::string line, geom_name;
   if (!getline(javaview,line)) return;
   if (line.substr(0,5) == "read ")
      geom_name=line.substr(5);

   if (options[opt_skip_node_attr]) {
      if (options[opt_read_edge_weights]) {
	 p.give(graph_section) >> ignore_node_attrs<int>(SE.G);
      } else {
	 p.give(graph_section) >> ignore_attrs<int,nothing>(SE.G);
      }
   } else {
      if (options[opt_read_edge_weights]) {
	 p.give(graph_section) >> ignore_node_attrs(SE.G);
      } else {
	 p.give(graph_section) >> ignore_attrs(SE.G);
      }
   }
   SE.adjust_edge_lengths(options[opt_read_edge_weights]);
    
   if (options[SpringEmbedder::opt_objective]) {
      const Matrix<double> V = p.give("VERTICES");
      Vector<double> Obj=p.give(options[SpringEmbedder::opt_objective]);
      Obj[0]=0;
      Obj /= std::sqrt(sqr(Obj));
      SE.apply_objective_function(V, Obj);
   }

   time_t t;
   long seed = options[opt_seed] ? atol(options[opt_seed]) : (time(&t), t);
   int max_iter=options[opt_max_iter] ? atoi(options[opt_max_iter]) : 10000;
    
   SpringEmbedderWindow se_window(SE,max_iter,seed);
   if (options[opt_step]) {
      se_window.set_param("step",atof(options[opt_step]));
      se_window.set_param("continue",1);
      se_window.compute_points(0);
      parser.print_short(javaview,geom_name,se_window);
   }
      
   do {
      int count = 0;
      if (se_window.get_param("step") != 0) {
	 int iter = (int)se_window.get_param("step");
	 if (se_window.get_param("max_iter") != 0) {
	    max_iter = (int)se_window.get_param("max_iter");
	 }
	
	 while ( !se_window.compute_points(iter) && count <= max_iter ) {
	    count += iter;
	    usleep((int)se_window.get_param("delay")*1000);
	    parser.print_short(javaview, geom_name, se_window);
	    if (!parse(parser,javaview,se_window)) {
	       return;
	    }
	    if (se_window.get_param("continue") == 0) {
	       break;
	    }
	 }
	 se_window.set_param("continue",0);
	 parser.print_short(javaview, geom_name, se_window);

      } else if (se_window.get_param("restart") != 0) {
	 se_window.set_param("restart",0);
	 se_window.compute_points(0);
	 parser.print_short(javaview, geom_name, se_window);

      } else {
	 count = max_iter;
	 if (! se_window.compute_points(max_iter)) {
	    cerr << "WARNING: spring_embedder not converged after " << max_iter << " iterations" << endl;
	 }
	 parser.print_short(javaview,geom_name, se_window);
      }
   } while (parse(parser,javaview,se_window));
}

} }

using namespace polymake;

/** @file se_interactive
 *
 *  Driver for interactive graph visualization
 *
 *  @synopsis se_interactive <file.poly> <port> [ -objective <section>  -read-edge-weights  -seed <s>  -max-iterations <n>  {-eps,-scale,-balance,-viscosity,-inertion,-objective-factor} <x> ... ]
 *
 *  @reading  
 */

int main(int argc, const char *argv[])
{
   argv_option options[]={ { "-viscosity", true }, { "-inertion", true }, { "-eps", true },
			   { "-scale", true }, { "-balance", true },
			   { "-objective", true }, { "-objective-factor", true },
			   { "-read-edge-weights", false }, { "-skip-node-attr", false },
			   { "-seed", true }, { "-max-iterations", true }, { "-step", true }
   };
  
   if (!extract_options(argc,argv,3,options) || argc < 4) {
      cerr << "usage: " << argv[0] << " <file.poly> <graph_section> <fd> [ -objective <section>  -read-edge-weights -skip-node-attr  -seed <s>  -max-iterations <n>  {-eps,-scale,-balance,-viscosity,-inertion,-objective-factor, -step} <x> ... ]" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      std::socketstream javaview(argv[3]);
      graph::se_interactive(p, argv[2], javaview, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}

// Local Variables:
// c-basic-offset:3
// End:
