/*! \file src/graphlst.cpp
    \brief Implements a list of graphs
    \author Klaas Holwerda 
 
    Copyright: 2001-2004 (C) Klaas Holwerda 
 
    Licence: see kboollicense.txt 
 
    RCS-ID: $Id: graphlst.cpp,v 1.4 2009/09/10 17:04:09 titato Exp $
*/

//#include "debugdrv.h"
#include "kbool/booleng.h"
#include "kbool/graphlst.h"

//extern Debug_driver* _debug_driver;
//this here is to initialize the static iterator of graphlist
//with NOLIST constructor

int  graphsorterX( kbGraph *, kbGraph * );
int  graphsorterY( kbGraph *, kbGraph * );

kbGraphList::kbGraphList( Bool_Engine* GC )
{
    _GC = GC;
}

kbGraphList::kbGraphList( kbGraphList* other )
{
    _GC = other->_GC;

    TDLI<kbGraph> _LI = TDLI<kbGraph>( other );
    _LI.tohead();
    while ( !_LI.hitroot() )
    {
        insend( new kbGraph( _LI.item() ) );
        _LI++;
    }
}

kbGraphList::~kbGraphList()
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    //first empty the graph
    _LI.delete_all();
}

//prepare the graphlist for the boolean operations
//group all graphs into ONE graph
void kbGraphList::Prepare( kbGraph* total )
{
    if ( empty() )
        return;

    //round to grid and put in one graph
    _GC->SetState( "Simplify" );

    // Simplify all graphs in the list
    Simplify( ( double ) _GC->GetGrid() );

    if ( ! _GC->GetOrientationEntryMode() )
    {
        TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
        _LI.tohead();
        while ( !_LI.hitroot() )
        {
            _LI.item()->MakeClockWise();
            _LI++;
        }
    }

    Renumber();

    //the graplist contents will be transferred to one graph
    MakeOneGraph( total );
}

// the function will make from all the graphs in the graphlist one graph,
// simply by throwing all the links in one graph, the graphnumbers will
// not be changed
void kbGraphList::MakeOneGraph( kbGraph* total )
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    _LI.tohead();
    while( !_LI.hitroot() )
    {
        total->TakeOver( _LI.item() );
        delete _LI.item();
        _LI.remove();
    }
}

//
// Renumber all the graphs
//
void kbGraphList::Renumber()
{
    if ( _GC->GetOrientationEntryMode() )
    {
        TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
        _LI.tohead();
        while ( !_LI.hitroot() )
        {
            if ( _LI.item()->GetFirstLink()->Group() == GROUP_A )
                _LI.item()->SetNumber( 1 );
            else
                _LI.item()->SetNumber( 2 );
            _LI++;
        }
    }
    else
    {
        unsigned int Number = 1;
        TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
        _LI.tohead();
        while ( !_LI.hitroot() )
        {
            _LI.item()->SetNumber( Number++ );
            _LI++;
        }
    }
}


// Simplify the graphs
void kbGraphList::Simplify( double marge )
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    _LI.foreach_mf( &kbGraph::Reset_Mark_and_Bin );

    _LI.tohead();
    while ( !_LI.hitroot() )
    {
        if ( _LI.item()->Simplify( ( B_INT ) marge ) )
        {
            if ( _LI.item()->GetNumberOfLinks() < 3 )
                // delete this graph from the graphlist
            {
                delete _LI.item();
                _LI.remove();
            }
        }
        else
            _LI++;
    }
}

// Smoothen the graphs
void kbGraphList::Smoothen( double marge )
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    _LI.foreach_mf( &kbGraph::Reset_Mark_and_Bin );

    _LI.tohead();
    while ( !_LI.hitroot() )
    {
        if ( _LI.item()->Smoothen( ( B_INT ) marge ) )
        {
            if ( _LI.item()->GetNumberOfLinks() < 3 )
                // delete this graph from the graphlist
            {
                delete _LI.item();
                _LI.remove();
            }
        }
        else
            _LI++;
    }
}


// Turn off all markers in all the graphs
void kbGraphList::UnMarkAll()
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    _LI.foreach_mf( &kbGraph::Reset_Mark_and_Bin );
}

//==============================================================================
//
//======================== BOOLEAN FUNCTIONS ===================================
//
//==============================================================================

void kbGraphList::Correction()
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    int todo = _LI.count();

    if ( _GC->GetInternalCorrectionFactor() ) //not zero
    {
        _LI.tohead();
        for( int i = 0; i < todo ; i++ )
        {
            //the input graph will be empty in the end
            kbGraphList *_correct = new kbGraphList( _GC );
            {
                _LI.item()->MakeClockWise();
                _LI.item()->Correction( _correct, _GC->GetInternalCorrectionFactor() );

                delete _LI.item();
                _LI.remove();

                //move corrected graphlist to result
                while ( !_correct->empty() )
                {
                    //add to end
                    _LI.insend( ( kbGraph* )_correct->headitem() );
                    _correct->removehead();
                }
            }
            delete _correct;
        }
    }
}

void kbGraphList::MakeRings()
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    int todo = _LI.count();

    _LI.tohead();
    for( int i = 0; i < todo ; i++ )
    {
        //the input graph will be empty in the end
        kbGraphList *_ring = new kbGraphList( _GC );
        {
            _LI.item()->MakeClockWise();
            _LI.item()->MakeRing( _ring, _GC->GetInternalCorrectionFactor() );

            delete _LI.item();
            _LI.remove();

            //move created rings graphs to this
            while ( !_ring->empty() )
            {
                //add to end
                ( ( kbGraph* )_ring->headitem() )->MakeClockWise();
                _LI.insend( ( kbGraph* )_ring->headitem() );
                _ring->removehead();
            }
        }
        delete _ring;
    }

}

//merge the graphs in the list and return the merged result
void kbGraphList::Merge()
{
    if ( count() <= 1 )
        return;

    {
        TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
        _LI.tohead();
        while ( !_LI.hitroot() )
        {
            _LI.item()->SetGroup( GROUP_A );
            _LI++;
        }
    }

    kbGraph* _tomerge = new kbGraph( _GC );

    Renumber();

    //the graplist contents will be transferred to one graph
    MakeOneGraph( _tomerge );
    //the original is empty now

    _tomerge->Prepare( 1 );
    _tomerge->Boolean( BOOL_OR, this );

    delete _tomerge;
}

#define TRIALS 30
#define SAVEME 1

//perform boolean operation on the graphs in the list
void kbGraphList::Boolean( BOOL_OP operation, int intersectionRunsMax )
{
    _GC->SetState( "Performing Boolean Operation" );

    if ( count() == 0 )
        return;

    kbGraph* _prepared = new kbGraph( _GC );

    if ( empty() )
        return;

    //round to grid and put in one graph
    _GC->SetState( "Simplify" );

    int intersectionruns = 1;

    while ( intersectionruns <= intersectionRunsMax )
    {
        try
        {
            Prepare( _prepared );

            if ( _prepared->GetNumberOfLinks() )
            {
                //calculate intersections etc.
                _GC->SetState( "prepare" );
                _prepared->Prepare( intersectionruns );
                //_prepared->writegraph(true);
                _prepared->Boolean( operation, this );
            }
            intersectionruns = intersectionRunsMax + 1;
        }
        catch ( Bool_Engine_Error & error )
        {
#if KBOOL_DEBUG
            _prepared->WriteGraphKEY( _GC );
#endif
            intersectionruns++;
            if ( intersectionruns == intersectionRunsMax )
            {
                _prepared->WriteGraphKEY( _GC );
                _GC->info( error.GetErrorMessage(), "error" );
                throw error;
            }
        }
        catch( ... )
        {

#if KBOOL_DEBUG
            _prepared->WriteGraphKEY( _GC );
#endif
            intersectionruns++;
            if ( intersectionruns == intersectionRunsMax )
            {
                _prepared->WriteGraphKEY( _GC );
                _GC->info( "Unknown exception", "error" );
                throw;
            }
        }
    }

    delete _prepared;
}


void kbGraphList::WriteGraphs()
{
    TDLI<kbGraph> _LI = TDLI<kbGraph>( this );
    _LI.tohead();
    while( !_LI.hitroot() )
    {
        _LI.item()->writegraph( false );
        _LI++;
    }
}

void kbGraphList::WriteGraphsKEY( Bool_Engine* GC )
{
    FILE * file = fopen( "graphkeyfile.key", "w" );

    fprintf( file, "\
             HEADER 5; \
             BGNLIB; \
             LASTMOD {2-11-15  15:39:21}; \
             LASTACC {2-11-15  15:39:21}; \
             LIBNAME trial; \
             UNITS; \
             USERUNITS 0.0001; PHYSUNITS 1e-009; \
             \
             BGNSTR;  \
             CREATION {2-11-15  15:39:21}; \
             LASTMOD  {2-11-15  15:39:21}; \
             STRNAME top; \
             ");

    TDLI<kbGraph> _LI=TDLI<kbGraph>(this);
    _LI.tohead();
    while(!_LI.hitroot())
    {
        _LI.item()->WriteKEY( GC, file );
        _LI++;
    }

    fprintf(file,"\
            ENDSTR top; \
            ENDLIB; \
            ");

    fclose (file);
}