// -*- 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                               |
// |               K n i k - Global Router                           |
// |                                                                 |
// |  Author      :                    Jean-Paul Chaput              |
// |  E-mail      :            Jean-Paul.Chaput@lip6.fr              |
// | =============================================================== |
// |  C++ Header  :       "./GraphicKnikEngine.cpp"                  |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <hurricane/Warning.h>
#include <hurricane/Go.h>
#include <hurricane/Cell.h>
#include <hurricane/viewer/Graphics.h>
#include <hurricane/viewer/CellWidget.h>
#include <hurricane/viewer/CellViewer.h>
#include <crlcore/AllianceFramework.h>
#include <knik/Vertex.h>
#include <knik/Edge.h>
#include <knik/GraphicKnikEngine.h>


namespace Knik {


  using namespace std;

  using Hurricane::Warning;
  using Hurricane::Graphics;
  using CRL::AllianceFramework;
    

// -------------------------------------------------------------------
// Class  :  "CRL::GraphicKnikEngine".


  size_t             GraphicKnikEngine::_references = 0;
  GraphicKnikEngine* GraphicKnikEngine::_singleton  = NULL;


  void GraphicKnikEngine::initKnikEdges ( CellWidget* widget ) 
  {
    widget->getDrawingPlanes().setPen ( Qt::NoPen );
  }


  void GraphicKnikEngine::drawKnikEdges ( CellWidget*           widget
                                        , const Go*             go
                                        , const BasicLayer*     basicLayer
                                        , const Box&            box
                                        , const Transformation& transformation
                                        ) 
  {
    const Edge* edge = dynamic_cast<const Edge*>(go);
    if ( edge ) {
      unsigned int occupancy = 255;
      if ( edge->getRealOccupancy() < edge->getCapacity() )
        occupancy = (unsigned int)( 255.0 * ( (float)edge->getRealOccupancy() / (float)edge->getCapacity() ) );

      QPainter& painter = widget->getPainter();
      if ( edge->getRealOccupancy() > edge->getCapacity() ) {
        QColor color (Qt::cyan);
        painter.setPen (color.darker(widget->getDarkening()));
      }
      painter.setBrush ( Graphics::getColorScale(ColorScale::Fire).getBrush(occupancy,widget->getDarkening()) );
      painter.drawRect ( widget->dbuToDisplayRect(edge->getBoundingBox(), false) );
      painter.setPen(Qt::NoPen);

    // affichage des infos de l'arete
      if ( edge->hasInfo() ) {
        QColor color (Qt::blue);
        painter.setPen (color.darker(widget->getDarkening()));
        QFont font;
        font.setBold(true);
        font.setPointSize(10);
        painter.setFont(font);
      //QString text = QString("%1%2%3").arg(edge->getConnexID()).arg(",").arg(edge->getConstCost());
        QString text = QString("%1").arg(edge->getConnexID());
        Point fromPos = edge->getFrom()->getPosition();
        Point   toPos = edge->getTo()->getPosition();
      //painter.drawText ( widget->dbuToDisplayPoint ( (fromPos.getX()+toPos.getX())/2+DbU::lambda(0.5), (fromPos.getY()+toPos.getY())/2+DbU::lambda(0.5) ), text );

        if ( edge->isVertical() ) {
          painter.save();
          Box bbox = edge->getBoundingBox();
          painter.translate (widget->dbuToDisplayPoint(bbox.getXMin(), bbox.getYMin()));
          painter.rotate(-90);
          painter.drawText (QRect(0,0,widget->dbuToDisplayLength(bbox.getHeight()),widget->dbuToDisplayLength(bbox.getWidth())), text, QTextOption(Qt::AlignCenter));
        //painter.drawText (0, 0, text);
          painter.restore();
        } else
          painter.drawText ( widget->dbuToDisplayRect ( edge->getBoundingBox(),false ), text, QTextOption (Qt::AlignCenter) );
        painter.setPen(Qt::NoPen);
      }
    }
  }


  void GraphicKnikEngine::initKnikVertex ( CellWidget* widget ) 
  {
  //widget->getDrawingPlanes().setPen ( Qt::NoPen );
  //widget->getDrawingPlanes().setBrush ( color.darker(widget->getDarkening()) );
  }


  void GraphicKnikEngine::drawKnikVertex ( CellWidget*           widget
                                         , const Go*             go
                                         , const BasicLayer*     basicLayer
                                         , const Box&            box
                                         , const Transformation& transformation
                                         ) 
  {
    const Vertex* vertex = dynamic_cast<const Vertex*>(go);
    if ( vertex ) {
      QPainter& painter = widget->getPainter();
      QColor color = Qt::darkRed;
      QPen pen (color.darker(widget->getDarkening()));
      pen.setWidth(2);
      widget->setPen(pen);
      painter.drawEllipse ( widget->dbuToDisplayRect ( vertex->getBoundingBox(), false ) );

      if ( vertex->hasInfo() ) {
        QColor color (Qt::blue);
        painter.setPen (color.darker(widget->getDarkening()));
        QFont font;
        font.setBold(true);
        font.setPointSize(10);
        painter.setFont(font);
      //QString text = QString("%1%2%3").arg(edge->getConnexID()).arg(",").arg(edge->getConstCost());
        QString text = QString("%1 / %2").arg(vertex->getConnexID()).arg(vertex->getDistance());
        Point center = vertex->getPosition();
        Box textBox = Box(vertex->getXMin(), vertex->getYMin(), vertex->getXMax(), vertex->getYMax());
        painter.drawText ( widget->dbuToDisplayRect ( textBox,false ), text, QTextOption (Qt::AlignCenter) );
        painter.setPen(Qt::NoPen);
      }
    }
  }


  KnikEngine* GraphicKnikEngine::createEngine ( unsigned int mode ) 
  {
    Cell* cell = getCell ();

    KnikEngine* knik = KnikEngine::get ( cell );
    if ( !knik ) {
      if ( cell->getRubbers().getFirst() == NULL )
        cell->flattenNets ( (mode==BuildSolution) );
      knik = KnikEngine::create ( cell
                                , _congestion
                                , _preCongestion
                                , _benchMode
                                , _useSegments
                                , _edgeCost
                                );
      if ( mode == LoadSolution )
        knik->createRoutingGraph ();
    }
  //else
  //  cerr << Warning("%s already has a Knik engine.",getString(cell).c_str()) << endl;

    return knik;
  }


  void GraphicKnikEngine::flattenNets () 
  {
    Cell* cell = getCell ();

    emit cellPreModificated();

    cell->flattenNets ( (true) );

    emit cellPostModificated();
  }


  void GraphicKnikEngine::run ()
  {
    KnikEngine* knik = createEngine ( BuildSolution );
    if ( !knik ) return;

    emit cellPreModificated ();

    knik->run ();

    emit cellPostModificated ();
  }


  void GraphicKnikEngine::route ()
  {
    KnikEngine* knik = createEngine ( BuildSolution );
    if ( !knik ) return;

    emit cellPreModificated ();

    knik->Route ();

    emit cellPostModificated ();
  }


  bool GraphicKnikEngine::analyse ()
  {
    Cell* cell = getCell ();

    emit cellPreModificated();

    bool        done = false;
    KnikEngine* knik = KnikEngine::get ( cell );
    if ( knik ) {
      done = knik->analyseRouting();
    }

    emit cellPostModificated();
    return done;
  }


  void GraphicKnikEngine::unroute () 
  {
    Cell* cell = getCell ();

    emit cellPreModificated();

    KnikEngine* knik = KnikEngine::get ( cell );
    if ( knik ) knik->unrouteOvSegments();

    emit cellPostModificated();
  }


  void GraphicKnikEngine::reroute () 
  {
    Cell* cell = getCell ();

    emit cellPreModificated();

    KnikEngine* knik = KnikEngine::get ( cell );
    if ( knik ) knik->reroute();

    emit cellPostModificated();
  }


  void GraphicKnikEngine::saveSolution () 
  {
    Cell* cell = getCell ();

    emit cellPreModificated();

    KnikEngine* knik = KnikEngine::get ( cell );
    if ( knik ) knik->saveSolution();

    emit cellPostModificated();
  }


  void GraphicKnikEngine::loadSolution () 
  {
    KnikEngine* knik = createEngine ( LoadSolution );
    if ( !knik ) return;

    emit cellPreModificated();

    knik->loadSolution();

    emit cellPostModificated();
  }


  void GraphicKnikEngine::addToMenu ( CellViewer* viewer )
  {
    assert ( _viewer == NULL );
    _viewer = viewer;

    QMenu* prMenu   = _viewer->findChild<QMenu*>("viewer.menuBar.placeAndRoute");
    QMenu* stepMenu = _viewer->findChild<QMenu*>("viewer.menuBar.placeAndRoute.stepByStep");
    if ( !prMenu ) {
      QMenuBar* menuBar = _viewer->findChild<QMenuBar*>("viewer.menuBar");
      if ( !menuBar ) {
        cerr << Warning("GraphicKnikEngine::addToMenu() - No MenuBar in parent widget.") << endl;
        return;
      }
      prMenu = menuBar->addMenu ( tr("P&&R") );
      prMenu->setObjectName ( "viewer.menuBar.placeAndRoute" );

      stepMenu = prMenu->addMenu ( tr("&Step by Step") );
      stepMenu->setObjectName ( "viewer.menuBar.placeAndRoute.stepByStep" );

      prMenu->addSeparator ();
    }

    QAction* gRunAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.globalRoute");
    if ( gRunAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in.") << endl;
    else {
      gRunAction = new QAction  ( tr("Knik - &Global Route"), _viewer );
      gRunAction->setObjectName ( "viewer.menuBar.placeAndRoute.globalRoute" );
      gRunAction->setStatusTip  ( tr("Run the <b>Knik</b> global router") );
      gRunAction->setVisible    ( true );
      prMenu->addAction ( gRunAction );

      connect ( gRunAction, SIGNAL(triggered()), this, SLOT(run()) );
    }

    QAction* gFlattenAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.flattenNets");
    if ( gFlattenAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in.") << endl;
    else {
      gFlattenAction = new QAction  ( tr("Knik - &Prepare nets"), _viewer );
      gFlattenAction->setObjectName ( "viewer.menuBar.placeAndRoute.flattenNets" );
      gFlattenAction->setStatusTip  ( tr("Prepare nets for routing") );
      gFlattenAction->setVisible    ( true );
      stepMenu->addAction ( gFlattenAction );

      connect ( gFlattenAction, SIGNAL(triggered()), this, SLOT(flattenNets()) );
    }

    QAction* gRouteAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.simpleGlobalRoute");
    if ( gRouteAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in.") << endl;
    else {
      gRouteAction = new QAction  ( tr("Knik - &Simple Global Route (no ripup)"), _viewer );
      gRouteAction->setObjectName ( "viewer.menuBar.placeAndRoute.simpleGlobalRoute" );
      gRouteAction->setStatusTip  ( tr("Run the <b>Knik</b> global router") );
      gRouteAction->setVisible    ( true );
      stepMenu->addAction ( gRouteAction );

      connect ( gRouteAction, SIGNAL(triggered()), this, SLOT(route()) );
    }

    QAction* gAnalyseAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.analyse");
    if ( gAnalyseAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in (analyse function).") << endl;
    else {
      gAnalyseAction = new QAction  ( tr("Knik - &Analyse routing"), _viewer );
      gAnalyseAction->setObjectName ( "viewer.menuBar.placeAndRoute.analyse" );
      gAnalyseAction->setStatusTip  ( tr("Analyse routing done by <b>Knik</b>") );
      gAnalyseAction->setVisible    ( true );
      stepMenu->addAction ( gAnalyseAction );

      connect ( gAnalyseAction, SIGNAL(triggered()), this, SLOT(analyse()) );
    }

    QAction* gUnrouteAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.unroute");
    if ( gUnrouteAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in (unroute function).") << endl;
    else {
      gUnrouteAction = new QAction  ( tr("Knik - &Unroute overflowed segments"), _viewer );
      gUnrouteAction->setObjectName ( "viewer.menuBar.placeAndRoute.unroute" );
      gUnrouteAction->setStatusTip  ( tr("Unroute overflowed segments") );
      gUnrouteAction->setVisible    ( true );
      stepMenu->addAction ( gUnrouteAction );

      connect ( gUnrouteAction, SIGNAL(triggered()), this, SLOT(unroute()) );
    }

    QAction* gRerouteAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.reroute");
    if ( gRerouteAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in (reroute function).") << endl;
    else {
      gRerouteAction = new QAction  ( tr("Knik - &Reroute"), _viewer );
      gRerouteAction->setObjectName ( "viewer.menuBar.placeAndRoute.reroute" );
      gRerouteAction->setStatusTip  ( tr("Reroute") );
      gRerouteAction->setVisible    ( true );
      stepMenu->addAction ( gRerouteAction );

      connect ( gRerouteAction, SIGNAL(triggered()), this, SLOT(reroute()) );
    }

    QAction* gSaveAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.saveSolution");
    if ( gSaveAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in (save function).") << endl;
    else {
      gSaveAction = new QAction  ( tr("Knik - &Save global routing"), _viewer );
      gSaveAction->setObjectName ( "viewer.menuBar.placeAndRoute.saveSolution" );
      gSaveAction->setStatusTip  ( tr("Save global routing (.kgr file)") );
      gSaveAction->setVisible    ( true );
      stepMenu->addAction ( gSaveAction );

      connect ( gSaveAction, SIGNAL(triggered()), this, SLOT(saveSolution()) );
    }

    QAction* gLoadAction = _viewer->findChild<QAction*>("viewer.menuBar.placeAndRoute.loadSolution");
    if ( gLoadAction )
      cerr << Warning("GraphicKnikEngine::addToMenu() - Knik global router already hooked in (load function).") << endl;
    else {
      gLoadAction = new QAction  ( tr("Knik - &Load global routing"), _viewer );
      gLoadAction->setObjectName ( "viewer.menuBar.placeAndRoute.loadSolution" );
      gLoadAction->setStatusTip  ( tr("Load global routing (.kgr file)") );
      gLoadAction->setVisible    ( true );
      stepMenu->addAction ( gLoadAction );

      connect ( gLoadAction, SIGNAL(triggered()), this, SLOT(loadSolution()) );
    }
    // fin du sous menu

    connect ( this, SIGNAL(cellPreModificated ()), _viewer->getCellWidget(), SLOT(cellPreModificate ()) );
    connect ( this, SIGNAL(cellPostModificated()), _viewer->getCellWidget(), SLOT(cellPostModificate()) );
  }


  const Name& GraphicKnikEngine::getName () const
  { return KnikEngine::staticGetName (); }


  Cell* GraphicKnikEngine::getCell ()
  {
    if ( !_viewer ) {
      throw Error ( "<b>Knik:</b> GraphicKnikEngine not bound to any Viewer." );
      return NULL;
    }

    if ( !_viewer->getCell() ) {
      throw Error ( "<b>Knik:</b> No Cell is loaded into the Viewer." );
      return NULL;
    }

    return _viewer->getCell();
  }


  GraphicKnikEngine* GraphicKnikEngine::grab ()
  {
    if ( !_references ) 
      _singleton = new GraphicKnikEngine ();
    _references++;

    return _singleton;
  }


  size_t GraphicKnikEngine::release ()
  {
    _references--;
    if ( !_references ) {
      delete _singleton;
      _singleton = NULL;
    }
    return _references;
  }


  GraphicKnikEngine::GraphicKnikEngine ()
    : GraphicTool   ()
    , _viewer       (NULL)
    , _congestion   (1)
    , _preCongestion(2)
    , _benchMode    (false)
    , _useSegments  (true)
    , _edgeCost     (2.5)
  {
    addDrawGo ( "Knik::Edges",  initKnikEdges,  drawKnikEdges );
    addDrawGo ( "Knik::Vertex", initKnikVertex, drawKnikVertex );
  }


  GraphicKnikEngine::~GraphicKnikEngine()
  { }


}  // End of Knik namespace.