
// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC/LIP6 2008-2010, All Rights Reserved
//
// ===================================================================
//
// $Id$
//
// x-----------------------------------------------------------------x
// |                                                                 |
// |                   C O R I O L I S                               |
// |      K i t e  -  D e t a i l e d   R o u t e r                  |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./KiteEngine.cpp"                         |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include  <sstream>
#include  <fstream>
#include  <iomanip>

#include  "hurricane/Bug.h"
#include  "hurricane/Error.h"
#include  "hurricane/Warning.h"
#include  "hurricane/Layer.h"
#include  "hurricane/Net.h"
#include  "hurricane/Pad.h"
#include  "hurricane/Plug.h"
#include  "hurricane/Cell.h"
#include  "hurricane/Instance.h"
#include  "hurricane/Vertical.h"
#include  "hurricane/Horizontal.h"
#include  "hurricane/UpdateSession.h"

#include  "crlcore/Measures.h"
#include  "knik/KnikEngine.h"
#include  "katabatic/AutoContact.h"
#include  "kite/DataNegociate.h"
#include  "kite/GCellGrid.h"
#include  "kite/RoutingPlane.h"
#include  "kite/Session.h"
#include  "kite/GCellRoutingSet.h"
#include  "kite/NegociateWindow.h"
#include  "kite/KiteEngine.h"


namespace Kite {


  using std::cout;
  using std::cerr;
  using std::endl;
  using std::setw;
  using std::left;
  using std::ofstream;
  using std::ostringstream;
  using std::setprecision;
  using Hurricane::tab;
  using Hurricane::inltrace;
  using Hurricane::ltracein;
  using Hurricane::ltraceout;
  using Hurricane::ForEachIterator;
  using Hurricane::Bug;
  using Hurricane::Error;
  using Hurricane::Warning;
  using Hurricane::Layer;
  using Hurricane::Cell;
  using CRL::addMeasure;
  using CRL::Measures;
  using CRL::MeasuresSet;
  using Knik::KnikEngine;


  const char* missingRW =
    "%s :\n\n"
    "    Cell %s do not have any KiteEngine (or not yet created).\n";


// -------------------------------------------------------------------
// Class  :  "Kite::KiteEngine".


  Name  KiteEngine::_toolName = "Kite";


  const Name& KiteEngine::staticGetName ()
  { return _toolName; }


  KiteEngine* KiteEngine::get ( const Cell* cell )
  {
    return static_cast<KiteEngine*>(ToolEngine::get(cell,staticGetName()));
  }


  KiteEngine::KiteEngine ( Cell* cell )
    : KatabaticEngine  (cell)
    , _knik            (NULL)
    , _obstacleNet     (NULL)
    , _configuration   (new Configuration(getKatabaticConfiguration()))
    , _routingPlanes   ()
    , _kiteGrid        (NULL)
    , _negociateWindow (NULL)
    , _trackSegmentLut ()
    , _minimumWL       (0.0)
    , _toolSuccess     (false)
  {
  //_configuration->setAllowedDepth ( 3 );
  }


  void  KiteEngine::_postCreate ()
  {
    KatabaticEngine::_postCreate ();

#ifdef KNIK_NOT_EMBEDDED
    size_t maxDepth = getRoutingGauge()->getDepth();

    _kiteGrid  = GCellGrid::create ( this );

    _routingPlanes.reserve ( maxDepth );
    for ( size_t depth=0 ; depth < maxDepth ; depth++ ) {
      _routingPlanes.push_back ( RoutingPlane::create ( this, depth ) );
    }
#endif
  }


  KiteEngine* KiteEngine::create ( Cell* cell )
  {
    KiteEngine* kite = new KiteEngine ( cell );

    kite->_postCreate ();
    return kite;
  }


  void  KiteEngine::_preDestroy ()
  {
    ltrace(90) << "KiteEngine::_preDestroy ()" << endl;
    ltracein(90);

    cmess1 << "  o  Deleting ToolEngine<" << getName() << "> from Cell <"
           << _cell->getName() << ">" << endl;

    if ( getState() < Katabatic::StateGutted )
      setState ( Katabatic::StatePreDestroying );

    _gutKite ();
    KatabaticEngine::_preDestroy ();

    cmess2 << "     - RoutingEvents := " << RoutingEvent::getAllocateds() << endl;

  //_knik->destroy ();

    ltraceout(90);
  }


  KiteEngine::~KiteEngine ()
  {
    delete _configuration;
  }


  const Name& KiteEngine::getName () const
  { return _toolName; }


  Configuration* KiteEngine::getConfiguration ()
  { return _configuration; }


  unsigned int  KiteEngine::getRipupLimit ( const TrackElement* segment ) const
  {
    if ( segment->isBlockage() ) return 0;

    if ( segment->getDataNegociate() ) {
      if ( segment->getDataNegociate()->isBorder() )
        return _configuration->getRipupLimit(Configuration::BorderRipupLimit);

      if ( segment->getDataNegociate()->isRing() )
        return _configuration->getRipupLimit(Configuration::GlobalRipupLimit);
    }

    if ( segment->isStrap () ) return _configuration->getRipupLimit(Configuration::StrapRipupLimit);
    if ( segment->isGlobal() ) {
      vector<GCell*> gcells;
      segment->getGCells(gcells);
      if ( gcells.size() > 2 )
        return _configuration->getRipupLimit(Configuration::LongGlobalRipupLimit);
      return _configuration->getRipupLimit(Configuration::GlobalRipupLimit);
    }
    return _configuration->getRipupLimit(Configuration::LocalRipupLimit);
  }


  RoutingPlane* KiteEngine::getRoutingPlaneByIndex ( size_t index ) const
  {
    if ( index >= getRoutingPlanesSize() ) return NULL;

    return _routingPlanes[index];
  }


  RoutingPlane* KiteEngine::getRoutingPlaneByLayer ( const Layer* layer ) const
  {
    for ( size_t index=0 ; index < getRoutingPlanesSize() ; index++ ) {
      if ( _routingPlanes[index]->getLayer() == layer )
        return _routingPlanes[index];
    }
    return NULL;
  }


  Track* KiteEngine::getTrackByPosition ( const Layer* layer, DbU::Unit axis, unsigned int mode ) const
  {
    RoutingPlane* plane = getRoutingPlaneByLayer ( layer );
    if ( !plane ) return NULL;

    return plane->getTrackByPosition ( axis, mode );
  }


  void  KiteEngine::setInterrupt ( bool state )
  {
    if ( _negociateWindow ) {
      _negociateWindow->setInterrupt ( state ); 
      cerr << "Interrupt [CRTL+C] of " << this << endl;
    }
  }


  void  KiteEngine::createGlobalGraph ( unsigned int mode )
  {
    Cell* cell = getCell();
    if ( not _knik ) {
      if ( cell->getRubbers().getFirst() == NULL )
        cell->flattenNets ( (mode==BuildGlobalSolution) );
      KnikEngine::setEdgeCapacityPercent ( getEdgeCapacityPercent() );
      _knik = KnikEngine::create ( cell
                                 , 1     // _congestion
                                 , 2     // _preCongestion
                                 , false // _benchMode
                                 , true  // _useSegments
                                 , 2.5   // _edgeCost
                                 );
    //if ( mode == LoadGlobalSolution )
      _knik->createRoutingGraph ();
    }
  }


  void  KiteEngine::createDetailedGrid ()
  {
    KatabaticEngine::createDetailedGrid ();
    _kiteGrid  = GCellGrid::create ( this );

    size_t maxDepth = getRoutingGauge()->getDepth();

    _routingPlanes.reserve ( maxDepth );
    for ( size_t depth=0 ; depth < maxDepth ; depth++ ) {
      _routingPlanes.push_back ( RoutingPlane::create ( this, depth ) );
    }
  }


  void  KiteEngine::saveGlobalSolution ()
  {
    if ( getState() < Katabatic::StateGlobalLoaded )
      throw Error ("KiteEngine::saveGlobalSolution() : global routing not present yet.");

    if ( getState() > Katabatic::StateGlobalLoaded )
      throw Error ("KiteEngine::saveGlobalSolution() : cannot save after detailed routing.");

    _knik->saveSolution ();
  }


  void  KiteEngine::annotateGlobalGraph ()
  {
    cmess1 << "  o  Back annotate global routing graph." << endl;

    for ( size_t depth=0 ; depth<_routingPlanes.size() ; ++depth ) {
      RoutingPlane* rp = _routingPlanes[depth];
      if ( rp->getLayerGauge()->getType() == Constant::PinOnly ) continue;

      size_t tracksSize = rp->getTracksSize();
      for ( size_t itrack=0 ; itrack<tracksSize ; ++itrack ) {
        Track*        track   = rp->getTrackByIndex ( itrack );
        TrackElement* element = track->getSegment ( (size_t)0 );

        if ( element == NULL ) continue;
        if ( element->getNet() == NULL ) continue;
        if ( not element->getNet()->isSupply() ) continue;

        cinfo << "Capacity from: " << element << endl;

        if ( track->getDirection() == Constant::Horizontal ) {
          for ( ; element != NULL ; element = element->getNext() ) {
            GCell* gcell = _kiteGrid->getGCell ( Point(element->getSourceU(),track->getAxis()) );
            GCell* end   = _kiteGrid->getGCell ( Point(element->getTargetU(),track->getAxis()) );
            GCell* right = NULL;
            if ( not gcell ) {
              cerr << Warning("annotageGlobalGraph(): TrackElement outside GCell grid.") << endl;
              continue;
            }
            while ( gcell and (gcell != end) ) {
              right = gcell->getRight();
              if ( right == NULL ) break;

              _knik->increaseEdgeCapacity ( gcell->getColumn()
                                          , gcell->getRow()
                                          , right->getColumn()
                                          , right->getRow()
                                          , -1 );
              gcell = right;
            }
          }
        } else {
          for ( ; element != NULL ; element = element->getNext() ) {
            GCell* gcell = _kiteGrid->getGCell ( Point(track->getAxis(),element->getSourceU()) );
            GCell* end   = _kiteGrid->getGCell ( Point(track->getAxis(),element->getTargetU()) );
            GCell* up    = NULL;
            if ( not gcell ) {
              cerr << Warning("annotageGlobalGraph(): TrackElement outside GCell grid.") << endl;
              continue;
            }
            while ( gcell and (gcell != end) ) {
              up = gcell->getUp();
              if ( up == NULL ) break;

              _knik->increaseEdgeCapacity ( gcell->getColumn()
                                          , gcell->getRow()
                                          , up->getColumn()
                                          , up->getRow()
                                          , -1 );

              gcell = up;
            }
          }
        }
      }
    }
  }


  void  KiteEngine::runGlobalRouter ( unsigned int mode )
  {
    if ( getState() >= Katabatic::StateGlobalLoaded )
      throw Error ("KiteEngine::runGlobalRouter() : global routing already done or loaded.");

    Session::open ( this );

    createGlobalGraph ( mode );
    createDetailedGrid ();
    buildBlockages ();
    buildPowerRails ();

    if ( mode == LoadGlobalSolution ) {
      _knik->loadSolution ();
    } else {
      annotateGlobalGraph ();
      _knik->run ();
    }

    setState ( Katabatic::StateGlobalLoaded );

    Session::close ();
  }

  
  void  KiteEngine::loadGlobalRouting ( unsigned int method, KatabaticEngine::NetSet& nets )
  {
    KatabaticEngine::loadGlobalRouting ( method, nets );

    Session::open ( this );
    getGCellGrid()->checkEdgeSaturation ( getEdgeCapacityPercent() );
    Session::close ();
  }


  void  KiteEngine::runNegociate ( unsigned int slowMotion )
  {
    if ( _negociateWindow ) return;

    unsigned int  rows    = _kiteGrid->getRows();
    unsigned int  columns = _kiteGrid->getColumns();

    startMeasures ();

    Session::open ( this );

    cmess1 << "  o  Running Negociate Algorithm" << endl;

    _negociateWindow = NegociateWindow::create ( this, 0, 0, columns, rows );
    _negociateWindow->loadRouting ();
    preProcess ();
    _computeCagedConstraints ();
    _negociateWindow->run ( slowMotion );
    _negociateWindow->printStatistics ();
    _negociateWindow->destroy ();
    _negociateWindow = NULL;

    Session::close ();
  //if ( _editor ) _editor->refresh ();

    stopMeasures ();
    printMeasures ( "algo" );
    printCompletion ();

    Session::open ( this );
    unsigned int overlaps     = 0;
    float        edgeCapacity = 1.0;
    KnikEngine* knik          = KnikEngine::get ( getCell() );

    if ( knik )
      edgeCapacity = knik->getEdgeCapacityPercent();

    cmess2 << "  o  Post-checking Knik capacity overload " << (edgeCapacity*100.0) << "%." << endl;

    _kiteGrid->base()->checkEdgeSaturation ( edgeCapacity );
    _check ( overlaps );
    Session::close ();

    _toolSuccess = _toolSuccess and (overlaps == 0);
  }


  void  KiteEngine::printCompletion () const
  {
    cout << "  o  Computing Completion ratios." << endl;
    cout << "     - Unrouted segments :" << endl;

    size_t             routeds          = 0;
    size_t             unrouteds        = 0;
    unsigned long long totalWireLength  = 0;
    unsigned long long routedWireLength = 0;

    TrackElementLut::const_iterator ilut = _trackSegmentLut.begin();
    for ( ; ilut != _trackSegmentLut.end() ; ilut++ ) {
      unsigned long long wl = (unsigned long long)DbU::getLambda(ilut->second->getLength());
      if ( wl > 100000 ) {
        cerr << Error("KiteEngine::printCompletion(): Suspiciously long wire: %llu for %p:%s"
                     ,wl,ilut->first,getString(ilut->second).c_str()) << endl;
        continue;
      }
      totalWireLength += wl;
      if ( ilut->second->getTrack() != NULL ) {
        routeds++;
        routedWireLength += wl;
      } else {
        cout << "   " << setw(4) << ++unrouteds << "| " << ilut->second << endl;
      }
    }

    float segmentRatio    = (float)(routeds) / (float)(_trackSegmentLut.size())  * 100.0;
    float wireLengthRatio = (float)(routedWireLength) / (float)(totalWireLength) * 100.0;

    cout << "     - Track Segment Completion Ratio := "
         << setprecision(4) << segmentRatio
         << "% [" << routeds << "/" << _trackSegmentLut.size() << "] "
         << (_trackSegmentLut.size() - routeds) << " remains." << endl;
    cout << "     - Wire Length Completion Ratio   := "
         << setprecision(4) << wireLengthRatio
         << "% [" << totalWireLength << "] "
         << (totalWireLength - routedWireLength) << " remains." << endl;

    if ( _minimumWL != 0.0 ) {
      float expandRatio = totalWireLength / _minimumWL;
      cout << "     - Wire Length Expand Ratio       := "
           << setprecision(4) << expandRatio
           << "% [min:" << setprecision(9) << _minimumWL << "] "
           << endl;
    }

    _toolSuccess = (unrouteds == 0);

    addMeasure<size_t> ( getCell(), "Segs", routeds+unrouteds );
  }


  void  KiteEngine::dumpMeasures ( ostream& out ) const
  {
    vector<Name> measuresLabels;
    measuresLabels.push_back ( "Gates"   );
    measuresLabels.push_back ( "GCells"  );
    measuresLabels.push_back ( "knikT"   );
    measuresLabels.push_back ( "knikS"   );
    measuresLabels.push_back ( "GWL(l)"  );
    measuresLabels.push_back ( "Area(l2)");
    measuresLabels.push_back ( "Sat."    );
    measuresLabels.push_back ( "loadT"   );
    measuresLabels.push_back ( "loadS"   );
    measuresLabels.push_back ( "Globals" );
    measuresLabels.push_back ( "Edges"   );
    measuresLabels.push_back ( "assignT" );
    measuresLabels.push_back ( "algoT"   );
    measuresLabels.push_back ( "algoS"   );
    measuresLabels.push_back ( "finT"    );
    measuresLabels.push_back ( "Segs"    );

    const MeasuresSet* measures = Measures::get(getCell());

    out << "#" << endl;
    out << "# " << getCell()->getName() << endl;
    out << measures->toStringHeaders(measuresLabels) << endl;
    out << measures->toStringDatas  (measuresLabels) << endl;
  }


  void  KiteEngine::dumpMeasures () const
  {
    ostringstream path;
    path << getCell()->getName() << ".knik-kite.dat";

    ofstream sfile ( path.str().c_str() );
    dumpMeasures ( sfile );
    sfile.close ();
  }


  bool  KiteEngine::_check ( unsigned int& overlap, const char* message ) const
  {
    cmess1 << "  o  Checking Kite Database coherency." << endl;

    bool coherency = true;
    coherency = coherency && KatabaticEngine::_check ( message );
    for ( size_t i=0 ; i<_routingPlanes.size() ; i++ )
      coherency = _routingPlanes[i]->_check(overlap) && coherency;

    Katabatic::Session* ktbtSession = Session::base ();
    forEach ( Net*, inet, getCell()->getNets() ) {
      forEach ( Segment*, isegment, inet->getComponents().getSubSet<Segment*>() ) {
        AutoSegment* autoSegment = ktbtSession->lookup ( *isegment );
        if ( !autoSegment ) continue;
        if ( !autoSegment->isCanonical() ) continue;

        TrackElement* trackSegment = Session::lookup ( *isegment );
        if ( !trackSegment ) {
          coherency = false;
          cerr << Bug("%p %s whithout Track Segment"
                     ,autoSegment
                     ,getString(autoSegment).c_str()
                     ) << endl;
        } else
          trackSegment->_check ();
      }
    }

#if defined(CHECK_DATABASE)
  //Session::getKiteEngine()->setInterrupt ( !coherency );
#endif

    return coherency;
  }


  void  KiteEngine::finalizeLayout ()
  {
    ltrace(90) << "KiteEngine::finalizeLayout()" << endl;
    if ( getState() > Katabatic::StateDriving ) return;

    ltracein(90);

    setState ( Katabatic::StateDriving );
    _gutKite ();

    KatabaticEngine::finalizeLayout ();
    ltrace(90) << "State: " << getState() << endl;

    ltraceout(90);
  }


  void  KiteEngine::_gutKite ()
  {
    ltrace(90) << "KiteEngine::_gutKite()" << endl;
    ltracein(90);
    ltrace(90) << "State: " << getState() << endl;

    if ( getState() < Katabatic::StateGutted ) {
      Session::open ( this );

      size_t maxDepth = getRoutingGauge()->getDepth();
      for ( size_t depth=0 ; depth < maxDepth ; depth++ ) {
        _routingPlanes[depth]->destroy ();
      }

      _kiteGrid->destroy ();
      _kiteGrid = NULL;
      Session::close ();
    }

    ltraceout(90);
  }


  TrackElement* KiteEngine::_lookup ( Segment* segment ) const
  {
    TrackElementLut::const_iterator it = _trackSegmentLut.find ( segment );
    if ( it == _trackSegmentLut.end() ) {
      AutoSegment* autoSegment = KatabaticEngine::_lookup ( segment );
      if ( not autoSegment or autoSegment->isCanonical() ) return NULL;

      Interval dummy;
      autoSegment = autoSegment->getCanonical ( dummy );
      it = _trackSegmentLut.find ( autoSegment->base() );
      
      if ( it == _trackSegmentLut.end() ) return NULL;
    }
    return (*it).second;
  }


  void  KiteEngine::_link ( TrackElement* trackSegment )
  {
    if ( getState() > Katabatic::StateActive ) return;

    if ( !trackSegment ) {
      cerr << Bug("KiteEngine::_link(): Rejecting NULL TrackElement.") << endl;
      return;
    }

    _trackSegmentLut [ trackSegment->base()->base() ] = trackSegment;
  // Not needed: Canonical search is done before lookup.
//     forEach ( AutoSegment*, isegment, trackSegment->base()->getCollapseds() ) {
//       _trackSegmentLut [ isegment->base() ] = trackSegment;
//     }
  }


  void  KiteEngine::_unlink ( TrackElement* trackSegment )
  {
    if ( getState() > Katabatic::StateActive ) return;

    TrackElementLut::iterator it = _trackSegmentLut.find ( trackSegment->base()->base() );
    if ( it != _trackSegmentLut.end() )
      _trackSegmentLut.erase ( it );

  // Not needed: Canonical search is done before lookup.
//     forEach ( AutoSegment*, isegment, trackSegment->base()->getCollapseds() ) {
//       TrackElementLut::iterator it = _trackSegmentLut.find ( isegment->base() );
//       if ( it != _trackSegmentLut.end() )
//         _trackSegmentLut.erase ( it );
//     }
  }


  void  KiteEngine::_check ( Net* net ) const
  {
    cerr << "     o  Checking " << net << endl;

    forEach ( Segment*, isegment, net->getComponents().getSubSet<Segment*>() ) {
      TrackElement* trackSegment = _lookup ( *isegment );
      if ( trackSegment ) {
        trackSegment->_check ();

        AutoContact* autoContact = trackSegment->base()->getAutoSource();
        if ( autoContact ) autoContact->checkTopology ();

        autoContact = trackSegment->base()->getAutoTarget();
        if ( autoContact ) autoContact->checkTopology ();
      }
    }
  }


  string  KiteEngine::_getTypeName () const
  { return "Kite::KiteEngine"; }


  string  KiteEngine::_getString () const
  {
    ostringstream  os;

    os << "<" << "KiteEngine "
       << _cell->getName () << " "
    // << getString(_rg->getName())
       << ">";

    return os.str();
  }


  Record* KiteEngine::_getRecord () const
  {
    Record* record = KatabaticEngine::_getRecord ();
                                     
    record->add ( getSlot ( "_routingPlanes", &_routingPlanes ) );
    return record;
  }


} // End of Kite namespace.
