
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC/LIP6 2008-2009, All Rights Reserved
//
// ===================================================================
//
// $Id$
//
// x-----------------------------------------------------------------x 
// |                                                                 |
// |                   C O R I O L I S                               |
// |    I s p d   G l o b a l   r o u t i n g  -  M a i n   G U I    |
// |                                                                 |
// |  Author      :                       Damien Dupuis              |
// |  E-mail      :               Damien.Dupuis@lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./IspdMain.cpp"                           |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include  <boost/program_options.hpp>
namespace poptions = boost::program_options;

#include  <QtGui>

#include "hurricane/DataBase.h"
#include "hurricane/Technology.h"
#include "hurricane/Cell.h"
#include "hurricane/Warning.h"
#include "hurricane/Net.h"
#include "hurricane/RoutingPad.h"
#include "hurricane/UpdateSession.h"
#include "hurricane/Horizontal.h"
#include "hurricane/Vertical.h"
#include "hurricane/viewer/Graphics.h"
#include "hurricane/viewer/HApplication.h"
using namespace Hurricane;

#include "crlcore/Utilities.h"
#include "crlcore/AllianceFramework.h"
using namespace CRL;

#include "knik/GraphicKnikEngine.h"
#include "knik/KnikEngine.h"
#include "knik/NetExtension.h"
using namespace Knik;

#include "katabatic/GraphicKatabaticEngine.h"
using namespace Katabatic;

#include "IspdGui.h"
using namespace Ispd;


namespace {
// -------------------------------------------------------------------
// Global variables

# define    LINE_SIZE    4096

    enum ParserState { StateGrid
                     , StateVerti
                     , StateHoriz
                     , StateWidth
                     , StateSpacing
                     , StateVia
                     , StateDim
                     , StateNet
                     , StateObs
                     , StateEOF
                     };

    unsigned __congestion__    = 1;
    unsigned __precongestion__ = 2;
    float    __edge_cost__     = 3.0;


    Cell*               _cell = NULL;
    DataBase::DataBase* _db   = NULL;
    KnikEngine*         _knik = NULL;
    AllianceFramework*  _af   = AllianceFramework::create ();
    string              _filePath;
    int                 _parserState = StateGrid;
    size_t              _lineNumber = 0;
    char                _rawLine[LINE_SIZE];
    unsigned            _nbGCellsX;
    unsigned            _nbGCellsY;
    unsigned            _nbLayers;
    DbU::Unit           _lowerLeftX;
    DbU::Unit           _lowerLeftY;
    DbU::Unit           _tileWidth;
    DbU::Unit           _tileHeight;
    vector<unsigned>    _vertiCap;
    vector<unsigned>    _horizCap;
    vector<unsigned>    _minWidth;
    vector<unsigned>    _minSpacing;
    vector<unsigned>    _viaSpacing;
    unsigned            _nbNets;
    unsigned            _nbObs;
    Layer*              _layers[2];
    bool                _createRings = true;

// -------------------------------------------------------------------
// Function  :  "printHelp()".

  void  printHelp ()
  {
    cout << endl;
    cout << "Usage: unicorn [-v|--verbose] [-V|--very-verbose] [-D|--core-dump]    \\\n"
         << "               [-l|--trace-level <traceLevel>] [-c|--cell <cellName>] \\\n"
         << endl;
    cout << "Options:\n"
         << "    o  [-v|--verbose]      : First level of verbosity.\n"
         << "    o  [-V|--very-verbose] : Second level of verbosity (very talkative).\n"
         << "    o  [-D|--core-dump]    : Enable core dumping.\n"
         << "    o  [-l|--trace-level <traceLevel>] :\n"
         << "           Sets the level of trace, trace messages with a level superior to\n"
         << "           <traceLevel> will be printed on <stderr>.\n"
         << "    o  [-c|--cell <cellName>] :\n"
         << "           The name of the Cell to load, without extention.\n"
         << "    o  [-k|--knik] : Run the global router : Knik.\n"
         << "    o  [-K|--KNIK] : Run the global router : Knik, then analyse routing and unroute overflowed segments.\n"
         << endl;
  }

  void  _printWarning ( const char* format, ... )
  {
    static char     formatted [ 8192 ];
           va_list  args;

    va_start ( args, format );
    vsnprintf ( formatted, 8191, format, args );
    va_end ( args );

    cerr << "[WARNING] GrParser(): " << formatted << "\n"
         << "          (file: " << _filePath << ", line: " << _lineNumber << ")" << endl;
  }

  void  _printError ( bool interrupt, const char* format, ... )
  {
    static char     formatted [ 8192 ];
           va_list  args;

    va_start ( args, format );
    vsnprintf ( formatted, 8191, format, args );
    va_end ( args );

    cerr << "[ERROR] GrParser(): " << formatted << "\n"
         << "        (file: " << _filePath << ", line: " << _lineNumber << ")" << endl;

    if ( interrupt )
      throw Error ( "GrParser processed" );
  }

  long _getLong ( const char* s )
  {
      char* end;
      long value = strtol ( s, &end, 10 );
      if ( *end != '\0' )
          _printError ( false, "Incomplete string to integer conversion for \"%s\" (%ld).", s, value );
      return value;
  }

  vector<char*>  _splitString ( char* s )
  {
    vector<char*>  fields;

    fields.push_back ( s );
    while ( *s != '\0' ) {
      unsigned i = 0;
      if ( *s == ' ' || *s == '\t' ) {
        i++;
        *s = '\0';
        while ( *(s+i) == ' ' || *(s+i) == '\t' )
          i++;
        fields.push_back ( s+i );
        s += i;
      }
      else 
        s++;
    }

    return fields;
  }

  vector<char*>  _splitSegmentString ( char* s )
  {
    vector<char*>  fields;

    //fields.push_back ( s );
    while ( *s != '\0' ) {
      unsigned i = 0;
      if ( *s == '(' || *s == ')' || *s == ',' || *s == '-' ) {
        i++;
        *s = '\0';
        while ( *(s+i) == '(' || *(s+i) == ')' || *(s+i) == ',' || *(s+i) == '-' )
          i++;
        if ( *(s+i+1) != '\0' )
            fields.push_back ( s+i );
        s += i;
      }
      else 
        s++;
    }

    return fields;
  }

  void  _parseGrid ()
  {
    if ( strncmp(_rawLine,"grid", 4) )
      _printError ( true, "Missing Grid Declaration." );

    vector<char*> fields =_splitString ( _rawLine+5 );
    if ( fields.size() != 3 ) {
      _printError ( true, "Malformed Grid Line." );
    }
    else {
        _nbGCellsX = _getLong ( fields[0] );
        _nbGCellsY = _getLong ( fields[1] );
        _nbLayers  = _getLong ( fields[2] );
    }
    if ( _nbLayers != 2 )
        _printError ( false, "Global routing only supports two layers right now !" );
  }

  void  _parseVerti ()
  {
    if ( strncmp(_rawLine, "vertical capacity", 17) )
        _printError ( true, "Missing Vertical Declaration." );
          
    vector<char*>  fields = _splitString ( _rawLine+18 );

    if ( fields.size() < _nbLayers )
      _printError ( true, "Malformed Vertical line." );
    else 
        for ( unsigned i = 0 ; i < _nbLayers ; i++ ) {
            _vertiCap.push_back ( _getLong ( fields[i] ) );
        }
  }

  void  _parseHoriz ()
  {
    if ( strncmp(_rawLine, "horizontal capacity", 19) )
        _printError ( true, "Missing Horizontal Declaration." );
          
    vector<char*>  fields = _splitString ( _rawLine+20 );

    if ( fields.size() < _nbLayers )
      _printError ( true, "Malformed Horizontal line." );
    else
        for ( unsigned i = 0 ; i < _nbLayers ; i++ ) {
            _horizCap.push_back ( _getLong ( fields[i] ) );
        }
  }

  void  _parseWidth ()
  {
    if ( strncmp(_rawLine, "minimum width", 13) )
        _printError ( true, "Missing Minimum Width Declaration." );
          
    vector<char*>  fields = _splitString ( _rawLine+14 );

    if ( fields.size() < _nbLayers )
      _printError ( true, "Malformed Minimum Width line." );
    else
        for ( unsigned i = 0 ; i < _nbLayers ; i++ ) {
            _minWidth.push_back ( _getLong ( fields[i] ) );
        }
  }

  void  _parseSpacing ()
  {
    if ( strncmp(_rawLine, "minimum spacing", 15) )
        _printError ( true, "Missing Minimum Spacing Declaration." );
          
    vector<char*>  fields = _splitString ( _rawLine+16 );

    if ( fields.size() < _nbLayers )
      _printError ( true, "Malformed Minimum Spacing line." );
    else
        for ( unsigned i = 0 ; i < _nbLayers ; i++ ) {
            _minSpacing.push_back ( _getLong ( fields[i] ) );
        }
  }

  void  _parseVia ()
  {
    if ( strncmp(_rawLine, "via spacing", 11) )
        _printError ( true, "Missing Via Spacing Declaration." );
          
    vector<char*>  fields = _splitString ( _rawLine+12 );

    if ( fields.size() < _nbLayers )
      _printError ( true, "Malformed Via Spacing line." );
    else
        for ( unsigned i = 0 ; i < _nbLayers ; i++ ) {
            _viaSpacing.push_back ( _getLong ( fields[i] ) );
        }
  }

  void  _parseDim ()
  {
    vector<char*>  fields = _splitString ( _rawLine );

    if ( fields.size() < 4 )
      _printError ( true, "Malformed Dimension line." );
    else {
        _lowerLeftX = DbU::lambda ( _getLong ( fields[0] ) );
        _lowerLeftY = DbU::lambda ( _getLong ( fields[1] ) );
        _tileWidth  = DbU::lambda ( _getLong ( fields[2] ) );
        _tileHeight = DbU::lambda ( _getLong ( fields[3] ) );
    }

    DbU::Unit cellWidth  = (2*_lowerLeftX)+(_nbGCellsX*_tileWidth);
    DbU::Unit cellHeight = (2*_lowerLeftY)+(_nbGCellsY*_tileHeight);

    cmess1 << "  o  Creating cell ..." << endl
           << "     - " << _nbGCellsX << "x" << _nbGCellsY << " -> " << DbU::getValueString(cellWidth) << "x" << DbU::getValueString(cellHeight) << endl
           << "     - congestion: " << __congestion__ << endl
           << "     - precongestion: " << __precongestion__ << endl
           << "     - edge cost: " << __edge_cost__ << endl;
    _cell = Cell::create ( _af->getLibrary(0), _filePath );
    assert ( _cell );
    _cell->setTerminal(0);
    _cell->setAbutmentBox ( Box ( DbU::lambda(0), DbU::lambda(0), cellWidth, cellHeight ) );
    _knik = KnikEngine::get ( _cell );
    if ( !_knik )
        _knik = KnikEngine::create ( _cell, __congestion__, __precongestion__, true, true, __edge_cost__ );

    unsigned hcapacity = 0;
    for ( unsigned i = 0 ; i < _horizCap.size() ; i++ )
        hcapacity += _horizCap[i];
    hcapacity = hcapacity / ( _minWidth[0]+_minSpacing[0] );

    unsigned vcapacity = 0;
    for ( unsigned i = 0 ; i < _vertiCap.size() ; i++ )
        vcapacity += _vertiCap[i];
    vcapacity = vcapacity / ( _minWidth[1]+_minSpacing[1] ); // XXX we consider only 2 layers!!!
    
    _knik->createRoutingGrid ( _nbGCellsX, _nbGCellsY, _cell->getAbutmentBox(), _tileWidth, _tileHeight, hcapacity, vcapacity );
    // for ispd07 reload
    _knik->createRoutingGraph();
  }

  void  _parseNets ()
  {
    if ( strncmp(_rawLine, "num net", 7) )
        _printError ( true, "Missing Number of Nets Declaration." );
          
    cmess1 << "  o  Parsing nets ..." << endl;

    vector<char*>  fields = _splitString ( _rawLine+8 );
    if ( fields.size() != 1 )
      _printError ( true, "Malformed Number of Nets line." );
    else 
        _nbNets = _getLong ( fields[0] );
    
    cmess1 << "     - " << _nbNets << " nets found" << endl;
  }

  void _parseNet ( Net* &net, unsigned &nbPins)
  {
      vector<char*> fields = _splitString ( _rawLine );
      if ( fields.size() != 4 )
          _printError ( true, "Malformed Net Line." );
      else {
          Name netName = Name ( fields[0] );
          long netID = _getLong ( fields[1] );

          nbPins = _getLong ( fields[2] );
          net    = Net::create ( _cell, netName );

          //net->put ( StandardPrivateProperty<unsigned>::create(netID) );
          NetExtension::setId ( net, netID );
      }
  }

  void _parseNode ( Net* net, RoutingPad* &firstRoutingPad )
  {
      DbU::Unit x,y;
      long layerID = 0;
      vector<char*> fields = _splitString ( _rawLine );
      if ( fields.size() != 3 ) {
          for (unsigned i = 0 ; i < fields.size(); i ++ ){
              cerr << fields[i] << ",";
          }
          cerr << endl;
          _printError ( true, "Malformed Node Line." );
      }
      else {
          x = DbU::lambda ( _getLong ( fields[0] ) );
          y = DbU::lambda ( _getLong ( fields[1] ) );
          layerID = _getLong ( fields[2] ) - 1;
      }
      //UpdateSession::open();
      Contact*    contact    = Contact::create ( net, _layers[layerID], x, y, DbU::lambda(2), DbU::lambda(2) );
      RoutingPad* routingPad = RoutingPad::create ( net, Occurrence ( contact ) );

      // Dans le cas d'un chargment de solution, il se peut que le routingPad ne soit pas au centre du vertex -> on crée arbitrairement un contact au centre qu'on attache au vertex
      if ( !_createRings )
        _knik->addRoutingPadToGraph ( routingPad );

      if ( _createRings ) {
          if ( firstRoutingPad )
              routingPad->getBodyHook()->attach ( firstRoutingPad->getBodyHook() );
          else
              firstRoutingPad = routingPad;
      }
      //UpdateSession::close();
  }

  void _parseObs ()
  {
    cmess1 << "  o  Parsing obstacles ..." << endl;

    vector<char*>  fields = _splitString ( _rawLine );
    if ( fields.size() != 1 )
      _printError ( true, "Malformed Number of Obstacles line." );
    else 
        _nbObs = _getLong ( fields[0] );
    
    cmess1 << "     - " << _nbObs << " obstacles found" << endl;
  }

  void _parseObstacle ()
  {
      unsigned col1, row1, lID1, col2, row2, lID2, cap;

      vector<char*>  fields = _splitString ( _rawLine );
      if ( fields.size() != 7 )
          _printError ( true, "Malformed Obstacle line." );
      else {
          col1 = _getLong ( fields[0] );
          row1 = _getLong ( fields[1] );
          lID1 = _getLong ( fields[2] );
          col2 = _getLong ( fields[3] );
          row2 = _getLong ( fields[4] );
          lID2 = _getLong ( fields[5] );
          cap  = _getLong ( fields[6] );

          if ( lID1 != lID2 )
              _printError( true, "Layers must be the same on Obstacle line." );

          cap = cap / (_minWidth[lID1-1]+_minSpacing[lID1-1]);
          _knik->updateEdgeCapacity ( col1, row1, col2, row2, cap );
      }
  }

  Cell* loadFromFile()
  {
    string fullPath = _filePath;
    fullPath += ".gr";
    cmess1 << "  o  Loading cell :" << fullPath << endl;


    IoFile fileStream ( fullPath );
    fileStream.open ( "r" );
    if ( !fileStream.isOpen() ) {
        cerr << " [ERROR] Can't find file : " << fullPath  << " !" << endl;
        exit (48);
    }

    _db = DataBase::getDB ();
    _layers[0] = _db->getTechnology()->getLayer ( Name ( "metal1" ) );
    _layers[1] = _db->getTechnology()->getLayer ( Name ( "metal2" ) );
    
    _lineNumber  = 0;
    _parserState = StateGrid;

    try {
      while ( !fileStream.eof() ) {
        fileStream.readLine ( _rawLine, LINE_SIZE );
        _lineNumber++;

        if ( _rawLine[0] == '\0' ) {
          if ( _parserState == StateEOF ) break;
          continue;
        } else {
          if ( _parserState == StateEOF )
            _printError ( true, "Garbage after EOF." );
        }
        if ( !strcmp(_rawLine,"EOF") ) { _parserState = StateEOF; continue; }

        if ( _parserState == StateGrid ) {
          _parseGrid ();
          _parserState = StateVerti;
          continue;
        }

        if ( _parserState == StateVerti ) {
          _parseVerti ();
          _parserState = StateHoriz;
          continue;
        }

        if ( _parserState == StateHoriz ) {
          _parseHoriz ();
          _parserState = StateWidth;
          continue;
        }

        if ( _parserState == StateWidth ) {
          _parseWidth ();
          _parserState = StateSpacing;
          continue;
        }

        if ( _parserState == StateSpacing ) {
          _parseSpacing ();
          _parserState = StateVia;
          continue;
        }

        if ( _parserState == StateVia ) {
          _parseVia ();
          _parserState = StateDim;
          continue;
        }

        if ( _parserState == StateDim ) {
          _parseDim ();
          _parserState = StateNet;
          continue;
        }

        if ( _parserState == StateNet ) {
          _parseNets ();
          for ( unsigned i = 0 ; i < _nbNets ; i++ ) {
              fileStream.readLine ( _rawLine, LINE_SIZE );
              _lineNumber++;
              Net*        net = NULL;
              RoutingPad* firstRoutingPad = NULL;
              unsigned    nbPins = 0;
              _parseNet( net, nbPins );
              for ( unsigned j = 0 ; j < nbPins ; j++ ) {
                  fileStream.readLine ( _rawLine, LINE_SIZE );
                  _lineNumber++;
                  _parseNode ( net, firstRoutingPad );
              }
              //cmess1 << "     [";
              //cmess1.width(3);
              //cmess1 << floor((float)(i*100/(float)(_nbNets)));
              //cmess1 << "%]\r";
          }
          cmess1 << "     [100%] Done." << endl;

          _knik->initGlobalRouting();
          _parserState = StateObs;
          continue;
        }

        if ( _parserState == StateObs ) {
            _parseObs ();
            for ( unsigned i = 0 ; i < _nbObs ; i++ )
            {
                fileStream.readLine ( _rawLine, LINE_SIZE );
                _lineNumber++;
                _parseObstacle ();
                //cmess1 << "     [" << floor((float)(i*100/(float)(_nbObs))) << "%]\r";
            }
            cmess1 << "     [100%] Done." << endl;
            _parserState = StateEOF;
            continue;
        }
      }
    } catch ( Error& e ) {
      if ( e.what() != "[ERROR] GrParser processed" )
        cerr << e.what() << endl;
    }

    fileStream.close ();

    return _cell;
  }

  void loadSolutionFile()
  // ********************
  {
    cmess1 << "  o  Loading solution :" << _filePath << endl;


    IoFile fileStream ( _filePath );
    fileStream.open ( "r" );
    if ( !fileStream.isOpen() ) {
        cerr << " [ERROR] Can't find file : " << _filePath  << " !" << endl;
        exit (48);
    }

    _db = DataBase::getDB ();
    Layer* _gmetalh  = _db->getTechnology()->getLayer ( Name ( "gmetalh"  ) );
    Layer* _gmetalv  = _db->getTechnology()->getLayer ( Name ( "gmetalv"  ) );
    Layer* _gcontact = _db->getTechnology()->getLayer ( Name ( "gcontact" ) );
    
    _lineNumber  = 0;
    unsigned _uselessContact = 0;
    unsigned _illegalVerti   = 0;
    unsigned _illegalHoriz   = 0;
    unsigned _illegalDiag    = 0;
    unsigned _totalVias      = 0;
    unsigned _validSegments  = 0;

    try {
      while ( !fileStream.eof() ) {
        fileStream.readLine ( _rawLine, LINE_SIZE );
        _lineNumber++;

        if ( _rawLine[0] == '\0' )
            break;
        if ( _rawLine[0] == '\n' ) 
            continue;

        vector<char*> fields = _splitString ( _rawLine );
        if ( fields.size() != 3 )
            _printError ( true, "Malformed Net Line." );
        else {
            Name netName = Name ( fields[0] );
            /*long netID =*/ _getLong ( fields[1] );

            unsigned nbPins = _getLong ( fields[2] );
            Net* net = _cell->getNet ( netName );
            if ( !net ) {
                string message = "Parse solution failed : cannot find net : ";
                message += getString(netName);
                _printError ( true , message.c_str() );
            }
            
            vector<Segment*> segments;
            for ( unsigned i = 0 ; i < nbPins ; i++ ) {
                fileStream.readLine ( _rawLine, LINE_SIZE );
                _lineNumber++;
                fields = _splitSegmentString ( _rawLine );
                if ( fields.size() != 6 )
                    _printError ( true, "Malformed Net Line." );
                else {
                    DbU::Unit xSource = DbU::lambda(_getLong(fields[0]));
                    DbU::Unit ySource = DbU::lambda(_getLong(fields[1]));
                    unsigned  zSource =  (unsigned)(_getLong(fields[2]));
                    DbU::Unit xTarget = DbU::lambda(_getLong(fields[3]));
                    DbU::Unit yTarget = DbU::lambda(_getLong(fields[4]));
                    unsigned  zTarget =  (unsigned)(_getLong(fields[5]));

                    if ( xSource == xTarget ) {
                        if ( ySource == yTarget ) { //contact
                            if ( zSource != zTarget ) {
                                //UpdateSession::open();
                                Contact::create ( net, _gcontact, xSource, ySource );
                                //UpdateSession::close();
                                _totalVias++;
                            }
                            else
                                _uselessContact++;
                        }
                        else { // segment vertical
                            if ( zSource != zTarget ) // illegal segment
                                _illegalVerti++;
                            else {
                                //UpdateSession::open();
                                Vertical* verti = Vertical::create ( net, _gmetalv, xSource );
                                segments.push_back(verti);
                                if ( ySource < yTarget ) {
                                    verti->setDySource ( ySource );
                                    verti->setDyTarget ( yTarget );
                                }
                                else {
                                    verti->setDySource ( yTarget );
                                    verti->setDyTarget ( ySource );
                                }
                                _knik->insertSegment ( verti );
                                //UpdateSession::close();
                                _validSegments++;
                            }
                        }
                    }
                    else { // segment horizontal
                        if ( ySource != yTarget )
                            _illegalDiag++;
                        else {
                            if ( zSource != zTarget )
                                _illegalHoriz++;
                            else {
                                //UpdateSession::open();
                                Horizontal* horiz = Horizontal::create ( net, _gmetalh, ySource );
                                segments.push_back(horiz);
                                if ( xSource < xTarget ) {
                                    horiz->setDxSource ( xSource );
                                    horiz->setDxTarget ( xTarget );
                                }
                                else {
                                    horiz->setDxSource ( xTarget );
                                    horiz->setDxTarget ( xSource );
                                }
                                _knik->insertSegment ( horiz );
                                //UpdateSession::close();
                                _validSegments++;
                            }
                        }
                    }
                }
            }
            fileStream.readLine ( _rawLine, LINE_SIZE );
            _lineNumber++;
            if ( _rawLine[0] != '!' ) 
                throw Error ("gnagnagnagnagna"+getString(_lineNumber));
            // on va relier les segments et les contacts :
            for ( unsigned i = 0 ; i < segments.size() ; i++ ) {
                Segment* segment = segments[i];
                Point sourcePos = segment->getSourcePosition();
                Point targetPos = segment->getTargetPosition();
                Contact* source = NULL;
                Contact* target = NULL;
                forEach ( Contact*, contact, net->getContacts() ) {
                    Point pos = contact->getPosition();
                    if ( pos == sourcePos )
                        source = *contact;
                    else if ( pos == targetPos )
                        target = *contact;
                    if ( source && target )
                        break;
                }
                if ( !source ) {
                    string message = "Cannot find source contact for ";
                    message += getString(segment);
                    throw Error (message);
                }
                if ( !target ) {
                    string message = "Cannot find target contact for ";
                    message += getString(segment);
                    throw Error (message);
                }
                //UpdateSession::open();
                if ( Horizontal* horiz = dynamic_cast<Horizontal*>(segment) ) {
                    horiz->setDxSource(0);
                    horiz->setDxTarget(0);
                }
                else if ( Vertical* verti = dynamic_cast<Vertical*>(segment) ) {
                    verti->setDySource(0);
                    verti->setDyTarget(0);
                }
                else
                    throw Error ("A segment which is not a Horizontal nor a Vertical !!!");
                segment->getSourceHook()->attach(source->getBodyHook());
                segment->getTargetHook()->attach(target->getBodyHook());
                //UpdateSession::close();
            }
        }
      }
    } catch ( Error& e ) {
      if ( e.what() != "[ERROR] GrParser processed" )
        cerr << e.what() << endl;
    }

    fileStream.close ();
  }

  void printToFile(IspdGui* ispd)
  // ****************************
  {
      float width  = DbU::getLambda(_cell->getAbutmentBox().getWidth());
      float height = DbU::getLambda(_cell->getAbutmentBox().getHeight());
      assert(width);
      assert(height);
      float minSize = 800;
      unsigned wWidth  = (unsigned int)((width < height) ? minSize : floor(width*minSize/height));
      unsigned wHeight = (unsigned int)((width < height) ? floor(height*minSize/width) : minSize);
      //unsigned wWidth = floor(width);
      //unsigned wHeight = floor(height);
      CellWidget* widget = ispd->getCellWidget();
      widget->setFixedSize(wWidth, wHeight);
      widget->fitToContents();
      forEach ( Layer*, layer, DataBase::getDB()->getTechnology()->getLayers() )
          widget->setLayerVisible ( layer->getName(), false );
      widget->setLayerVisible ( Name("Knik::Edges"), true );

      QImage image (wWidth, wHeight+60, QImage::Format_RGB32);
      widget->copyToImage(&image);
      string savePath = _filePath;
      savePath += "_map.png";
      image.save(savePath.c_str(),"png");
  }

} // End of anonymous namespace.




// x-----------------------------------------------------------------x
// |                      Fonctions Definitions                      |
// x-----------------------------------------------------------------x


// -------------------------------------------------------------------
// Function  :  "main()".

int main ( int argc, char *argv[] )
{
  int  returnCode = 0;

  try {
    unsigned int  traceLevel;
    bool          verbose1;
    bool          verbose2;
    bool          coreDump;
    bool          textMode;
    bool          graphicMode;
    bool          knikSimple;
    bool          knikOverflow;
    bool          loadSolution;
    bool          saveSolution;

    poptions::options_description options ("Command line arguments & options");
    options.add_options()
      ( "help,h"        , "Print this help." )
      ( "verbose,v"     , poptions::bool_switch(&verbose1)->default_value(false)
                        , "First level of verbosity.")
      ( "very-verbose,V", poptions::bool_switch(&verbose2)->default_value(false)
                        , "Second level of verbosity.")
      ( "text,t"        , poptions::bool_switch(&textMode)->default_value(false)
                        , "Run in pure text mode.")
      ( "core-dump,D"   , poptions::bool_switch(&coreDump)->default_value(false)
                        , "Enable core dumping.")
      ( "trace-level,l" , poptions::value<unsigned int>(&traceLevel)->default_value(1000)
                        , "Set the level of trace, trace messages with a level superior to "
                          "<arg> will be printed on <stderr>." )
      ( "cell,c"        , poptions::value<string>()
                        , "The name of the cell to load, whithout extension." )
      ( "knik,k"        , poptions::bool_switch(&knikSimple)->default_value(false)
                        , "Perform a simple routing pass.")
      ( "KNIK,K"        , poptions::bool_switch(&knikOverflow)->default_value(false)
                        , "Perform a routing pass, then analyse & re-route overflowed edges.")
      ( "solution,s"    , poptions::bool_switch(&loadSolution)->default_value(true)
                        , "Load a previously generated routing solution.")
      ( "Save,S"        , poptions::bool_switch(&saveSolution)->default_value(false)
                        , "Save the routed design.")
      ( "editor,e"      , poptions::bool_switch(&graphicMode)->default_value(false)
                        , "Launch the graphical editor.");

    poptions::variables_map arguments;
    poptions::store  ( poptions::parse_command_line(argc,argv,options), arguments );
    poptions::notify ( arguments );

    if ( arguments.count("help") ) {
      cout << options << endl;
      exit ( 0 );
    }

    System::get()->setCatchCore ( not coreDump ); 
    if ( verbose1 ) mstream::enable ( mstream::VerboseLevel1 );
    if ( verbose2 ) mstream::enable ( mstream::VerboseLevel2 ); 
    ltracelevel ( traceLevel );
    _createRings = not loadSolution;

    if ( arguments.count("cell") ) {
      _filePath = arguments["cell"].as<string>();
      UpdateSession::open();
      _cell = loadFromFile();
      UpdateSession::close();
      if (!_cell) {
        cerr << "[ERROR] Cell not found: " << arguments["cell"].as<string>() << endl;
        exit ( -45 );
      }
    }

    if ( loadSolution ) {
      _filePath = arguments["cell"].as<string>();
      UpdateSession::open();
      loadSolutionFile();
      UpdateSession::close();
    }

    if ( not textMode ) {
      QApplication* qa = new HApplication ( argc, argv );
      Graphics::enable ();

      IspdGui* ispd = IspdGui::create ();
      cmess1 << ispd->getBanner() << endl;;

      GraphicKnikEngine* grKnik = Knik::GraphicKnikEngine::grab();
      ispd->registerTool ( grKnik );
      //ispd->registerTool ( Katabatic::GraphicKatabaticEngine::grab() );

      ispd->setCell ( _cell );
      ispd->show();
      if ( arguments.count("knik") ) {
        grKnik->route();
        grKnik->analyse();
      }
      else if ( arguments.count("KNIK") ) {
        grKnik->run();
      //bool done = grKnik->analyse();
      //while ( !done ) {
      //    grKnik->unroute();
      //    grKnik->reroute();
      //    done = grKnik->analyse();
      //}
      }
      else if ( saveSolution ) {
      //grKnik->analyse();
        printToFile(ispd);
      }

      if ( not saveSolution or graphicMode )
        returnCode = qa->exec();

      ispd->destroy();
      delete qa;
    } else {
      KnikEngine* knik = Knik::KnikEngine::get(_cell);
      if ( arguments.count("knik") ) {
        knik->Route();
        knik->analyseRouting();
        if ( saveSolution ) knik->saveSolution();
      } else if ( arguments.count("KNIK") ) {
      //knik->Route();
      //bool done = knik->analyseRouting();
      //while ( !done ) {
      //    knik->unrouteOvSegments();
      //    knik->reroute();
      //    done = knik->analyseRouting();
      //}
        knik->run();
        if ( saveSolution )
          knik->saveSolution();
      } else if ( loadSolution ) {
        knik->analyseRouting();
      } else
        cerr << "[ERROR] Using text mode without -k, -K or -s option is useless." << endl;
    }

    _af->destroy();
  }
  catch ( Error& e ) {
    cerr << e.what() << endl;
    exit ( 1 );
  }
  catch ( ... ) {
    cout << "[ERROR] Abnormal termination: unmanaged exception.\n" << endl;
    exit ( 2 );
  }

  return returnCode;
}


