#include <algorithm>
//#include "crlcore/CEditor.h"
#include "hurricane/Technology.h"
#include "hurricane/DataBase.h"
#include "knik/Vertex.h"
#include "knik/Edge.h"
#include "knik/Graph.h"

namespace {
using namespace Knik;
struct EdgeToYComp : public binary_function<const Edge*, const Edge*, bool> { 
public:
    bool operator() ( Edge* edge1, Edge* edge2 ) const
    {
        return ( edge1->getTo()->getY() < edge2->getTo()->getY() ); 
    }
};
struct EdgeToXComp : public binary_function<const Edge*, const Edge*, bool> { 
public:
    bool operator() ( Edge* edge1, Edge* edge2 ) const
    {
        return ( edge1->getTo()->getX() > edge2->getTo()->getX() ); 
    }
};
struct EdgeFromYComp : public binary_function<const Edge*, const Edge*, bool> { 
public:
    bool operator() ( Edge* edge1, Edge* edge2 ) const
    {
        return ( edge1->getFrom()->getY() > edge2->getFrom()->getY() ); 
    }
};
struct EdgeFromXComp : public binary_function<const Edge*, const Edge*, bool> { 
public:
    bool operator() ( Edge* edge1, Edge* edge2 ) const
    {
        return ( edge1->getFrom()->getX() <  edge2->getFrom()->getX() ); 
    }
};
}

namespace Knik {

extern unsigned __congestion__;
extern unsigned __precongestion__;

  const Name Vertex::_extensionName = "Knik::Vertex";

// ********************************************************************
// VERTEX_Edges declaration
// ********************************************************************
class VERTEX_Edges : public Collection<Edge*> {
    public:
        typedef Collection<Edge*> Inherit;
    public:
        class Locator : public Hurricane::Locator<Edge*> {
                public:
                    typedef Hurricane::Locator<Edge*> Inherit;

                private:
                    const Vertex* _vertex;
                          Edge*   _edge;
                          int     _direction;

                public:
                    Locator(const Vertex* vertex = NULL, Edge* edge = NULL, int direction = 0);
                    Locator(const Locator& locator);

                    Locator& operator=(const Locator& locator);

                    virtual Edge* getElement() const;
                    virtual Hurricane::Locator<Edge*>* getClone() const;

                    virtual bool isValid() const;

                    virtual void progress();

                    virtual string _getString() const;
            };

    private:
        const Vertex* _vertex;
              Edge*   _edge;
              int     _direction;

    public: 
        VERTEX_Edges(const Vertex* vertex);
        VERTEX_Edges(const VERTEX_Edges& edges);

        VERTEX_Edges& operator=(const VERTEX_Edges& edges);
        virtual Collection<Edge*>* getClone() const;
        virtual Hurricane::Locator<Edge*>* getLocator() const;
        virtual string _getString() const;
};


Vertex::Vertex ( Graph* routingGraph, Point position, DbU::Unit halfWidth, DbU::Unit halfHeight )
// **********************************************************************************************
    : Inherit (routingGraph->getCell())
    , _routingGraph (routingGraph)
    , _predecessor (NULL)
    , _contact (NULL)
    , _position (position)
    , _vtuple (NULL)
    , _distance ((float)(HUGE))
    , _connexID (-1)
    , _netStamp (0)
    , _halfWidth  (halfWidth)
    , _halfHeight (halfHeight)
{
    _firstEdges[0] = NULL;
    _firstEdges[1] = NULL;
    _firstEdges[2] = NULL;
    _firstEdges[3] = NULL;
}

Vertex* Vertex::create ( Graph* routingGraph, Point position, DbU::Unit halfWidth, DbU::Unit halfHeight )
// *******************************************************************************************************
{
    Vertex* vertex = new Vertex ( routingGraph, position, halfWidth, halfHeight );

    vertex->_postCreate();

    return vertex;
}

void Vertex::_postCreate()
// ***********************
{
    Inherit::_postCreate();
}

Vertex::~Vertex()
// **************
{
}

void Vertex::destroy()
// ******************
{
    _preDestroy();
    delete this;
}

void Vertex::_preDestroy()
// **********************
{
    Inherit::_preDestroy();
}
 
void Vertex::attachToLocalRing ( Component* component )
// ****************************************************
{
    // XXX plus utilisée ? XXX
    //assert ( component );

    //Hook* hook = component->getBodyHook();
    //hook->detach();
    //if ( Hook* previousHook = getLocalRingHook() ) {
    //    //cerr << "Vertex::attachToLocalRingHook : going to attach " << previousHook << " to " << hook << endl;
    //    hook->attach ( previousHook );
    //}
    //setLocalRingHook ( hook );
}

void Vertex::sortEdges()
// *********************
{
    vector<Edge*> hEdgesOut;
    vector<Edge*> vEdgesOut;
    vector<Edge*> hEdgesIn;
    vector<Edge*> vEdgesIn;

    Edge* currentEdge;
    //ltracein(425);
    if ( (currentEdge = getHEdgeOut()) ) {  // si pas de HEdgeOut : rien a faire ^^
        while ( currentEdge ) {
            //ltrace(425) << "Pushing_back edge " << getString(currentEdge) << endl;
            hEdgesOut.push_back ( currentEdge );
            currentEdge = currentEdge->getNextFrom();
        }
        //ltrace(425) << "sorting vector..." << endl;
        sort ( hEdgesOut.begin(), hEdgesOut.end(), EdgeToYComp() );
        //ltrace(425) << "setting " << getString(hEdgesOut[0]) << " as hEdgeOut for vertex " << getString(this) << endl;
        setHEdgeOut ( hEdgesOut[0] );
        for ( unsigned i = 1 ; i < hEdgesOut.size() ; i++ ) {
            //ltrace(425) << "next : " << getString(hEdgesOut[i]) << endl;
            hEdgesOut[i-1]->setNextFrom ( hEdgesOut[i] );
        }
        hEdgesOut[hEdgesOut.size()-1]->setNextFrom ( NULL ); // si jamais avant en milieu de chaine ...
    }
    //ltraceout(425);
    if ( (currentEdge = getVEdgeOut()) ) {
        while ( currentEdge ) {
            vEdgesOut.push_back ( currentEdge );
            currentEdge = currentEdge->getNextFrom();
        }
        sort ( vEdgesOut.begin(), vEdgesOut.end(), EdgeToXComp() );
        setVEdgeOut ( vEdgesOut[0]);
        for ( unsigned i = 1 ; i < vEdgesOut.size() ; i++ ) {
            vEdgesOut[i-1]->setNextFrom ( vEdgesOut[i] );
        }
        vEdgesOut[vEdgesOut.size()-1]->setNextFrom ( NULL );
    }
    if ( (currentEdge = getHEdgeIn()) ) {
        while ( currentEdge ) {
            hEdgesIn.push_back ( currentEdge );
            currentEdge = currentEdge->getNextTo();
        }
        sort ( hEdgesIn.begin(), hEdgesIn.end(), EdgeFromYComp() );
        setHEdgeIn ( hEdgesIn[0] );
        for ( unsigned i = 1 ; i < hEdgesIn.size() ; i++ ) {
            hEdgesIn[i-1]->setNextTo ( hEdgesIn[i] );
        }
        hEdgesIn[hEdgesIn.size()-1]->setNextTo ( NULL );
    }
    if ( (currentEdge = getVEdgeIn()) ) {
        while ( currentEdge ) {
            vEdgesIn.push_back ( currentEdge );
            currentEdge = currentEdge->getNextTo();
        }
        sort ( vEdgesIn.begin(), vEdgesIn.end(), EdgeFromXComp() );
        setVEdgeIn ( vEdgesIn[0] );
        for ( unsigned i = 1 ; i < vEdgesIn.size() ; i++ ) {
            vEdgesIn[i-1]->setNextTo ( vEdgesIn[i] );
        }
        vEdgesIn[vEdgesIn.size()-1]->setNextTo ( NULL );
    }
    
}

Edges Vertex::getAdjacentEdges() const
// ***********************************
{
    return VERTEX_Edges ( this );
}

Contact* Vertex::getContact ( Edge* arrivalEdge )
// **********************************************
{

    if ( _contact ) {
        // il y deja un contact associe au vertex   XXX on verifie que le contact appartient bien au workingNet du routingGraph XXX
        if ( _routingGraph->getWorkingNet() == _contact->getNet() )
            return _contact;
        _contact = NULL;
    }

    //assert ( arrivalEdge ); // ca change si on se decide quele vertex pointe sur le contact et pas le hook d'un routingPad ou autre
    if ( arrivalEdge ) {
        int arrivalConnexID = arrivalEdge->getConnexID();
        bool createContact = false;
        bool straightOn = false;
        if ( arrivalEdge->isVertical() ) {
            if ( _firstEdges[0] && ( _firstEdges[0]->getConnexID() == arrivalConnexID ) && ( _firstEdges[0]->getNetStamp() == _netStamp ) )
                createContact = true;
            else if ( _firstEdges[2] && ( _firstEdges[2]->getConnexID() == arrivalConnexID ) && ( _firstEdges[2]->getNetStamp() == _netStamp ) )
                createContact = true;
            else if (  ( _firstEdges[1] == arrivalEdge )
                    && ( _firstEdges[3] && ( _firstEdges[3]->getConnexID() == arrivalConnexID ) && ( _firstEdges[3]->getNetStamp() == _netStamp ) ) )
                straightOn = true;
            else if (  ( _firstEdges[3] == arrivalEdge )
                    && ( _firstEdges[1] && ( _firstEdges[1]->getConnexID() == arrivalConnexID ) && ( _firstEdges[1]->getNetStamp() == _netStamp ) ) )
                straightOn = true;
            else
                throw Error ( "Vertex::getContact(): problem with edges configuration *1." );
        }
        else {
            if ( _firstEdges[1] && ( _firstEdges[1]->getConnexID() == arrivalConnexID ) && ( _firstEdges[1]->getNetStamp() == _netStamp ) )
                createContact = true;
            else if ( _firstEdges[3] && ( _firstEdges[3]->getConnexID() == arrivalConnexID ) && ( _firstEdges[3]->getNetStamp() == _netStamp ) )
                createContact = true;
            else if (  ( _firstEdges[0] == arrivalEdge )
                    && ( _firstEdges[2] && ( _firstEdges[2]->getConnexID() == arrivalConnexID ) && ( _firstEdges[2]->getNetStamp() == _netStamp ) ) )
                straightOn = true;
            else if (  ( _firstEdges[2] == arrivalEdge )
                    && ( _firstEdges[0] && ( _firstEdges[0]->getConnexID() == arrivalConnexID ) && ( _firstEdges[0]->getNetStamp() == _netStamp ) ) )
                straightOn = true;
            else
                throw Error ( "Vertex::getContact(): problem with Edges configuration *2." );
        }
        if ( createContact ) {
            Contact* contact = Contact::create ( _routingGraph->getWorkingNet()
                                             //, getLayer(1)
                                               , DataBase::getDB()->getTechnology()->getLayer("metal2")
                                               , _position.getX()
                                               , _position.getY()
                                               , _halfWidth/5
                                               , _halfHeight/5
                                               );
            // on a besoin de tester si par routage précédent il n'y a pas un segment du net qui traverse le vertex ....
            Segment* segment0 = NULL;
            Segment* segment1 = NULL;
            Segment* segment2 = NULL;
            Segment* segment3 = NULL;
            if ( _firstEdges[0] )
                segment0 = _firstEdges[0]->getSegmentFor ( _routingGraph->getWorkingNet() );
            if ( _firstEdges[1] )
                segment1 = _firstEdges[1]->getSegmentFor ( _routingGraph->getWorkingNet() );
            if ( _firstEdges[2] )
                segment2 = _firstEdges[2]->getSegmentFor ( _routingGraph->getWorkingNet() );
            if ( _firstEdges[3] )
                segment3 = _firstEdges[3]->getSegmentFor ( _routingGraph->getWorkingNet() );

            if ( (segment0 != NULL) && (segment0 == segment2) ) {
                if ( !dynamic_cast<Contact*>(segment0->getSource()) )    throw Error ("Vertex::getContact(): does not consider Segment not based on Contact (source).");
                if ( !dynamic_cast<Contact*>(segment0->getTarget()) )    throw Error ("Vertex::getContact(): does not consider Segment not based on Contact (target).");
                Contact* targetContact = static_cast<Contact*>(segment0->getTarget());
                _routingGraph->removeSegment(segment0);
                segment0->getTargetHook()->detach();
                segment0->getTargetHook()->attach(contact->getBodyHook());
                _routingGraph->insertSegment(segment0);
                Segment* newSeg = _routingGraph->createSegment(contact, targetContact);
                _routingGraph->insertSegment(newSeg);
            }
            if ( (segment1 != NULL) && (segment1 == segment3) ) {
                if ( !dynamic_cast<Contact*>(segment1->getSource()) )    throw Error ("Vertex::getContact(): does not consider Segment not based on Contact (Source).");
                if ( !dynamic_cast<Contact*>(segment1->getTarget()) )    throw Error ("Vertex::getContact(): does not consider Segment not based on Contact (Target).");
                Contact* targetContact = static_cast<Contact*>(segment1->getTarget());
                _routingGraph->removeSegment(segment1);
                segment1->getTargetHook()->detach();
                segment1->getTargetHook()->attach(contact->getBodyHook());
                _routingGraph->insertSegment(segment1);
                Segment* newSeg = _routingGraph->createSegment(contact, targetContact);
                _routingGraph->insertSegment(newSeg);
            }
            return contact;
        }
        if ( straightOn )
            return NULL;
    }

    throw Error ( "Vertex::getContact(): what are you doing here O_o?" );
}

Edge* Vertex::getHEdgeLeadingTo ( Vertex* to )
// *******************************************
{
    Edge* edge = getHEdgeOut();
    while ( edge ) {
        if ( edge->getTo() == to )
            break;
        edge = edge->getNextFrom();
    }
    return edge;
}

Edge* Vertex::getVEdgeLeadingTo ( Vertex* to )
// *******************************************
{
    Edge* edge = getVEdgeOut();
    while ( edge ) {
        if ( edge->getTo() == to ) 
            break;
        edge = edge->getNextFrom();
    }
    return edge;
}

Edge* Vertex::getHEdgeComingFrom ( Vertex* from )
// **********************************************
{
    Edge* edge = getHEdgeIn();
    while ( edge ) {
        if ( edge->getFrom() == from ) 
            break;
        edge = edge->getNextTo();
    }
    return edge;
}

Edge* Vertex::getVEdgeComingFrom ( Vertex* from )
// **********************************************
{
    Edge* edge = getVEdgeIn();
    while ( edge ) {
        if ( edge->getFrom() == from ) 
            break;
        edge = edge->getNextTo();
    }
    return edge;
}

Edge* Vertex::getBestHEdgeOut ( DbU::Unit yDest )
// *****************************************
{
    Edge* bestEdge    = getHEdgeOut();
    Edge* currentEdge = bestEdge;

    while ( (currentEdge = currentEdge->getNextFrom()) ) {
        if ( abs(currentEdge->getTo()->getY() - yDest) < abs(bestEdge->getTo()->getY() - yDest) )
            bestEdge = currentEdge;
    }

    return bestEdge;
}

Edge* Vertex::getBestVEdgeOut ( DbU::Unit xDest )
// *****************************************
{
    Edge* bestEdge    = getVEdgeOut();
    Edge* currentEdge = bestEdge;

    while ( (currentEdge = currentEdge->getNextFrom()) ) {
        if ( abs(currentEdge->getTo()->getX() - xDest) < abs(bestEdge->getTo()->getX() - xDest) )
            bestEdge = currentEdge;
    }

    return bestEdge;
}

Edge* Vertex::getBestHEdgeIn ( DbU::Unit yDest )
// ****************************************
{
    Edge* bestEdge    = getHEdgeIn();
    Edge* currentEdge = bestEdge;

    while ( (currentEdge = currentEdge->getNextTo()) ) {
        if ( abs(currentEdge->getFrom()->getY() - yDest) < abs(bestEdge->getFrom()->getY() - yDest) )
            bestEdge = currentEdge;
    }

    return bestEdge;
}

Edge* Vertex::getBestVEdgeIn ( DbU::Unit xDest )
// ****************************************
{
    Edge* bestEdge    = getVEdgeIn();
    Edge* currentEdge = bestEdge;

    while ( (currentEdge = currentEdge->getNextTo()) ) {
        if ( abs(currentEdge->getFrom()->getX() - xDest) < abs(bestEdge->getFrom()->getX() - xDest) )
            bestEdge = currentEdge;
    }

    return bestEdge;
}

bool Vertex::hasInfo() const
// *************************
{
   return (_netStamp == _routingGraph->getNetStamp());
}

void Vertex::translate ( const DbU::Unit& dx, const DbU::Unit& dy )
// ****************************************************************
{
    _position.translate ( dx, dy );
}

Cell* Vertex::getCell() const
// **************************
{
    return _routingGraph->getCell();
}

// void Vertex::_Draw ( View* view, BasicLayer* basicLayer, const Box& updateArea, const Transformation& transformation )
// // *******************************************************************************************************************
// {
//     DbU::Unit size = DbU::lambda(5);
//     view->FillCircle ( transformation.getPoint ( getPosition() ), size );
//     //if ( _distance < (float)(HUGE) )
//     if ( _netStamp == _routingGraph->getNetStamp() &&  _distance < (float)(HUGE) )
//         view->DrawString ( getString(_distance), getXMax() - (_halfWidth), getYMax() - (_halfHeight/2) );
//     if ( _connexID != -1 )
//         view->DrawString ( getString(_connexID)+","+getString(_netStamp), getXMax() - (_halfWidth), getYMin() + (_halfHeight/2) );
//     //if ( _predecessor ) {
//     //    Vertex* oppositeVertex = _predecessor->getOpposite ( this );
//     //    Point position = transformation.getPoint ( _position );
//     //    Point oppPos   = transformation.getPoint ( oppositeVertex->getPosition() );
//     //    DbU::Unit  origX = position.getX();
//     //    DbU::Unit  origY = position.getY();
//     //    DbU::Unit  destX = position.getX() + (4*(oppPos.getX() - position.getX()))/5;
//     //    DbU::Unit  destY = position.getY() + (4*(oppPos.getY() - position.getY()))/5;
//     //    view->DrawLine ( origX, origY, destX, destY );
//     //}
// }

// void Vertex::_Highlight ( View* view, const Box& updateArea, const Transformation& transformation )
// // ************************************************************************************************
// {
//     DbU::Unit size = DbU::lambda(5);
//     view->FillCircle ( transformation.getPoint ( getPosition() ), size );
// }

// bool Vertex::_IsInterceptedBy ( View* view, const Point& point, const DbU::Unit& aperture ) const
// // *****************************************************************************************
// {
//     Box area ( point );
//     area.inflate(aperture);

//     return getBoundingBox().intersect ( area );
// //    return false;
// }

void Vertex::invalidate ( bool propagateFlag )
// *******************************************
{
    Inherit::invalidate ( false );

    // Invalider toutes les Edges associées ?
}

string Vertex::_getString() const
// ******************************
{
    return "<" + _TName ( "Vertex" )
         + ":" + getString ( _connexID )
         + " " + getString ( _netStamp )
         + " " + getString ( DbU::getValueString(_position.getX()) )
         + " " + getString ( DbU::getValueString(_position.getY()) ) + ">";
}

Record* Vertex::_getRecord() const
// *************************
{
    Record* record = Inherit::_getRecord();

    if ( !record )
        record = new Record ( getString ( this ) );

    record->add ( getSlot ( "hEdgeOut", getHEdgeOut() ) );
    record->add ( getSlot ( "vEdgeOut", getVEdgeOut() ) );
    record->add ( getSlot ( "hEdgeIn", getHEdgeIn() ) );
    record->add ( getSlot ( "vEdgeIn", getVEdgeIn() ) );
    record->add ( getSlot ( "predecessor", _predecessor ) );
    //if ( _localRingHook)  record->add ( getSlot ( "localRingHook", _localRingHook ) );
    record->add ( getSlot ( "contact", _contact ) );
    record->add ( getSlot ( "connexID", _connexID ) );
    record->add ( getSlot ( "position", _position ) );
    record->add ( getSlot ( "distance", _distance ) );
    record->add ( getSlot ( "netStamp", _netStamp ) );
    record->add ( getSlot ( "halfWidth", _halfWidth ) );
    record->add ( getSlot ( "halfHeight", _halfHeight ) );

    return record;
}


// ************************************************************************************************
// VERTEX_Edges implementation
// ************************************************************************************************
VERTEX_Edges::VERTEX_Edges ( const Vertex* vertex )
// ************************************************
    : Inherit()
    , _vertex(vertex)
    , _edge(NULL)
    , _direction(0)
{
    // _direction represents one of the four direction in the routing graph. these ditections are set as follow :
    // 0 = East
    // 1 = North
    // 2 = West
    // 3 = South
    for ( _direction = 0 ; _direction < 4 ; _direction++ ) {
        if ( (_edge = _vertex->getFirstEdges(_direction)) )
            return;
    }
}

VERTEX_Edges::VERTEX_Edges ( const VERTEX_Edges& edges )
// *****************************************************
    : Inherit()
    , _vertex(edges._vertex)
    , _edge(edges._edge)
    , _direction(edges._direction)
{
}

VERTEX_Edges& VERTEX_Edges::operator=(const VERTEX_Edges& edges)
// *************************************************************
{
    _vertex    = edges._vertex;
    _edge      = edges._edge;
    _direction = edges._direction;
    return *this;
}

Collection<Edge*>* VERTEX_Edges::getClone() const
// **********************************************
{
    return new VERTEX_Edges(*this);
}

Locator<Edge*>* VERTEX_Edges::getLocator() const
// *********************************************
{
    return new Locator(_vertex, _edge, _direction);
}

string VERTEX_Edges::_getString() const
// ************************************
{
    string s = "<" + _TName("Vertex::Edges");
    if (_vertex) s += " " + getString(_vertex);
    s += ">";
    return s;
}

// ************************************************************************************************
// VERTEX_Edges::Locator implementation
// ************************************************************************************************
VERTEX_Edges::Locator::Locator(const Vertex* vertex, Edge* edge, int direction)
// ****************************************************************************
    : Inherit()
    , _vertex(vertex)
    , _edge(edge)
    , _direction(direction)
{
}

VERTEX_Edges::Locator::Locator(const Locator& locator)
// ***************************************************
    : Inherit()
    , _vertex(locator._vertex)
    , _edge(locator._edge)
    , _direction(locator._direction)
{
}

VERTEX_Edges::Locator& VERTEX_Edges::Locator::operator=(const Locator& locator)
// ****************************************************************************
{
    _vertex    = locator._vertex;
    _edge      = locator._edge;
    _direction = locator._direction;
    return *this;
}

Edge* VERTEX_Edges::Locator::getElement() const
// ********************************************
{
    return _edge;
}

Locator<Edge*>* VERTEX_Edges::Locator::getClone() const
// ****************************************************
{
    return new Locator(*this);
}

bool VERTEX_Edges::Locator::isValid() const
// ****************************************
{
    return (_edge != NULL );
}

void VERTEX_Edges::Locator::progress()
// ***********************************
{
    if ( !_edge )
        return;

    if ( _direction == 0 || _direction == 1 )
        _edge = _edge->getNextFrom();
    else
        _edge = _edge->getNextTo();

    while ( !_edge ) {
        if ( _direction == 3 )
            return;
        _direction++;
        _edge = _vertex->getFirstEdges(_direction);
    }
}

string VERTEX_Edges::Locator::_getString() const
// *********************************************
{
    string s = "<" + _TName("Vertex::Edges::Locator");
    if (_vertex) s += " " + getString(_vertex);
    s += ">";
    return s;
}

} // namespace Knik
