// -*- C++ -*-
//
// This file is part of the Coriolis Software.
// Copyright (c) UPMC 2008-2018, All Rights Reserved
//
// +-----------------------------------------------------------------+
// |                   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  :       "./RoutingPlane.cpp"                       |
// +-----------------------------------------------------------------+


#include "hurricane/Error.h"
#include "hurricane/Box.h"
#include "hurricane/Cell.h"
#include "crlcore/RoutingLayerGauge.h"
#include "kite/HorizontalTrack.h"
#include "kite/VerticalTrack.h"
#include "kite/RoutingPlane.h"
#include "kite/KiteEngine.h"


namespace {


  const char* badLayerGauge =
    "RoutingPlane::create() :\n\n"
    "    No plane at depth %u in %s.";


} // End of local namespace.


namespace Kite {

  using std::cerr;
  using std::endl;
  using Hurricane::tab;
  using Hurricane::Error;
  using Hurricane::Box;
  using Hurricane::Cell;


// -------------------------------------------------------------------
// Class  :  "RoutingPlane".


  RoutingPlane::RoutingPlane ( KiteEngine* kite, size_t depth )
    : _kite      (kite)
    , _layerGauge(kite->getLayerGauge(depth))
    , _depth     (depth)
    , _flags     (0)
    , _axisMin   (0)
    , _axisMax   (0)
    , _trackMin  (0)
    , _trackMax  (0)
    , _tracks    ()
  {
    switch ( _layerGauge->getDirection() ) {
      case Constant::Horizontal: _flags |= KbHorizontal; break;
      case Constant::Vertical:   _flags |= KbVertical; break;
      default:
        cerr << Error( "RoutingPlane::RoutingPlane() - Unknown plane direction from LayerGauge: %u"
                     , _layerGauge->getDirection() ) << endl;
    }
  }


  RoutingPlane::~RoutingPlane ()
  { }


  void  RoutingPlane::destroy ()
  {
    cdebug_log(155,1) << "RoutingPlane::destroy() - "
                      << (void*)this << " " << this << endl;

    for ( size_t index=0 ; index<_tracks.size() ; ++index )
      _tracks[index]->destroy();

    delete this;

    cdebug_tabw(155,-1);
  }


  RoutingPlane* RoutingPlane::create ( KiteEngine* kite, size_t depth )
  {
    RoutingPlane* plane = new RoutingPlane ( kite, depth );

    if (not plane->_layerGauge)
      throw Error( badLayerGauge, depth, getString(kite->getRoutingGauge()).c_str() );

    DbU::Unit    hExtension = 0;
    DbU::Unit    vExtension = 0;
    unsigned int gaugeDepth = 0;
    if (Session::getLayerGauge(gaugeDepth)->getType() == Constant::PinOnly) ++gaugeDepth;

    bool HV = (Session::getLayerGauge(gaugeDepth)->getDirection() == Constant::Horizontal);
    hExtension = Session::getLayerGauge( gaugeDepth + (HV?1:0) )->getPitch() / 2;
    vExtension = Session::getLayerGauge( gaugeDepth + (HV?0:1) )->getPitch() / 2;

    size_t  trackNumber;
    Box     abutmentBox = kite->getCell()->getAbutmentBox();
  // HARD CODED.
    if (plane->getDirection() == KbHorizontal) {
      plane->_trackMin = abutmentBox.getXMin() - hExtension;
      plane->_trackMax = abutmentBox.getXMax() + hExtension;
      plane->_axisMin  = abutmentBox.getYMin();
      plane->_axisMax  = abutmentBox.getYMax();
      trackNumber      = plane->computeTracksSize();
    } else {
      plane->_trackMin = abutmentBox.getYMin() - vExtension;
      plane->_trackMax = abutmentBox.getYMax() + vExtension;
      plane->_axisMin  = abutmentBox.getXMin();
      plane->_axisMax  = abutmentBox.getXMax();
      trackNumber      = plane->computeTracksSize();
    }

    plane->_tracks.reserve( trackNumber );
    for ( size_t index=0 ; index<trackNumber ; ++index ) {
      if (plane->getDirection() == KbHorizontal) {
        plane->_tracks.push_back( HorizontalTrack::create( plane, index ) );
      // Ugly: Direct uses of CellGauge (middle tracks 4 & 5 for local use).
        if (depth == 1) {
          switch ( index%10 ) {
            case 4:
            case 5:
              plane->_tracks.back()->setLocalAssigned( true );
              break;
          }
        }
      } else {
        plane->_tracks.push_back( VerticalTrack::create( plane, index ) );
      }
    }

    return plane;
  }


  RoutingPlane* RoutingPlane::getTop () const
  { return getKiteEngine()->getRoutingPlaneByIndex( getDepth()+1 ); }


  RoutingPlane* RoutingPlane::getBottom () const
  {
    if (not getDepth()) return NULL;
    return getKiteEngine()->getRoutingPlaneByIndex( getDepth()-1 );
  }


  Track* RoutingPlane::getTrackByIndex ( size_t index ) const
  {
    if (index >= getTracksSize()) return NULL;
    return _tracks[index];
  }


  Track* RoutingPlane::getTrackByPosition ( DbU::Unit axis, unsigned int mode ) const
  {
    return getTrackByIndex( getLayerGauge()->getTrackIndex( getAxisMin()
                                                          , getAxisMax()
                                                          , axis
                                                          , mode
                                                          ) );
  }


  bool  RoutingPlane::_check ( unsigned int& overlaps ) const
  {
    bool coherency = true;

    for ( size_t i=0 ; i<_tracks.size() ; ++i ) {
      coherency = _tracks[i]->check(overlaps) and coherency;
    }

    return coherency;
  }


  string  RoutingPlane::_getString () const
  {
    return "<" + _getTypeName() + " @"
               + getString(_depth) + " "
               + getString(getLayer()) + " [ "
               + ((getDirection() == KbHorizontal) ? " horizontal [" : " vertical [")
               + getString(_tracks.size()) + "/"
               + getString(_tracks.capacity())
               + "]>";
  }


  Record* RoutingPlane::_getRecord () const
  {
    Record* record = new Record ( getString(this) );
    record->add( getSlot          ( "_kite"         ,  _kite       ) );
    record->add( getSlot          ( "_layerGauge"   ,  _layerGauge ) );
    record->add( getSlot          ( "_depth"        , &_depth      ) );
    record->add( DbU::getValueSlot( "_axisMin"      , &_axisMin    ) );
    record->add( DbU::getValueSlot( "_axisMax"      , &_axisMax    ) );
    record->add( DbU::getValueSlot( "_trackMin"     , &_trackMin   ) );
    record->add( DbU::getValueSlot( "_trackMax"     , &_trackMax   ) );
    record->add( getSlot          ( "_tracks"       , &_tracks     ) );
                                     
    return record;
  }


} // Kite namespace.