// -*- 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  :       "./DataNegociate.cpp"                      |
// +-----------------------------------------------------------------+


#include <cstdlib>
#include <sstream>
#include "hurricane/Bug.h"
#include "hurricane/DebugSession.h"
#include "katabatic/AutoSegment.h"
#include "kite/DataNegociate.h"
#include "kite/RoutingEvent.h"


namespace Kite {

  using std::cerr;
  using std::endl;
  using std::map;
  using std::multimap;
  using std::make_pair;
  using std::ostringstream;
  using Hurricane::Bug;
  using Hurricane::DebugSession;
  using Hurricane::tab;
  using Katabatic::KbHorizontal;
  using Katabatic::KbPropagate;


// -------------------------------------------------------------------
// Class  :  "DataNegociate".


  DataNegociate::DataNegociate ( TrackElement* trackSegment )
    : _trackSegment     (trackSegment)
    , _childSegment     (NULL)
    , _routingEvent     (NULL)
    , _net              (trackSegment->getNet())
    , _state            (RipupPerpandiculars)
    , _stateCount       (1)
    , _terminals        (0)
    , _ripupCount       (0)
    , _leftMinExtend    (DbU::Max)
    , _rightMinExtend   (DbU::Min)
    , _attractors       ()
    , _perpandiculars   ()
    , _perpandicularFree(false)
    , _reduceRanges     { Interval(), Interval() }
  { }


  DataNegociate::~DataNegociate ()
  { }


  DbU::Unit  DataNegociate::getWiringDelta ( DbU::Unit axis ) const
  {
    DbU::Unit  attraction = 0;
    for ( size_t i=0 ; i < _attractors.size() ; i++ ) {
      if ( _attractors[i] > axis ) attraction += _attractors[i] - axis;
      else                         attraction += axis - _attractors[i];
    }
    for ( size_t i=0 ; i<2 ; ++i ) {
      if (_reduceRanges[i].isEmpty()) continue;
      if (_reduceRanges[i].contains(axis)) attraction -= 2*_trackSegment->getPitch();
    }
    return attraction;
  }


  void  DataNegociate::update ()
  {
    DebugSession::open( _trackSegment->getNet(), 150, 160 );

  //cdebug_log(9000,0) << "Deter| DataNegociate::update() - " << _trackSegment << endl;
    cdebug_log(159,1) << "DataNegociate::update() - " << _trackSegment << endl;

  //size_t               reduceCandidates = 0;
  //DbU::Unit            pitch            = _trackSegment->getPitch();
    vector<AutoSegment*> collapseds;
    vector<AutoSegment*> perpandiculars;
    map<DbU::Unit,int>   attractorSpins;

    _reduceRanges[0].makeEmpty();
    _reduceRanges[1].makeEmpty();
    _perpandiculars.clear();
    AutoSegment::getTopologicalInfos( _trackSegment->base()
                                    , collapseds
                                    , perpandiculars
                                    , _leftMinExtend
                                    , _rightMinExtend
                                    );

    _terminals = AutoSegment::getTerminalCount( _trackSegment->base(), collapseds );
  //cdebug_log(9000,0) << "Deter|    Terminals:" << _terminals << endl;
    _attractors.clear();
    _perpandiculars.clear();
    _perpandicularFree = Interval(false);

    cdebug_log(159,0) << "Extracting attractors from perpandiculars." << endl;
    for ( size_t i=0 ; i < perpandiculars.size() ; i++ ) {
      Interval      interval;
      TrackElement* perpandicular;

      if (perpandiculars[i]->isCanonical()) {
        perpandicular = Session::lookup( perpandiculars[i]->base() );
        if (perpandicular) perpandicular->getCanonical( interval );
      } else {
        perpandicular = Session::lookup( perpandiculars[i]->getCanonical(interval)->base() );
      }
      if (not perpandicular) {
        cerr << Bug( "Not a TrackSegment: %s\n      (perpandicular: %s)"
                 //, getString((void*)perpandiculars[i]->getCanonical(interval)->base()).c_str()
                   , getString(perpandiculars[i]->getCanonical(interval)).c_str()
                 //, getString((void*)perpandiculars[i]->base()).c_str()
                   , getString(perpandiculars[i]).c_str()
                   ) << endl;
        continue;
      }

      if (RoutingEvent::getStage() == RoutingEvent::Repair)
        perpandicular->base()->setFlagsOnAligneds( Katabatic::SegUnbound );

    //cerr << "perpandicular:" << perpandicular << endl;
    //cerr << "  " << interval << endl;
    //interval.inflate( DbU::fromLambda(-0.5) );

      cdebug_log(159,0)   << "| perpandicular: " << perpandiculars[i] << endl;
      cdebug_log(159,0)   << "| canonical:     " << perpandicular << endl;
      cdebug_log(159,1) << "Canonical // interval: " << interval << endl;

      _perpandiculars.push_back( perpandicular );
      if (perpandicular->getTrack()) {
        Interval  trackFree = perpandicular->getFreeInterval();
        cdebug_log(159,0) << "Track Perpandicular Free: " << trackFree << endl;

        _perpandicularFree.intersection( trackFree );
      } else {
        cdebug_log(159,0) << "Not in any track " << perpandicular << endl;
      }

#if 0
      if (interval.isPonctual()) {
        cdebug_log(159,0) << "Punctual attractor @" << DbU::getValueString(interval.getVMin()) << endl;
        _attractors.push_back( interval.getVMin() );
        cdebug_tabw(159,-1);
        continue;
      }

      if (  (interval.getVMin() != _trackSegment->getAxis())
         or AutoSegment::isTopologicalBound(perpandiculars[i]
                                           ,perpandicular->isHorizontal() ? KbHorizontal : 0
                                           ) ) {
        map<DbU::Unit,int>::iterator iattractor = attractorSpins.find( interval.getVMin() );
        if (iattractor == attractorSpins.end()) {
          attractorSpins.insert( make_pair(interval.getVMin(),-1) );
        } else {
          iattractor->second -= 1;
        }
        cdebug_log(159,0) << "Left attractor @" << DbU::getValueString(interval.getVMin()) << endl;
      }

      if (  (interval.getVMax() != _trackSegment->getAxis())
         or AutoSegment::isTopologicalBound(perpandiculars[i]
                                           ,KbPropagate | (perpandicular->isHorizontal() ? KbHorizontal : 0)
                                           ) ) {
        map<DbU::Unit,int>::iterator iattractor = attractorSpins.find( interval.getVMax() );
        if (iattractor == attractorSpins.end()) {
          attractorSpins.insert( make_pair(interval.getVMax(),1) );
        } else {
          iattractor->second += 1;
        }
        cdebug_log(159,0) << "Right attractor @" << DbU::getValueString(interval.getVMax()) << endl;
      }

      if (perpandicular->base()->isReduceCandidate()) {
        if (reduceCandidates < 2) {
          if (interval.getVMin()+DbU::fromLambda(0.5) == _trackSegment->getAxis()) {
            _reduceRanges[reduceCandidates] = Interval( interval.getVMax()-pitch
                                                      , interval.getVMax()+pitch );
          } else if (interval.getVMax()-DbU::fromLambda(0.5) == _trackSegment->getAxis()) {
            _reduceRanges[reduceCandidates] = Interval( interval.getVMin()-pitch
                                                      , interval.getVMin()+pitch );
          }
          ++reduceCandidates;
        } 
      }
#endif

      cdebug_tabw(159,-1);
    }
    if ( not _trackSegment->isTerminal() and (_perpandiculars.size() < 2) )
      cerr << Bug( "Less than two perpandiculars on %s.", getString(_trackSegment).c_str() ) << endl;

    map<DbU::Unit,int>::iterator iattractor = attractorSpins.begin();
    for ( ; iattractor != attractorSpins.end() ; iattractor++ ) {
      if (iattractor->second != 0)
        _attractors.push_back( iattractor->first );
    }

    ostringstream s;
    s << "Attractors [";
    for ( size_t i=0 ; i<_attractors.size() ; i++ ) {
      if ( i ) s << ", ";
      s << DbU::getValueString( _attractors[i] );
    }
    s << "]";
    cdebug_log(159,0) << s.str() << endl;
    cdebug_log(159,0) << "Perpandicular Free: " << _perpandicularFree << endl;


    cdebug_tabw(159,-1);
    DebugSession::close();
  }


  string  DataNegociate::_getString () const
  {
    return "<" + _getTypeName() + " "
               + getString(_trackSegment) + " "
               + getString(_terminals)
               + " [" + DbU::getValueString(_leftMinExtend)
               +  ":" + DbU::getValueString(_rightMinExtend)
               + "]>";
  }


  Record* DataNegociate::_getRecord () const
  {
    Record* record = new Record ( getString(this) );
    record->add( getSlot          ( "_routingEvent"  ,  _routingEvent   ) );
    record->add( getSlot          ( "_trackSegment"  ,  _trackSegment   ) );
    record->add( getSlot          ( "_childSegment"  ,  _childSegment   ) );
    record->add( getSlot          ( "_terminals"     ,  _terminals      ) );
    record->add( getSlot          ( "_ripupCount"    ,  _ripupCount     ) );
    record->add( DbU::getValueSlot( "_leftMinExtend" , &_leftMinExtend  ) );
    record->add( DbU::getValueSlot( "_rightMinExtend", &_rightMinExtend ) );
    record->add( getSlot          ( "_net"           ,  _net            ) );
                                     
    return record;
  }


  string  DataNegociate::getStateString ( DataNegociate* data )
  {
    ostringstream s;
    switch ( data->_state ) {
      case RipupPerpandiculars:    s << "RipupPerpandiculars";     break;
      case Minimize:               s << "Minimize";                break;
      case Dogleg:                 s << "Dogleg";                  break;
      case Slacken:                s << "Slacken";                 break;
      case ConflictSolveByHistory: s << "ConflictSolveByHistory1"; break;
      case ConflictSolveByPlaceds: s << "ConflictSolveByPlaceds";  break;
      case LocalVsGlobal:          s << "LocalVsGlobal";           break;
      case MoveUp:                 s << "MoveUp";                  break;
      case MaximumSlack:           s << "MaximumSlack";            break;
      case Unimplemented:          s << "Unimplemented";           break;
      case Repair:                 s << "REPAIR";                  break;
      default:
        s << "Unknown(" << data->_state << ")"; break;
    }
    s << ":" << data->_stateCount;
    return s.str();
  }


} // Kite namespace.