
// -*- 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 a t a b a t i c  -  Routing Toolbox                    |
// |                                                                 |
// |  Author      :                    Jean-Paul CHAPUT              |
// |  E-mail      :       Jean-Paul.Chaput@asim.lip6.fr              |
// | =============================================================== |
// |  C++ Module  :       "./Session.cpp"                            |
// | *************************************************************** |
// |  U p d a t e s                                                  |
// |                                                                 |
// x-----------------------------------------------------------------x


#include  <cstdlib>
#include  <sstream>

#include  "hurricane/Error.h"
#include  "hurricane/Horizontal.h"
#include  "hurricane/Vertical.h"
#include  "hurricane/Cell.h"
#include  "hurricane/UpdateSession.h"
#include  "crlcore/RoutingGauge.h"
#include  "katabatic/Session.h"
#include  "katabatic/AutoContact.h"
#include  "katabatic/AutoSegment.h"
#include  "katabatic/AutoSegment.h"
#include  "katabatic/GCellGrid.h"
#include  "katabatic/KatabaticEngine.h"


namespace {


  const char* reopenSession =
    "Session::open() :\n\n"
    "    Session already open for %s (internal error).";

  const char* openSessionError =
    "%s :\n\n"
    "    Session has not been opened (internal error).";


} // End of local namespace.




namespace Katabatic {


  using namespace std;
  using Hurricane::tab;
  using Hurricane::ltracein;
  using Hurricane::ltraceout;
  using Hurricane::inltrace;
  using Hurricane::Error;
  using Hurricane::ForEachIterator;
  using Hurricane::UpdateSession;
  using Hurricane::Horizontal;
  using Hurricane::Vertical;
  using Hurricane::Cell;


// -------------------------------------------------------------------
// Class  :  "Katabatic::Session".


  Session*  Session::_session = NULL;


  Session* Session::get ( const char* message )
  {
    if ( not _session and message )
      throw Error ( openSessionError, message );
    return _session;
  }


  Session::Session ( KatabaticEngine* ktbt )
    : _katabatic       (ktbt)
    , _technology      (ktbt->getRoutingGauge()->getTechnology())
    , _routingGauge    (ktbt->getRoutingGauge())
    , _autoContacts    ()
    , _autoSegments    ()
    , _revalidateds    ()
    , _dogLegs         ()
    , _netInvalidateds ()
    , _netRevalidateds ()
    , _invalidateMask  (0)
  { }


  void  Session::_postCreate ()
  {
    UpdateSession::open ();
    _session = this;
  }


  Session::~Session ()
  { }


  size_t  Session::_preDestroy ()
  {
    size_t count = 0;
    if ( _katabatic->getState() <= StateActive ) {
      _revalidate ();

      if ( _katabatic->getGCellGrid() )
        _katabatic->getGCellGrid()->updateDensity ();
    }
    UpdateSession::close();

    return count;
  }


  bool           Session::_doDestroyBaseContact () { return _katabatic->doDestroyBaseContact(); }
  bool           Session::_doDestroyBaseSegment () { return _katabatic->doDestroyBaseSegment(); }
  bool           Session::_doDestroyTool        () { return _katabatic->doDestroyTool(); }
  Configuration* Session::_getConfiguration     () { return _katabatic->getConfiguration(); }


  void  Session::_splitContacts ()
  {
    ltrace(110) << "Katabatic::Session::_splitContacts()" << endl;
    ltracein(110);

    for ( size_t i=0; i<_autoContacts.size() ; i++ )
      _autoContacts[i]->split ();

    ltraceout(110);
  }


  void  Session::_restoreVCon ()
  {
    ltrace(110) << "Katabatic::Session::_restoreVCon()" << endl;
    ltracein(110);

    for ( size_t i=0; i<_autoContacts.size() ; i++ ) {
      DbU::Unit y = DbU::Max;
      forEach ( Horizontal*, isegment, _autoContacts[i]->getSlaveComponents().getSubSet<Horizontal*>() ) {
        y = isegment->getY();
      }
      _autoContacts[i]->restoreVConnexity ( y, true );
    }

    ltraceout(110);
  }


  void  Session::_restoreHCon ()
  {
    ltrace(110) << "Katabatic::Session::_restoreHCon()" << endl;
    ltracein(110);

    for ( size_t i=0; i<_autoContacts.size() ; i++ ) {
      DbU::Unit x = DbU::Max;
      forEach ( Vertical*, isegment, _autoContacts[i]->getSlaveComponents().getSubSet<Vertical*>() ) {
        x = isegment->getX();
      }
      _autoContacts[i]->restoreHConnexity ( x, true );
    }

    ltraceout(110);
  }


  void  Session::_canonize ()
  {
    ltrace(110) << "Katabatic::Session::_canonize()" << endl;
    ltracein(110);

    set<Segment*>        exploredSegments;
    vector<AutoSegment*> aligneds;

    sort ( _autoSegments.begin(), _autoSegments.end(), AutoSegment::CompareCanonical() );

    for ( size_t i=0 ; i<_autoSegments.size() ; i++ ) {
      AutoSegment* seedSegment = _autoSegments[i];

      if ( exploredSegments.find(seedSegment->getSegment()) == exploredSegments.end() ) {
        ltrace(110) << "New chunk from: " << seedSegment << endl;
        aligneds.push_back ( seedSegment );

        bool isCanonicalLocal = seedSegment->isLocal();
        forEach ( AutoSegment*, collapsed, seedSegment->getCollapseds() ) {
          ltrace(110) << "Aligned: " << *collapsed << endl;
          aligneds.push_back ( *collapsed );
          exploredSegments.insert ( collapsed->getSegment() );

          if ( collapsed->isGlobal() ) isCanonicalLocal = false;
        }

        ltracein(110);
        sort ( aligneds.begin(), aligneds.end(), AutoSegment::CompareCanonical() );

        if ( aligneds.size() > 1 ) {
          if ( not AutoSegment::CompareCanonical() ( aligneds[0], aligneds[1] ) ) {
            cerr << "Ambiguous canonization: " << aligneds[0]->base() << endl;
            cerr << "Ambiguous canonization: " << aligneds[1]->base() << endl;
          }
        }

        aligneds[0]->setCanonical      ( true );
        aligneds[0]->setCanonicalLocal ( isCanonicalLocal );
        ltrace(110) << "Canonical: " << aligneds[0] << endl;

        for ( size_t j=1 ; j<aligneds.size() ; j++ ) {
          aligneds[j]->setCanonical ( false );
          ltrace(110) << "Secondary: " << aligneds[j] << endl;
        }

        ltrace(159) << "Align @" << DbU::getLambda(aligneds[0]->getAxis())
                    << " on " << aligneds[0] << endl;

        aligneds[0]->setAxis ( aligneds[0]->getAxis(), Realignate );
        aligneds.clear ();
        ltraceout(110);
      }
    }

    ltraceout(110);
  }


  void  Session::_revalidateTopology ()
  {
    ltrace(110) << "Katabatic::Session::_revalidateTopology()" << endl;
    ltracein(110);

    set<AutoSegment*> faileds;

    if ( not _netInvalidateds.empty() ) {
      set<Net*>::iterator inet = _netInvalidateds.begin();

      if ( _invalidateMask & NetSplitContacts ) {
        _splitContacts ();
        _invalidateMask &= ~NetSplitContacts;
      }

      if ( _invalidateMask & RestoreVCon ) {
        _restoreVCon ();
        _invalidateMask &= ~RestoreVCon;
      }

      if ( _invalidateMask & RestoreHCon ) {
        _restoreVCon ();
        _invalidateMask &= ~RestoreHCon;
      }

      if ( _invalidateMask & NetCanonize ) {
        for ( ; inet != _netInvalidateds.end() ; inet++ ) {
          ltrace(110) << "Katabatic::Session::_revalidateTopology(Net*)" << *inet << endl;
          _katabatic->_computeNetConstraints ( *inet, faileds );
          _katabatic->_computeNetOptimals    ( *inet );
          _katabatic->_computeNetTerminals   ( *inet );
        }
        _canonize ();

        for ( size_t i=0 ; i<_autoSegments.size() ; ++i ) {
          if (   _autoSegments[i]->isUnsetAxis()
             and _autoSegments[i]->isCanonical() ) {
            _autoSegments[i]->toOptimalAxis ();
          }
        }
        _invalidateMask &= ~NetCanonize;
      }
    }

    ltraceout(110);
  }


  size_t  Session::_revalidate ()
  {
    ltrace(110) << "Katabatic::Session::revalidate()" << endl;
    ltracein(110);

    size_t count = 0;

    _revalidateTopology ();
    _netRevalidateds.clear ();
    _netRevalidateds.swap ( _netInvalidateds );

    ltrace(110) << "AutoContacts Revalidate (after canonize)." << endl;
    for ( size_t i=0 ; i < _autoContacts.size() ; i++, count++ )
      _autoContacts[i]->revalidate ();
    _autoContacts.clear ();

    ltrace(110) << "AutoSegments Revalidate (after canonize)." << endl;
    for ( size_t i=0 ; i < _autoSegments.size() ; i++, count++ )
      _autoSegments[i]->revalidate ();
    _revalidateds.clear ();
    _autoSegments.swap  ( _revalidateds );
    _dogLegs.clear ();

    ltrace(110) << "AutoSegments/AutoContacts queued deletion." << endl;
    bool destroySegment = _katabatic->setDestroyBaseSegment ( true );
    bool destroyContact = _katabatic->setDestroyBaseContact ( true );
    set<AutoSegment*>::iterator  isegment = _destroyedSegments.begin();
    for ( ; isegment != _destroyedSegments.end() ; isegment++ ) {
      AutoContact* source = (*isegment)->getAutoSource();
      AutoContact* target = (*isegment)->getAutoTarget();
      (*isegment)->destroy ();
      if (source and source->canDestroy(true)) source->destroy ();
      if (target and target->canDestroy(true)) target->destroy ();
    }
    _katabatic->setDestroyBaseSegment ( destroySegment );
    _katabatic->setDestroyBaseContact ( destroyContact );
    set<AutoSegment*>().swap ( _destroyedSegments );

    ltraceout(110);

    return count;
  }


  Session*  Session::open ( KatabaticEngine* ktbt )
  {
    ltrace(110) << "Session::open()" << endl;

    if ( _session ) {
      if ( _session->_katabatic != ktbt )
        throw Error ( reopenSession, getString(_session->getKatabatic()).c_str() );

      return _session;
    }

    Session* session = new Session ( ktbt );
    session->_postCreate ();

    return session;
  }


  size_t  Session::close ()
  {
    ltrace(110) << "Session::close()" << endl;
    ltracein(110);

    if ( !_session ) throw Error ( openSessionError, "Session::Close()" );

    size_t count = _session->_preDestroy ();

    delete _session;
    _session = NULL;

    ltraceout(110);

    return count;
  }


  bool  Session::getDemoMode ()
  { return get("getDemoMode()")->_katabatic->getDemoMode(); }


  float  Session::getSaturateRatio ()
  { return get("getSaturateRatio()")->_katabatic->getSaturateRatio(); }


  bool  Session::getWarnGCellOverload ()
  { return get("getWarnGCellOverload()")->_katabatic->getWarnGCellOverload(); }


  void  Session::setWarnGCellOverload ( bool state )
  { get("getWarnGCellOverload()")->_katabatic->setWarnGCellOverload(state); }


  DbU::Unit  Session::getExtensionCap ()
  { return getConfiguration()->getExtensionCap(); }


  const Layer* Session::getRoutingLayer ( size_t depth )
  { return getConfiguration()->getRoutingLayer(depth); }


  const Layer* Session::getContactLayer ( size_t depth )
  { return getConfiguration()->getContactLayer(depth); }


  void  Session::link ( AutoContact* autoContact )
  { return get("link(AutoContact*)")->_katabatic->_link ( autoContact ); }


  void  Session::link ( AutoSegment* autoSegment )
  { return get("link(AutoSegment*)")->_katabatic->_link ( autoSegment ); }


  void  Session::unlink ( AutoContact* autoContact )
  { return get("unlink(AutoContact*)")->_katabatic->_unlink ( autoContact ); }


  void  Session::unlink ( AutoSegment* autoSegment )
  { return get("unlink(AutoSegment*)")->_katabatic->_unlink ( autoSegment ); }


  AutoContact* Session::lookup ( Contact* contact )
  { return get("lookup(Contact*)")->_katabatic->_lookup ( contact ); }


  AutoSegment* Session::lookup ( Segment* segment )
  { return get("lookup(Segment*)")->_katabatic->_lookup ( segment ); }


  string  Session::_getString () const
  {
    return "<" + _getTypeName() + " "
               + getString(_katabatic->getCell()->getName())
               + ">";
  }


  Record* Session::_getRecord () const
  {
    Record* record = new Record ( _getString() );
    record->add ( getSlot ( "_katabatic"   , _katabatic     ) );
    record->add ( getSlot ( "_autoContacts", &_autoContacts ) );
    record->add ( getSlot ( "_autoSegments", &_autoSegments ) );
    return record;
  }


} // End of Katabatic namespace.
