/*********************************************************/
/* Modules de creations de Traits, Wires, Bus, Junctions */
/*********************************************************/

#include "fctsys.h"
#include "gr_basic.h"

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

#include "protos.h"


/* Routines Locales */
static void Polyline_in_Ghost( WinEDA_DrawPanel* panel, wxDC* DC, bool erase );
static void Segment_in_Ghost( WinEDA_DrawPanel* panel, wxDC* DC, bool erase );
static void ExitTrace( WinEDA_DrawFrame* frame, wxDC* DC );
static bool IsTerminalPoint( SCH_SCREEN* screen, const wxPoint& pos, int layer );


/*************************************************************/
void WinEDA_SchematicFrame::BeginSegment( wxDC* DC, int type )
/*************************************************************/

/* Routine de Trace de segments ( WIRES, BUS ) pour lesquels chaque segment
 *  est une structure.
 */
{
    DrawSegmentStruct* oldsegment, * newsegment;
    wxPoint            pos = GetScreen()->m_Curseur;

    if( GetScreen()->GetCurItem()
       && (GetScreen()->GetCurItem()->m_Flags == 0) )
        GetScreen()->SetCurItem( NULL );

    if( GetScreen()->GetCurItem() )
    {
        switch( GetScreen()->GetCurItem()->Type() )
        {
        case DRAW_SEGMENT_STRUCT_TYPE:
        case DRAW_POLYLINE_STRUCT_TYPE:
            break;

        default:
            return;
        }
    }

    oldsegment = newsegment =
                     (DrawSegmentStruct*) GetScreen()->GetCurItem();

    if( !newsegment )  /* 1er point : creation de la 1ere structure */
    {
        switch( type )
        {
        default:
            newsegment = new DrawSegmentStruct( pos, LAYER_NOTES );
            break;

        case LAYER_WIRE:
            newsegment = new DrawSegmentStruct( pos, LAYER_WIRE );
            if( LocatePinEnd( GetScreen()->EEDrawList, pos ) )
                newsegment->m_StartIsDangling = FALSE;
            break;

        case LAYER_BUS:
            newsegment = new DrawSegmentStruct( pos, LAYER_BUS );
            break;
        }

        newsegment->m_Flags = IS_NEW;
        GetScreen()->SetCurItem( newsegment );
        GetScreen()->ManageCurseur = Segment_in_Ghost;
        GetScreen()->ForceCloseManageCurseur = ExitTrace;
        g_ItemToRepeat = NULL;
    }
    else    /* Trace en cours: Placement d'un point supplementaire */
    {
        if( (oldsegment->m_Start.x == oldsegment->m_End.x)
           && (oldsegment->m_Start.y == oldsegment->m_End.y) )  /* Structure inutile */
            return;
        GetScreen()->ManageCurseur( DrawPanel, DC, FALSE );
        oldsegment->m_EndIsDangling = FALSE;

        /* Creation du segment suivant ou fin de tracé si point sur pin, jonction ...*/
        if( IsTerminalPoint( GetScreen(), oldsegment->m_End, oldsegment->m_Layer ) )
        {
            EndSegment( DC ); return;
        }

        /* Placement en liste generale */
        oldsegment->Pnext = GetScreen()->EEDrawList;
        g_ItemToRepeat    = GetScreen()->EEDrawList = oldsegment;
        GetScreen()->CursorOff( DrawPanel, DC );    // Erase schematic cursor
        RedrawOneStruct( DrawPanel, DC, oldsegment, GR_DEFAULT_DRAWMODE );
        GetScreen()->CursorOn( DrawPanel, DC );     // Display schematic cursor

        /* Creation du segment suivant */
        newsegment = oldsegment->GenCopy();
        newsegment->m_Start = oldsegment->m_End;
        newsegment->m_End   = pos;
        oldsegment->m_Flags = 0;
        newsegment->m_Flags = IS_NEW;
        GetScreen()->SetCurItem( newsegment );
        GetScreen()->ManageCurseur( DrawPanel, DC, FALSE );
        newsegment->m_StartIsDangling = FALSE;
        newsegment->m_EndIsDangling   = TRUE;
    }
}


/*************************************************************/
/*	 Routine de fin de trace d'une struct segment (Wire, Bus */
/*************************************************************/
void WinEDA_SchematicFrame::EndSegment( wxDC* DC )
{
    DrawSegmentStruct* segment = (DrawSegmentStruct*) GetScreen()->GetCurItem();

    if( GetScreen()->ManageCurseur == NULL )
        return;
    if( segment == NULL )
        return;
    if( (segment->m_Flags & IS_NEW) == 0 )
        return;

    if( (segment->m_Start.x == segment->m_End.x)
       && (segment->m_Start.y == segment->m_End.y) )/* Structure inutile */
    {
        EraseStruct( segment, (SCH_SCREEN*) GetScreen() );
        segment = NULL;
    }
    else
    {
        /* Placement en liste generale */
        GetScreen()->ManageCurseur( DrawPanel, DC, FALSE );
        segment->Pnext   = GetScreen()->EEDrawList;
        g_ItemToRepeat   = GetScreen()->EEDrawList = segment;
        segment->m_Flags = 0;
    }

    /* Fin de trace */
    GetScreen()->ManageCurseur = NULL;
    GetScreen()->ForceCloseManageCurseur = NULL;
    GetScreen()->SetCurItem( NULL );

    TestDanglingEnds( GetScreen()->EEDrawList, DC );
    SetFlagModify( GetScreen() );
    if( segment )
    {
        GetScreen()->CursorOff( DrawPanel, DC );    // Erase schematic cursor
        RedrawOneStruct( DrawPanel, DC, segment, GR_DEFAULT_DRAWMODE );
        GetScreen()->CursorOn( DrawPanel, DC );     // Display schematic cursor
    }
}


/****************************************************************************/
static void Segment_in_Ghost( WinEDA_DrawPanel* panel, wxDC* DC, bool erase )
/****************************************************************************/

/*  Dessin du Segment Fantome lors des deplacements du curseur
 */
{
    DrawSegmentStruct* segment =
        (DrawSegmentStruct*) panel->m_Parent->GetScreen()->GetCurItem();
    wxPoint            endpos;
    int color;

    if( segment == NULL )
        return;

    color = ReturnLayerColor( segment->m_Layer ) ^ HIGHT_LIGHT_FLAG;

    endpos = panel->m_Parent->GetScreen()->m_Curseur;

    if( g_HVLines ) /* Coerce the line to vertical or horizontal one: */
    {
        if( ABS( endpos.x - segment->m_Start.x ) < ABS( endpos.y - segment->m_Start.y ) )
            endpos.x = segment->m_Start.x;
        else
            endpos.y = segment->m_Start.y;
    }

    if( erase )     // Redraw if segment lengtht != 0
    {
        if( (segment->m_Start.x != segment->m_End.x)
           || (segment->m_Start.y != segment->m_End.y) )
            RedrawOneStruct( panel, DC, segment, XOR_MODE, color );
    }
    segment->m_End = endpos;

    // Redraw if segment lengtht != 0

    if( (segment->m_Start.x != segment->m_End.x)
       || (segment->m_Start.y != segment->m_End.y) )
        RedrawOneStruct( panel, DC, segment, XOR_MODE, color );
}


/*****************************************************************************/
static void Polyline_in_Ghost( WinEDA_DrawPanel* panel, wxDC* DC, bool erase )
/*****************************************************************************/

/*  Dessin du du Polyline Fantome lors des deplacements du curseur
 */
{
    DrawPolylineStruct* NewPoly =
        (DrawPolylineStruct*) panel->m_Parent->GetScreen()->GetCurItem();
    int color;
    wxPoint             endpos;

    endpos = panel->m_Parent->GetScreen()->m_Curseur;
    color  = ReturnLayerColor( NewPoly->m_Layer );

    GRSetDrawMode( DC, XOR_MODE );

    if( g_HVLines )
    {
        /* Coerce the line to vertical or horizontal one: */
        if( ABS( endpos.x - NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 2] ) <
           ABS( endpos.y - NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 1] ) )
            endpos.x = NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 2];
        else
            endpos.y = NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 1];
    }

    NewPoly->m_NumOfPoints++;
    if( erase )
        RedrawOneStruct( panel, DC, NewPoly, XOR_MODE, color );

    NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 2] = endpos.x;
    NewPoly->m_Points[NewPoly->m_NumOfPoints * 2 - 1] = endpos.y;
    RedrawOneStruct( panel, DC, NewPoly, XOR_MODE, color );
    NewPoly->m_NumOfPoints--;
}


/**********************************************************/
void WinEDA_SchematicFrame::DeleteCurrentSegment( wxDC* DC )
/**********************************************************/

/*
 *  Routine effacant le dernier trait trace, ou l'element pointe par la souris
 */
{
    g_ItemToRepeat = NULL;

    if( (GetScreen()->GetCurItem() == NULL)
       || ( (GetScreen()->GetCurItem()->m_Flags & IS_NEW) == 0 ) )
    {
        return;
    }

    /* Trace en cours: annulation */
    if( GetScreen()->GetCurItem()->Type() == DRAW_POLYLINE_STRUCT_TYPE )
    {
        Polyline_in_Ghost( DrawPanel, DC, FALSE ); /* Effacement du trace en cours */
    }
    else
    {
        Segment_in_Ghost( DrawPanel, DC, FALSE ); /* Effacement du trace en cours */
    }

    EraseStruct( GetScreen()->GetCurItem(), GetScreen() );
    GetScreen()->ManageCurseur = NULL;
    GetScreen()->SetCurItem( NULL );
}


/***************************************************************************/
EDA_BaseStruct* WinEDA_SchematicFrame::CreateNewJunctionStruct( wxDC* DC )
/***************************************************************************/

/* Routine to create new connection struct.
 */
{
    DrawJunctionStruct* NewConnect;

    NewConnect = new DrawJunctionStruct( GetScreen()->m_Curseur );

    g_ItemToRepeat = NewConnect;

    GetScreen()->CursorOff( DrawPanel, DC );    // Erase schematic cursor
    RedrawOneStruct( DrawPanel, DC, NewConnect, GR_DEFAULT_DRAWMODE );
    GetScreen()->CursorOn( DrawPanel, DC );     // Display schematic cursor

    NewConnect->Pnext = GetScreen()->EEDrawList;
    GetScreen()->EEDrawList = NewConnect;
    SetFlagModify( GetScreen() );
    return NewConnect;
}


/*************************************************************************/
EDA_BaseStruct* WinEDA_SchematicFrame::CreateNewNoConnectStruct( wxDC* DC )
/*************************************************************************/

/*Routine to create new NoConnect struct. ( Symbole de Non Connexion)
 */
{
    DrawNoConnectStruct* NewNoConnect;

    NewNoConnect   = new DrawNoConnectStruct( GetScreen()->m_Curseur );
    g_ItemToRepeat = NewNoConnect;

    GetScreen()->CursorOff( DrawPanel, DC );    // Erase schematic cursor
    RedrawOneStruct( DrawPanel, DC, NewNoConnect, GR_DEFAULT_DRAWMODE );
    GetScreen()->CursorOn( DrawPanel, DC );     // Display schematic cursor

    NewNoConnect->Pnext     = GetScreen()->EEDrawList;
    GetScreen()->EEDrawList = NewNoConnect;
    SetFlagModify( GetScreen() );
    return NewNoConnect;
}


/**********************************************************/
static void ExitTrace( WinEDA_DrawFrame* frame, wxDC* DC )
/**********************************************************/
/* Routine de sortie des menus de trace */
{
    BASE_SCREEN* Screen = frame->GetScreen();

    if( Screen->GetCurItem() )  /* trace en cours */
    {
        Screen->ManageCurseur( frame->DrawPanel, DC, FALSE );
        Screen->ManageCurseur = NULL;
        Screen->ForceCloseManageCurseur = NULL;
        EraseStruct( Screen->GetCurItem(), (SCH_SCREEN*) Screen );
        Screen->SetCurItem( NULL );
        return;
    }
    else
        g_ItemToRepeat = NULL;  // Fin de commande generale
}


/***************************************************/
void WinEDA_SchematicFrame::RepeatDrawItem( wxDC* DC )
/***************************************************/

/* Routine de recopie du dernier element dessine
 *  Les elements duplicables sont
 *      fils, bus, traits, textes, labels
 *      Les labels termines par un nombre seront incrementes
 */
{
    char Line[256];
    int  ox = 0, oy = 0;

    if( g_ItemToRepeat == NULL )
        return;

    switch( g_ItemToRepeat->Type() )
    {
    case DRAW_JUNCTION_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawJunctionStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        break;

    case DRAW_NOCONNECT_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawNoConnectStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        break;

    case DRAW_TEXT_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawTextStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        /*** Increment du numero de label ***/
        strcpy( Line, STRUCT->GetText() );
        IncrementLabelMember( Line );
        STRUCT->m_Text = Line;
        break;


    case DRAW_LABEL_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawLabelStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        /*** Increment du numero de label ***/
        strcpy( Line, STRUCT->GetText() );
        IncrementLabelMember( Line );
        STRUCT->m_Text = Line;
        break;


    case DRAW_GLOBAL_LABEL_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawGlobalLabelStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        /*** Increment du numero de label ***/
        strcpy( Line, STRUCT->GetText() );
        IncrementLabelMember( Line );
        STRUCT->m_Text = Line;
        break;

    case DRAW_SEGMENT_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawSegmentStruct*) g_ItemToRepeat )
        g_ItemToRepeat     = STRUCT->GenCopy();
        STRUCT->m_Start.x += g_RepeatStep.x; ox = STRUCT->m_Start.x;
        STRUCT->m_Start.y += g_RepeatStep.y; oy = STRUCT->m_Start.y;
        STRUCT->m_End.x   += g_RepeatStep.x;
        STRUCT->m_End.y   += g_RepeatStep.y;
        break;

    case DRAW_RACCORD_STRUCT_TYPE:
            #undef STRUCT
            #define STRUCT ( (DrawRaccordStruct*) g_ItemToRepeat )
        g_ItemToRepeat   = STRUCT->GenCopy();
        STRUCT->m_Pos.x += g_RepeatStep.x; ox = STRUCT->m_Pos.x;
        STRUCT->m_Pos.y += g_RepeatStep.y; oy = STRUCT->m_Pos.y;
        break;

    default:
        g_ItemToRepeat = NULL;
        DisplayError( this, "Repeat Type Error", 10 );
        break;
    }

    if( g_ItemToRepeat )
    {
        g_ItemToRepeat->Pnext   = GetScreen()->EEDrawList;
        GetScreen()->EEDrawList = g_ItemToRepeat;
        TestDanglingEnds( GetScreen()->EEDrawList, NULL );
        RedrawOneStruct( DrawPanel, DC, g_ItemToRepeat, GR_DEFAULT_DRAWMODE );

//		GetScreen()->Curseur.x = ox; GetScreen()->Curseur.x = oy;
//		GRMouseWarp(DrawPanel, DrawPanel->CursorScreenPosition() );
    }
}


/******************************************/
void IncrementLabelMember( char* Line )
/******************************************/

/* Routine incrementant les labels, c'est a dire pour les textes finissant
 *  par un nombre, ajoutant <RepeatDeltaLabel> a ce nombre
 */
{
    char* strnum;
    int   ii;

    strnum = Line + strlen( Line ) - 1;
    if( !isdigit( *strnum ) )
        return;

    while( (strnum >= Line) && isdigit( *strnum ) )
        strnum--;

    strnum++;   /* pointe le debut de la chaine des digits */
    ii = atoi( strnum ) + g_RepeatDeltaLabel;
    sprintf( strnum, "%d", ii );
}


/***************************************************************************/
static bool IsTerminalPoint( SCH_SCREEN* screen, const wxPoint& pos, int layer )
/***************************************************************************/

/* Returne TRUE si pos est un point possible pour terminer automatiquement un
 *  segment, c'est a dire pour
 *  - type WIRE, si il y a
 *      - une jonction
 *      - ou une pin
 *      - ou une extrémité unique de fil
 * 
 *  - type BUS, si il y a
 *      - ou une extrémité unique de BUS
 */
{
    EDA_BaseStruct*       item;
    LibDrawPin*           pin;
    DrawLibItemStruct*    LibItem = NULL;
    DrawSheetLabelStruct* pinsheet;
    wxPoint itempos;

    switch( layer )
    {
    case LAYER_BUS:
        item = PickStruct( screen, BUSITEM );
        if( item )
            return TRUE;
        pinsheet = LocateAnyPinSheet( pos, screen->EEDrawList );
        if( pinsheet && IsBusLabel( pinsheet->GetText() ) )
        {
            itempos = pinsheet->m_Pos;
            if( (itempos.x == pos.x) && (itempos.y == pos.y) )
                return TRUE;
        }
        break;

    case LAYER_NOTES:
        item = PickStruct( screen, DRAWITEM );
        if( item )
            return TRUE;
        break;

    case LAYER_WIRE:
        item = PickStruct( screen, RACCORDITEM | JUNCTIONITEM );
        if( item )
            return TRUE;

        pin = LocateAnyPin( screen->EEDrawList, pos, &LibItem );
        if( pin && LibItem )
        {
            // calcul de la position exacte du point de connexion de la pin,
            // selon orientation du composant:
            itempos    = LibItem->GetScreenCoord( pin->m_Pos );
            itempos.x += LibItem->m_Pos.x;
            itempos.y += LibItem->m_Pos.y;
            if( (itempos.x == pos.x) && (itempos.y == pos.y) )
                return TRUE;
        }

        item = PickStruct( screen, WIREITEM );
        if( item )
            return TRUE;

        item = PickStruct( screen, LABELITEM );
        if( item && (item->Type() != DRAW_TEXT_STRUCT_TYPE)
           && ( ( (DrawGlobalLabelStruct*) item )->m_Pos.x == pos.x )
           && ( ( (DrawGlobalLabelStruct*) item )->m_Pos.y == pos.y ) )
            return TRUE;

        pinsheet = LocateAnyPinSheet( pos, screen->EEDrawList );
        if( pinsheet && !IsBusLabel( pinsheet->GetText() ) )
        {
            itempos = pinsheet->m_Pos;
            if( (itempos.x == pos.x) && (itempos.y == pos.y) )
                return TRUE;
        }

        break;

    default:
        break;
    }

    return FALSE;
}
