/*********************/
/* dangling_ends.cpp */
/*********************/

#include "fctsys.h"
#include "gr_basic.h"
#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"

#include "netlist.h" /* Definitions generales liees au calcul de netliste */
#include "protos.h"

enum End_Type {
    UNKNOWN = 0,
    WIRE_START_END,
    WIRE_END_END,
    BUS_START_END,
    BUS_END_END,
    JUNCTION_END,
    PIN_END,
    LABEL_END,
    ENTRY_END,
    SHEET_LABEL_END
};

class DanglingEndHandle
{
public:
    const void*        m_Item;
    wxPoint            m_Pos;
    int                m_Type;
    DanglingEndHandle* m_Pnext;

    DanglingEndHandle( int type ) {
        m_Item  = NULL;
        m_Type  = type;
        m_Pnext = NULL;
    }
};

DanglingEndHandle* ItemList;

static void         TestWireForDangling( EDA_DrawLineStruct* DrawRef,
                                         WinEDA_SchematicFrame* frame, wxDC* DC );
void                TestLabelForDangling( DrawTextStruct* label,
                                          WinEDA_SchematicFrame* frame, wxDC* DC );
DanglingEndHandle*  RebuildEndList( EDA_BaseStruct* DrawList );

/**********************************************************/
bool SegmentIntersect( int Sx1, int Sy1, int Sx2, int Sy2,
                       int Px1, int Py1 )
/**********************************************************/

/* Retourne TRUE si le point P est sur le segment S.
 *  Le segment est suppose horizontal ou vertical.
 */
{
    int Sxmin, Sxmax, Symin, Symax;

    if( Sx1 == Sx2 )          /* Line S is vertical. */
    {
        Symin = MIN( Sy1, Sy2 ); Symax = MAX( Sy1, Sy2 );
        if( Px1 != Sx1 )
            return FALSE;
        if( Py1 >= Symin  &&  Py1 <= Symax )
            return TRUE;
        else
            return FALSE;
    }
    else if( Sy1 == Sy2 )    /* Line S is horizontal. */
    {
        Sxmin = MIN( Sx1, Sx2 ); Sxmax = MAX( Sx1, Sx2 );
        if( Py1 != Sy1 )
            return FALSE;
        if( Px1 >= Sxmin  &&  Px1 <= Sxmax )
            return TRUE;
        else
            return FALSE;
    }
    else
        return FALSE;   // Segments quelconques
}


/******************************************************************************/
void WinEDA_SchematicFrame::TestDanglingEnds( EDA_BaseStruct* DrawList, wxDC* DC )
/******************************************************************************/

/* Met a jour les membres m_Dangling des wires, bus, labels
 */
{
    EDA_BaseStruct*          DrawItem;
    const DanglingEndHandle* DanglingItem, * nextitem;

    if( ItemList )
        for( DanglingItem = ItemList; DanglingItem != NULL; DanglingItem = nextitem )
        {
            nextitem = DanglingItem->m_Pnext;
            delete DanglingItem;
        }

    ItemList = RebuildEndList( DrawList );

    // Controle des elements
    for( DrawItem = DrawList; DrawItem != NULL; DrawItem = DrawItem->Pnext )
    {
        switch( DrawItem->Type() )
        {
        case DRAW_GLOBAL_LABEL_STRUCT_TYPE:
        case DRAW_LABEL_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (DrawLabelStruct*) DrawItem )
            TestLabelForDangling( STRUCT, this, DC );
            break;
            break;

        case DRAW_SEGMENT_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (EDA_DrawLineStruct*) DrawItem )
            if( STRUCT->m_Layer == LAYER_WIRE )
            {
                TestWireForDangling( STRUCT, this, DC );
                break;
            }
            if( STRUCT->m_Layer == LAYER_NOTES )
                break;
            if( STRUCT->m_Layer == LAYER_BUS )
            {
                STRUCT->m_StartIsDangling   =
                    STRUCT->m_EndIsDangling = FALSE;
                break;
            }
            break;

        default:
            ;
        }
    }
}


/********************************************************************/
LibDrawPin* WinEDA_SchematicFrame::LocatePinEnd( EDA_BaseStruct* DrawList,
                                                 const wxPoint&  pos )
/********************************************************************/

/* Teste si le point de coordonnées pos est sur l'extrémité d'une PIN
 *  retourne un pointeur sur la pin
 *  NULL sinon
 */
{
    EDA_SchComponentStruct* DrawLibItem;
    LibDrawPin* Pin;
    wxPoint pinpos;

    Pin = LocateAnyPin( DrawList, pos, &DrawLibItem );
    if( !Pin )
        return NULL;

    pinpos = Pin->m_Pos;

    if( DrawLibItem == NULL )
        pinpos.y = -pinpos.y;

    else
    {
        int x1 = pinpos.x, y1 = pinpos.y;
        pinpos.x = DrawLibItem->m_Pos.x + DrawLibItem->m_Transform[0][0] * x1
                   + DrawLibItem->m_Transform[0][1] * y1;
        pinpos.y = DrawLibItem->m_Pos.y + DrawLibItem->m_Transform[1][0] * x1
                   + DrawLibItem->m_Transform[1][1] * y1;
    }

    if( (pos.x == pinpos.x) && (pos.y == pinpos.y) )
        return Pin;
    return NULL;
}


/****************************************************************************/
void TestWireForDangling( EDA_DrawLineStruct* DrawRef,
                          WinEDA_SchematicFrame* frame, wxDC* DC )
/****************************************************************************/
{
    DanglingEndHandle* terminal_item;
    bool Sdangstate = TRUE, Edangstate = TRUE;

    for( terminal_item = ItemList; terminal_item != NULL;
         terminal_item = terminal_item->m_Pnext )
    {
        if( terminal_item->m_Item == DrawRef )
            continue;

        if( (DrawRef->m_Start.x == terminal_item->m_Pos.x)
           && (DrawRef->m_Start.y == terminal_item->m_Pos.y) )
            Sdangstate = FALSE;

        if( (DrawRef->m_End.x == terminal_item->m_Pos.x)
           && (DrawRef->m_End.y == terminal_item->m_Pos.y) )
            Edangstate = FALSE;

        if( (Sdangstate == FALSE) && (Edangstate == FALSE) )
            break;
    }

    if( (Sdangstate != DrawRef->m_StartIsDangling)
       || (Edangstate != DrawRef->m_EndIsDangling) )
    {
        if( DC )
            RedrawOneStruct( frame->DrawPanel, DC, DrawRef, g_XorMode );
        DrawRef->m_StartIsDangling = Sdangstate;
        DrawRef->m_EndIsDangling   = Edangstate;
        if( DC )
            RedrawOneStruct( frame->DrawPanel, DC, DrawRef, GR_DEFAULT_DRAWMODE );
    }
}


/********************************************************/
void TestLabelForDangling( DrawTextStruct* label,
                           WinEDA_SchematicFrame* frame, wxDC* DC )
/********************************************************/
{
    DanglingEndHandle* terminal_item;
    bool dangstate = TRUE;

    for( terminal_item = ItemList; terminal_item != NULL;
         terminal_item = terminal_item->m_Pnext )
    {
        if( terminal_item->m_Item == label )
            continue;

        switch( terminal_item->m_Type )
        {
        case PIN_END:
        case LABEL_END:
        case SHEET_LABEL_END:
            if( (label->m_Pos.x == terminal_item->m_Pos.x)
               && (label->m_Pos.y == terminal_item->m_Pos.y) )
                dangstate = FALSE;
            break;

        case WIRE_START_END:
        case BUS_START_END:
            dangstate = !SegmentIntersect( terminal_item->m_Pos.x,
                                           terminal_item->m_Pos.y,
                                           terminal_item->m_Pnext->m_Pos.x,
                                           terminal_item->m_Pnext->m_Pos.y,
                                           label->m_Pos.x, label->m_Pos.y );
            terminal_item = terminal_item->m_Pnext;
            break;

        case UNKNOWN:
        case JUNCTION_END:
        case ENTRY_END:
        case WIRE_END_END:
        case BUS_END_END:
            break;
        }

        if( dangstate == FALSE )
            break;
    }

    if( dangstate != label->m_IsDangling )
    {
        if( DC )
            RedrawOneStruct( frame->DrawPanel, DC, label, g_XorMode );
        label->m_IsDangling = dangstate;
        if( DC )
            RedrawOneStruct( frame->DrawPanel, DC, label, GR_DEFAULT_DRAWMODE );
    }
}


/****************************************************/
wxPoint ReturnPinPhysicalPosition( LibDrawPin*             Pin,
                                   EDA_SchComponentStruct* DrawLibItem )
/****************************************************/

/* Retourne la position physique de la pin, qui dépend de l'orientation
 *  du composant */
{
    wxPoint PinPos = Pin->m_Pos;

    if( DrawLibItem == NULL )
        PinPos.y = -PinPos.y;

    else
    {
        int x = Pin->m_Pos.x, y = Pin->m_Pos.y;
        PinPos.x = DrawLibItem->m_Pos.x + DrawLibItem->m_Transform[0][0] * x
                   + DrawLibItem->m_Transform[0][1] * y;
        PinPos.y = DrawLibItem->m_Pos.y + DrawLibItem->m_Transform[1][0] * x
                   + DrawLibItem->m_Transform[1][1] * y;
    }

    return PinPos;
}


/***********************************************************/
DanglingEndHandle* RebuildEndList( EDA_BaseStruct* DrawList )
/***********************************************************/
{
    DanglingEndHandle* StartList = NULL, * item, * lastitem = NULL;
    EDA_BaseStruct* DrawItem;

    for( DrawItem = DrawList; DrawItem != NULL; DrawItem = DrawItem->Pnext )
    {
        switch( DrawItem->Type() )
        {
        case DRAW_LABEL_STRUCT_TYPE:
            break;

        case DRAW_GLOBAL_LABEL_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (DrawGlobalLabelStruct*) DrawItem )
            item = new DanglingEndHandle( LABEL_END );

            item->m_Item = DrawItem;
            item->m_Pos  = STRUCT->m_Pos;
            if( lastitem )
                lastitem->m_Pnext = item;
            else
                StartList = item;
            lastitem = item;
            break;

        case DRAW_SEGMENT_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (EDA_DrawLineStruct*) DrawItem )
            if( STRUCT->m_Layer == LAYER_NOTES )
                break;
            if( (STRUCT->m_Layer == LAYER_BUS) || (STRUCT->m_Layer == LAYER_WIRE) )
            {
                item = new DanglingEndHandle( (STRUCT->m_Layer == LAYER_BUS) ?
                                             BUS_START_END : WIRE_START_END );

                item->m_Item = DrawItem;
                item->m_Pos  = STRUCT->m_Start;
                if( lastitem )
                    lastitem->m_Pnext = item;
                else
                    StartList = item;
                lastitem = item;
                item = new DanglingEndHandle( (STRUCT->m_Layer == LAYER_BUS) ?
                                             BUS_END_END : WIRE_END_END );

                item->m_Item      = DrawItem;
                item->m_Pos       = STRUCT->m_End;
                lastitem->m_Pnext = item;
                lastitem = item;
            }
            break;

        case DRAW_JUNCTION_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (DrawJunctionStruct*) DrawItem )
            item = new DanglingEndHandle( JUNCTION_END );

            item->m_Item = DrawItem;
            item->m_Pos  = STRUCT->m_Pos;
            if( lastitem )
                lastitem->m_Pnext = item;
            else
                StartList = item;
            lastitem = item;
            break;

        case DRAW_BUSENTRY_STRUCT_TYPE:
                #undef STRUCT
                #define STRUCT ( (DrawBusEntryStruct*) DrawItem )
            item = new DanglingEndHandle( ENTRY_END );

            item->m_Item = DrawItem;
            item->m_Pos  = STRUCT->m_Pos;
            if( lastitem )
                lastitem->m_Pnext = item;
            else
                StartList = item;
            lastitem = item;
            item = new DanglingEndHandle( ENTRY_END );

            item->m_Item      = DrawItem;
            item->m_Pos       = STRUCT->m_End();
            lastitem->m_Pnext = item;
            lastitem = item;
            break;

        case DRAW_LIB_ITEM_STRUCT_TYPE:
        {
            #undef STRUCT
            #define STRUCT ( (EDA_SchComponentStruct*) DrawItem )
            EDA_LibComponentStruct* Entry;
            Entry = FindLibPart( STRUCT->m_ChipName, wxEmptyString, FIND_ROOT );
            if( Entry == NULL )
                break;
            
            LibEDA_BaseStruct* DrawLibItem = Entry->m_Drawings;
            for( ; DrawLibItem != NULL; DrawLibItem = DrawLibItem->Next() )
            {
                if( DrawLibItem->Type() != COMPONENT_PIN_DRAW_TYPE )
                    continue;

                LibDrawPin* Pin = (LibDrawPin*) DrawLibItem;

                if( Pin->m_Unit && DrawLibItem->m_Unit
                   && (DrawLibItem->m_Unit != Pin->m_Unit) )
                    continue;

                if( Pin->m_Convert && DrawLibItem->m_Convert
                   && (DrawLibItem->m_Convert != Pin->m_Convert) )
                    continue;

                item = new DanglingEndHandle( PIN_END );

                item->m_Item = Pin;
                item->m_Pos  = ReturnPinPhysicalPosition( Pin, STRUCT );
                if( lastitem )
                    lastitem->m_Pnext = item;
                else
                    StartList = item;
                lastitem = item;
            }

            break;
        }

        case DRAW_SHEET_STRUCT_TYPE:
        {
            #undef STRUCT
            #define STRUCT ( (DrawSheetStruct*) DrawItem )
            DrawSheetLabelStruct* pinsheet = STRUCT->m_Label;
            while( pinsheet )
            {
                item = new DanglingEndHandle( SHEET_LABEL_END );

                item->m_Item = pinsheet;
                item->m_Pos  = pinsheet->m_Pos;
                if( lastitem )
                    lastitem->m_Pnext = item;
                else
                    StartList = item;
                lastitem = item;
                pinsheet = (DrawSheetLabelStruct*) pinsheet->Pnext;
            }

            break;
        }
        
        default:
            ;
        }
    }

    return StartList;
}
