/****************/
/* Edit traces.	*/
/****************/

#include "fctsys.h"
#include "class_drawpanel.h"

#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "class_board_design_settings.h"
#include "colors_selection.h"

#include "drc_stuff.h"
#include "trigo.h"

#include "protos.h"


static void Abort_Create_Track( EDA_DRAW_PANEL* panel, wxDC* DC );
void        ShowNewTrackWhenMovingCursor( EDA_DRAW_PANEL* aPanel, wxDC* aDC,
                                          const wxPoint& aPosition, bool aErase );
static void ComputeBreakPoint( TRACK* track, int n, wxPoint end );
static void DeleteNullTrackSegments( BOARD* pcb, DLIST<TRACK>& aTrackList );
static void EnsureEndTrackOnPad( D_PAD* Pad );

static PICKED_ITEMS_LIST s_ItemsListPicker;


/* Routine to cancel the route if a track is being drawn, or exit the application EDITRACK.
 */
static void Abort_Create_Track( EDA_DRAW_PANEL* Panel, wxDC* DC )
{
    PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) Panel->GetParent();
    BOARD * pcb = frame->GetBoard();
    TRACK*          track = (TRACK*) frame->GetCurItem();

    if( track && ( track->Type()==TYPE_VIA || track->Type()==TYPE_TRACK ) )
    {
        /* Erase the current drawing */
        ShowNewTrackWhenMovingCursor( Panel, DC, wxDefaultPosition, false );

        if( pcb->IsHightLightNetON() )
            frame->High_Light( DC );

        pcb->PopHightLight();

        if( pcb->IsHightLightNetON() )
            pcb->DrawHighLight( Panel, DC, pcb->GetHightLightNetCode() );

        frame->MsgPanel->EraseMsgBox();

        // Undo pending changes (mainly a lock point creation) and clear the
        // undo picker list:
        frame->PutDataInPreviousState( &s_ItemsListPicker, false, false );
        s_ItemsListPicker.ClearListAndDeleteItems();

        // Delete current (new) track
        g_CurrentTrackList.DeleteAll();
    }

    frame->SetCurItem( NULL );
}


/*
 * Function Begin_Route
 * Starts a new track and/or establish of a new track point.
 *
 * For a new track:
 * - Search the netname of the new track from the starting point
 * if it is on a pad or an existing track
 * - Highlight all this net
 * If a track is in progress:
 * - Call DRC
 * - If DRC is OK: finish the track segment and starts a new one.
 * param aTrack = the current track segment, or NULL to start a new track
 * param aDC = the current device context
 * return a pointer to the new track segment or null if not created (DRC error)
 */
TRACK* PCB_EDIT_FRAME::Begin_Route( TRACK* aTrack, wxDC* DC )
{
    D_PAD*      pt_pad = NULL;
    TRACK*      TrackOnStartPoint = NULL;
    int         layerMask = g_TabOneLayerMask[( (PCB_SCREEN*) GetScreen() )->m_Active_Layer];
    BOARD_ITEM* LockPoint;
    wxPoint     pos = GetScreen()->GetCrossHairPosition();

    if( aTrack == NULL )  /* Starting a new track  */
    {
        DrawPanel->SetMouseCapture( ShowNewTrackWhenMovingCursor, Abort_Create_Track );

        // Prepare the undo command info
        s_ItemsListPicker.ClearListAndDeleteItems();  // Should not be necessary, but...

        GetBoard()->PushHightLight();

        // erase old highlight
        if( GetBoard()->IsHightLightNetON() )
            High_Light( DC );

        g_CurrentTrackList.PushBack( new TRACK( GetBoard() ) );
        g_CurrentTrackSegment->m_Flags = IS_NEW;

        GetBoard()->SetHightLightNet(0);

        // Search for a starting point of the new track, a track or pad
        LockPoint = LocateLockPoint( GetBoard(), pos, layerMask );

        if( LockPoint ) // An item (pad or track) is found
        {
            if( LockPoint->Type() == TYPE_PAD )
            {
                pt_pad = (D_PAD*) LockPoint;

                /* A pad is found: put the starting point on pad centre */
                pos = pt_pad->m_Pos;
                GetBoard()->SetHightLightNet( pt_pad->GetNet() );
            }
            else /* A track segment is found */
            {
                TrackOnStartPoint    = (TRACK*) LockPoint;
                GetBoard()->SetHightLightNet( TrackOnStartPoint->GetNet() );
                CreateLockPoint( GetBoard(), pos, TrackOnStartPoint, &s_ItemsListPicker );
            }
        }
        else    // no starting point, but a filled zone area can exist. This is
                // also a good starting point.
        {
            ZONE_CONTAINER* zone;
            zone = GetBoard()->HitTestForAnyFilledArea( pos, GetScreen()-> m_Active_Layer );

            if( zone )
                GetBoard()->SetHightLightNet( zone->GetNet() );
        }

        D( g_CurrentTrackList.VerifyListIntegrity(); );

        build_ratsnest_pad( LockPoint, wxPoint( 0, 0 ), true );

        D( g_CurrentTrackList.VerifyListIntegrity(); );

        GetBoard()->HightLightON();
        GetBoard()->DrawHighLight( DrawPanel, DC, GetBoard()->GetHightLightNetCode() );

        // Display info about track Net class, and init track and vias sizes:
        g_CurrentTrackSegment->SetNet( GetBoard()->GetHightLightNetCode() );
        GetBoard()->SetCurrentNetClass( g_CurrentTrackSegment->GetNetClassName() );

        g_CurrentTrackSegment->SetLayer( GetScreen()->m_Active_Layer );
        g_CurrentTrackSegment->m_Width = GetBoard()->GetCurrentTrackWidth();

        if( GetBoard()->GetBoardDesignSettings()->m_UseConnectedTrackWidth )
        {
            if( TrackOnStartPoint && TrackOnStartPoint->Type() == TYPE_TRACK )
                g_CurrentTrackSegment->m_Width = TrackOnStartPoint->m_Width;
        }

        g_CurrentTrackSegment->m_Start = pos;
        g_CurrentTrackSegment->m_End   = pos;

        if( pt_pad )
        {
            g_CurrentTrackSegment->start = pt_pad;
            g_CurrentTrackSegment->SetState( BEGIN_ONPAD, ON );
        }
        else
        {
            g_CurrentTrackSegment->start = TrackOnStartPoint;
        }

        if( g_TwoSegmentTrackBuild )
        {
            // Create 2nd segment
            g_CurrentTrackList.PushBack( g_CurrentTrackSegment->Copy() );

            D( g_CurrentTrackList.VerifyListIntegrity(); );

            g_CurrentTrackSegment->start = g_FirstTrackSegment;
            g_FirstTrackSegment->end     = g_CurrentTrackSegment;

            g_FirstTrackSegment->SetState( BEGIN_ONPAD | END_ONPAD, OFF );
        }

        D( g_CurrentTrackList.VerifyListIntegrity(); );

        g_CurrentTrackSegment->DisplayInfoBase( this );
        SetCurItem( g_CurrentTrackSegment, false );
        DrawPanel->m_mouseCaptureCallback( DrawPanel, DC, wxDefaultPosition, false );

        if( Drc_On )
        {
            if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
            {
                return g_CurrentTrackSegment;
            }
        }
    }
    else   // Track in progress : segment coordinates are updated by ShowNewTrackWhenMovingCursor.
    {
        /* Tst for a D.R.C. error: */
        if( Drc_On )
        {
            if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
                return NULL;

            // We must handle 2 segments
            if( g_TwoSegmentTrackBuild && g_CurrentTrackSegment->Back() )
            {
                if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment->Back(), GetBoard()->m_Track ) )
                    return NULL;
            }
        }

        /* Current track is Ok: current segment is kept, and a new one is
         * created unless the current segment is null, or 2 last are null
         * if a 2 segments track build.
         */
        bool CanCreateNewSegment = true;

        if( !g_TwoSegmentTrackBuild && g_CurrentTrackSegment->IsNull() )
            CanCreateNewSegment = false;

        if( g_TwoSegmentTrackBuild && g_CurrentTrackSegment->IsNull()
           && g_CurrentTrackSegment->Back()
           && g_CurrentTrackSegment->Back()->IsNull() )
            CanCreateNewSegment = false;

        if( CanCreateNewSegment )
        {
            /* Erase old track on screen */
            D( g_CurrentTrackList.VerifyListIntegrity(); );

            ShowNewTrackWhenMovingCursor( DrawPanel, DC, wxDefaultPosition, false );

            D( g_CurrentTrackList.VerifyListIntegrity(); );

            if( g_Raccord_45_Auto )
            {
                Add_45_degrees_Segment( DC );
            }

            TRACK* oneBeforeLatest = g_CurrentTrackSegment;

            TRACK* newTrack = g_CurrentTrackSegment->Copy();
            g_CurrentTrackList.PushBack( newTrack );
            newTrack->m_Flags = IS_NEW;

            newTrack->SetState( BEGIN_ONPAD | END_ONPAD, OFF );

            oneBeforeLatest->end = Locate_Pad_Connecte( GetBoard(), oneBeforeLatest, END );

            if( oneBeforeLatest->end )
            {
                oneBeforeLatest->SetState( END_ONPAD, ON );
                newTrack->SetState( BEGIN_ONPAD, ON );
            }

            newTrack->start = oneBeforeLatest->end;

            D( g_CurrentTrackList.VerifyListIntegrity(); );

            newTrack->m_Start = newTrack->m_End;

            newTrack->SetLayer( ( (PCB_SCREEN*) GetScreen() )->m_Active_Layer );

            if( !GetBoard()->GetBoardDesignSettings()->m_UseConnectedTrackWidth )
            {
                newTrack->m_Width = GetBoard()->GetCurrentTrackWidth();
            }

            D( g_CurrentTrackList.VerifyListIntegrity(); );

            /* Show the new position */
            ShowNewTrackWhenMovingCursor( DrawPanel, DC, wxDefaultPosition, false );
        }
    }

    SetCurItem( g_CurrentTrackSegment, false );
    return g_CurrentTrackSegment;
}


/* Add a track segment between 2 tracks segments if these 2 segments
 * make a 90 deg angle, in order to have 45 deg track segments
 * Its only works on horizontal or vertical segments.
 *
 * Input: pointer to the current segment being created
 * Assume that the previous segment is the one that has been
 * previously created
 * Returns:
 *   true if ok
 *   false if not
 */
bool PCB_EDIT_FRAME::Add_45_degrees_Segment( wxDC* DC )
{
    int dx0, dy0, dx1, dy1;

    if( g_CurrentTrackList.GetCount() < 2 )
        return false;                         /* There must be 2 segments. */

    TRACK* curTrack  = g_CurrentTrackSegment;
    TRACK* prevTrack = curTrack->Back();

    // Test if we have 2 consecutive track segments ( not via ) to connect.
    if( curTrack->Type() != TYPE_TRACK || prevTrack->Type() != TYPE_TRACK )
    {
        return false;
    }

    int segm_step_45 = wxRound( GetScreen()->GetGridSize().x / 2 );

    if( segm_step_45 < ( curTrack->m_Width * 2 ) )
        segm_step_45 = curTrack->m_Width * 2;

    // Test if the segments are horizontal or vertical.
    dx0 = prevTrack->m_End.x - prevTrack->m_Start.x;
    dy0 = prevTrack->m_End.y - prevTrack->m_Start.y;

    dx1 = curTrack->m_End.x - curTrack->m_Start.x;
    dy1 = curTrack->m_End.y - curTrack->m_Start.y;

    // Segments must be of sufficient length.
    if( MAX( abs( dx0 ), abs( dy0 ) ) < ( segm_step_45 * 2 ) )
        return false;

    if( MAX( abs( dx1 ), abs( dy1 ) ) < ( segm_step_45 * 2 ) )
        return false;

    /* Create a new segment and connect it with the previous 2 segments. */
    TRACK* newTrack = curTrack->Copy();

    newTrack->m_Start = prevTrack->m_End;
    newTrack->m_End   = curTrack->m_Start;

    if( dx0 == 0 )          // Previous segment is Vertical
    {
        if( dy1 != 0 )      // 2 segments are not 90 degrees.
        {
            delete newTrack;
            return false;
        }

        /* Calculate coordinates the connection point.
         * The new segment connects the 1st vertical segment and the 2nd
         * horizontal segment.
         */
        if( dy0 > 0 )
            newTrack->m_Start.y -= segm_step_45;
        else
            newTrack->m_Start.y += segm_step_45;

        if( dx1 > 0 )
            newTrack->m_End.x += segm_step_45;
        else
            newTrack->m_End.x -= segm_step_45;

        if( Drc_On && BAD_DRC == m_drc->Drc( curTrack, GetBoard()->m_Track ) )
        {
            delete newTrack;
            return false;
        }

        prevTrack->m_End  = newTrack->m_Start;
        curTrack->m_Start = newTrack->m_End;

        g_CurrentTrackList.Insert( newTrack, curTrack );
        return true;
    }

    if( dy0 == 0 )      // Previous segment is horizontal
    {
        if( dx1 != 0 )  // 2 segments are not 90 degrees
        {
            delete newTrack;
            return false;
        }

        /*  Calculate the coordinates of the point of connection:
         * A new segment has been created, connecting segment 1
         * (horizontal) and segment 2 (vertical)
         */
        if( dx0 > 0 )
            newTrack->m_Start.x -= segm_step_45;
        else
            newTrack->m_Start.x += segm_step_45;

        if( dy1 > 0 )
            newTrack->m_End.y += segm_step_45;
        else
            newTrack->m_End.y -= segm_step_45;

        if( Drc_On && BAD_DRC==m_drc->Drc( newTrack, GetBoard()->m_Track ) )
        {
            delete newTrack;
            return false;
        }

        prevTrack->m_End  = newTrack->m_Start;
        curTrack->m_Start = newTrack->m_End;

        g_CurrentTrackList.Insert( newTrack, curTrack );
        return true;
    }

    return false;
}


/*
 * Function End_Route
 * Terminates a track currently being created
 * param aTrack = the current track segment in progress
 * @return true if the track was created, false if not (due to a DRC error)
 */
bool PCB_EDIT_FRAME::End_Route( TRACK* aTrack, wxDC* DC )
{
    int layerMask = g_TabOneLayerMask[( (PCB_SCREEN*) GetScreen() )->m_Active_Layer];

    if( aTrack == NULL )
        return false;

    if( Drc_On && BAD_DRC == m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
        return false;

    /* Saving the coordinate of end point of the trace */
    wxPoint pos = g_CurrentTrackSegment->m_End;

    D( g_CurrentTrackList.VerifyListIntegrity(); );

    if( Begin_Route( aTrack, DC ) == NULL )
        return false;

    ShowNewTrackWhenMovingCursor( DrawPanel, DC, wxDefaultPosition, true );
    ShowNewTrackWhenMovingCursor( DrawPanel, DC, wxDefaultPosition, false );
    trace_ratsnest_pad( DC );

    /* cleanup
     *  if( g_CurrentTrackSegment->Next() != NULL )
     *  {
     *   delete g_CurrentTrackSegment->Next();
     *   g_CurrentTrackSegment->SetNext( NULL );
     *  }
     */

    D( g_CurrentTrackList.VerifyListIntegrity(); );


    /* The track here is non chained to the list of track segments.
     * It must be seen in the area of net
     * As close as possible to the segment base (or end), because
     * This helps to reduce the computing time */

    /* Attaching the end of the track. */
    BOARD_ITEM* LockPoint = LocateLockPoint( GetBoard(), pos, layerMask );

    if( LockPoint ) /* End of trace is on a pad. */
    {
        if( LockPoint->Type() ==  TYPE_PAD )
        {
            EnsureEndTrackOnPad( (D_PAD*) LockPoint );
        }
        else    /* End of is on a different track, it will
                 * possibly create an anchor. */
        {
            TRACK* adr_buf = (TRACK*) LockPoint;
            GetBoard()->SetHightLightNet( adr_buf->GetNet() );

            /* Possible establishment of a hanging point. */
            LockPoint = CreateLockPoint( GetBoard(),
                                         g_CurrentTrackSegment->m_End,
                                         adr_buf,
                                         &s_ItemsListPicker );
        }
    }

    // Delete Null segments:
    DeleteNullTrackSegments( GetBoard(), g_CurrentTrackList );

    // Insert new segments if they exist.  This can be NULL on a double click
    // on the start point
    if( g_FirstTrackSegment != NULL )
    {
        int    netcode    = g_FirstTrackSegment->GetNet();
        TRACK* firstTrack = g_FirstTrackSegment;
        int    newCount   = g_CurrentTrackList.GetCount();

        // Put entire new current segment list in BOARD, and prepare undo
        // command
        TRACK* track;
        TRACK* insertBeforeMe = g_CurrentTrackSegment->GetBestInsertPoint( GetBoard() );

        while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
        {
            ITEM_PICKER picker( track, UR_NEW );
            s_ItemsListPicker.PushItem( picker );
            GetBoard()->m_Track.Insert( track, insertBeforeMe );
        }

        trace_ratsnest_pad( DC );

        DrawTraces( DrawPanel, DC, firstTrack, newCount, GR_OR );

        int i = 0;

        for( track = firstTrack; track && i<newCount; ++i, track = track->Next() )
        {
            track->m_Flags = 0;
            track->SetState( BUSY, OFF );
        }

        // erase the old track, if exists
        if( g_AutoDeleteOldTrack )
        {
            EraseRedundantTrack( DC, firstTrack, newCount, &s_ItemsListPicker );
        }

        SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
        s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more
                                            // owner of picked items
        /* compute the new rastnest */
        test_1_net_connexion( DC, netcode );
        OnModify();
        GetBoard()->DisplayInfo( this );
    }

    wxASSERT( g_FirstTrackSegment == NULL );
    wxASSERT( g_CurrentTrackSegment == NULL );
    wxASSERT( g_CurrentTrackList.GetCount() == 0 );

    if( GetBoard()->IsHightLightNetON() )
        High_Light( DC );

    GetBoard()->PopHightLight();

    if( GetBoard()->IsHightLightNetON() )
        GetBoard()->DrawHighLight( DrawPanel, DC, GetBoard()->GetHightLightNetCode() );

    DrawPanel->SetMouseCapture( NULL, NULL );
    SetCurItem( NULL );

    return true;
}


TRACK* LocateIntrusion( TRACK* listStart, TRACK* aTrack, int aLayer, const wxPoint& aRef )
{
    int     net   = aTrack->GetNet();
    int     width = aTrack->m_Width;

    TRACK*  found = NULL;

    for( TRACK* track = listStart; track; track = track->Next() )
    {
        if( track->Type() == TYPE_TRACK )    // skip vias
        {
            if( track->GetState( BUSY | IS_DELETED ) )
                continue;

            if( aLayer != track->GetLayer() )
                continue;

            if( track->GetNet() == net )
                continue;

            /* TRACK::HitTest */
            int     dist = (width + track->m_Width) / 2 + aTrack->GetClearance( track );

            wxPoint pos = aRef - track->m_Start;
            wxPoint vec = track->m_End - track->m_Start;

            if( !DistanceTest( dist, vec.x, vec.y, pos.x, pos.y ) )
                continue;

            found = track;

            /* prefer intrusions from the side, not the end */
            double tmp = (double) pos.x * vec.x + (double) pos.y * vec.y;

            if( tmp >= 0 && tmp <= (double) vec.x * vec.x + (double) vec.y * vec.y )
                break;
        }
    }

    return found;
}


/**
 * Function PushTrack
 * detects if the mouse is pointing into a conflicting track.
 * In this case, it tries to push the new track out of the conflicting track's
 * clearance zone. This gives us a cheap mechanism for drawing tracks that
 * tightly follow others, independent of grid settings.
 *
 * KNOWN BUGS:
 * - we do the same sort of search and calculation up to three times:
 *   1) we search for magnetic hits (in controle.cpp)
 *   2) we check if there's a DRC violation in the making (also controle.cpp)
 *   3) we try to fix the DRC violation (here)
 * - if we have a magnetic hit and a DRC violation at the same time, we choose
 *   the magnetic hit instead of solving the violation
 * - should locate conflicting tracks also when we're crossing over them
 */
static void PushTrack( EDA_DRAW_PANEL* panel )
{
    PCB_SCREEN* screen = ( (PCB_BASE_FRAME*) (panel->GetParent()) )->GetScreen();
    BOARD*  pcb    = ( (PCB_BASE_FRAME*) (panel->GetParent()) )->GetBoard();
    wxPoint cursor = screen->GetCrossHairPosition();
    wxPoint cv, vec, n;
    TRACK*  track = g_CurrentTrackSegment;
    TRACK*  other;
    double  det;
    int     dist;
    double  f;

    other = LocateIntrusion( pcb->m_Track, track, screen->m_Active_Layer, screen->RefPos( true ) );

    /* are we currently pointing into a conflicting trace ? */
    if( !other )
        return;

    if( other->GetNet() == track->GetNet() )
        return;

    cv  = cursor - other->m_Start;
    vec = other->m_End - other->m_Start;

    det = (double) cv.x * vec.y - (double) cv.y * vec.x;

    /* cursor is right at the center of the old track */
    if( !det )
        return;

    dist = (track->m_Width + 1) / 2 + (other->m_Width + 1) / 2 + track->GetClearance( other ) + 2;

    /*
     * DRC wants >, so +1.
     * We may have a quantization error of 1/sqrt(2), so +1 again.
     */

    /* Vector "n" is perpendicular to "other", pointing towards the cursor. */
    if( det > 0 )
    {
        n.x = vec.y;
        n.y = -vec.x;
    }
    else
    {
        n.x = -vec.y;
        n.y = vec.x;
    }

    f   = dist / hypot( double(n.x), double(n.y) );
    n.x = wxRound( f * n.x );
    n.y = wxRound( f * n.y );

    Project( &track->m_End, cursor, other );
    track->m_End += n;
}


/* Redraw the current track beiing created when the mouse cursor is moved
 */
void ShowNewTrackWhenMovingCursor( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
                                   bool aErase )
{
    D( g_CurrentTrackList.VerifyListIntegrity(); );

    PCB_SCREEN*     screen = (PCB_SCREEN*) aPanel->GetScreen();
    PCB_BASE_FRAME* frame  = (PCB_BASE_FRAME*) aPanel->GetParent();

    bool      Track_fill_copy = DisplayOpt.DisplayPcbTrackFill;
    DisplayOpt.DisplayPcbTrackFill = true;
    int       showTrackClearanceMode = DisplayOpt.ShowTrackClearanceMode;

    if ( g_FirstTrackSegment == NULL )
        return;

    NETCLASS* netclass = g_FirstTrackSegment->GetNetClass();

    if( showTrackClearanceMode != DO_NOT_SHOW_CLEARANCE )
        DisplayOpt.ShowTrackClearanceMode = SHOW_CLEARANCE_ALWAYS;

#ifndef USE_WX_OVERLAY
    /* Erase old track */
    if( aErase )
    {
        DrawTraces( aPanel, aDC, g_FirstTrackSegment, g_CurrentTrackList.GetCount(), GR_XOR );

        frame->trace_ratsnest_pad( aDC );

        if( showTrackClearanceMode >= SHOW_CLEARANCE_NEW_TRACKS_AND_VIA_AREAS )
        {
            int color = g_ColorsSettings.GetLayerColor(g_CurrentTrackSegment->GetLayer());

            GRCircle( &aPanel->m_ClipBox, aDC, g_CurrentTrackSegment->m_End.x,
                      g_CurrentTrackSegment->m_End.y,
                      ( netclass->GetViaDiameter() / 2 ) + netclass->GetClearance(),
                      color );
        }
    }
#endif
    // MacOSX seems to need this.
    if( g_CurrentTrackList.GetCount() == 0 )
        return;

    // Set track parameters, that can be modified while creating the track
    g_CurrentTrackSegment->SetLayer( screen->m_Active_Layer );

    if( !frame->GetBoard()->GetBoardDesignSettings()->m_UseConnectedTrackWidth )
        g_CurrentTrackSegment->m_Width = frame->GetBoard()->GetCurrentTrackWidth();

    if( g_TwoSegmentTrackBuild )
    {
        TRACK* previous_track = g_CurrentTrackSegment->Back();

        if( previous_track  &&  previous_track->Type()==TYPE_TRACK )
        {
            previous_track->SetLayer( screen->m_Active_Layer );

            if( !frame->GetBoard()->GetBoardDesignSettings()->m_UseConnectedTrackWidth )
                previous_track->m_Width = frame->GetBoard()->GetCurrentTrackWidth();
        }
    }

    if( g_Track_45_Only_Allowed )
    {
        if( g_TwoSegmentTrackBuild )
        {
            g_CurrentTrackSegment->m_End = screen->GetCrossHairPosition();

            if( Drc_On )
                PushTrack( aPanel );

            ComputeBreakPoint( g_CurrentTrackSegment,
                               g_CurrentTrackList.GetCount(),
                               g_CurrentTrackSegment->m_End );
        }
        else
        {
            /* Calculate of the end of the path for the permitted directions:
             * horizontal, vertical or 45 degrees.
             */
            CalculateSegmentEndPoint( screen->GetCrossHairPosition(),
                                        g_CurrentTrackSegment->m_Start.x,
                                        g_CurrentTrackSegment->m_Start.y,
                                        &g_CurrentTrackSegment->m_End.x,
                                        &g_CurrentTrackSegment->m_End.y );
        }
    }
    else    /* Here the angle is arbitrary */
    {
        g_CurrentTrackSegment->m_End = screen->GetCrossHairPosition();
    }

    /* Redraw the new track */
    D( g_CurrentTrackList.VerifyListIntegrity(); );
    DrawTraces( aPanel, aDC, g_FirstTrackSegment, g_CurrentTrackList.GetCount(), GR_XOR );

    if( showTrackClearanceMode >= SHOW_CLEARANCE_NEW_TRACKS_AND_VIA_AREAS )
    {
        int color = g_ColorsSettings.GetLayerColor(g_CurrentTrackSegment->GetLayer());

        GRCircle( &aPanel->m_ClipBox, aDC, g_CurrentTrackSegment->m_End.x,
                  g_CurrentTrackSegment->m_End.y,
                  ( netclass->GetViaDiameter() / 2 ) + netclass->GetClearance(),
                  color );
    }

    /* Display info about currrent segment and the full new track:
     *  Choose the interesting segment: because we are using a 2 segments step,
     *  the last segment can be null, and the previous segment can be the
     * interesting segment.
     */
    TRACK* isegm = g_CurrentTrackSegment;

    if( isegm->GetLength() == 0 && g_CurrentTrackSegment->Back() )
        isegm = g_CurrentTrackSegment->Back();

    // display interesting segment info only:
    isegm->DisplayInfoBase( frame );

    // Diosplay current track length (on board) and the the actual track len
    // if there is an extra len due to the len die on the starting pad (if any)
    double   trackLen = 0.0;
    double   lenDie = 0.0;
    wxString msg;

    // If the starting point is on a pad, add current track length+ lenght die
    if( g_FirstTrackSegment->GetState( BEGIN_ONPAD ) )
    {
        D_PAD * pad = (D_PAD *) g_FirstTrackSegment->start;
        lenDie = (double) pad->m_LengthDie;
    }

    // calculate track len on board:
    for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
        trackLen += track->GetLength();

    valeur_param( wxRound( trackLen ), msg );
    frame->AppendMsgPanel( _( "Track Len" ), msg, DARKCYAN );

    if( lenDie != 0 )      // display the track len on board and the actual track len
    {
        frame->AppendMsgPanel( _( "Full Len" ), msg, DARKCYAN );
        valeur_param( wxRound( trackLen+lenDie ), msg );
        frame->AppendMsgPanel( _( "On Die" ), msg, DARKCYAN );
    }


    // Add current segments count (number of segments in this new track):
    msg.Printf( wxT( "%d" ), g_CurrentTrackList.GetCount() );
    frame->AppendMsgPanel( _( "Segs Count" ), msg, DARKCYAN );

    DisplayOpt.ShowTrackClearanceMode = showTrackClearanceMode;
    DisplayOpt.DisplayPcbTrackFill    = Track_fill_copy;

    frame->build_ratsnest_pad( NULL, g_CurrentTrackSegment->m_End, false );
    frame->trace_ratsnest_pad( aDC );
}


/* Determine the coordinate to advanced the the current segment
 * in 0, 90, or 45 degrees, depending on position of origin and \a aPosition.
 */
void CalculateSegmentEndPoint( const wxPoint& aPosition, int ox, int oy, int* fx, int* fy )
{
    int deltax, deltay, angle;

    deltax = aPosition.x - ox;
    deltay = aPosition.y - oy;

    deltax = abs( deltax );
    deltay = abs( deltay );
    angle  = 45;

    if( deltax >= deltay )
    {
        if( deltax == 0 )
            angle = 0;
        else if( ( (deltay << 6 ) / deltax ) < 26 )
            angle = 0;
    }
    else
    {
        angle = 45;

        if( deltay == 0 )
            angle = 90;
        else if( ( (deltax << 6 ) / deltay ) < 26 )
            angle = 90;
    }

    switch( angle )
    {
    case 0:
        *fx = aPosition.x;
        *fy = oy;
        break;

    case 45:
        deltax = MIN( deltax, deltay );
        deltay = deltax;

        /* Recalculate the signs fo deltax and deltaY. */
        if( ( aPosition.x - ox ) < 0 )
            deltax = -deltax;

        if( ( aPosition.y - oy ) < 0 )
            deltay = -deltay;

        *fx = ox + deltax;
        *fy = oy + deltay;
        break;

    case 90:
        *fx = ox;
        *fy = aPosition.y;
        break;
    }
}


/**
 * Compute new track angle based on previous track.
 */
void ComputeBreakPoint( TRACK* track, int SegmentCount, wxPoint end )
{
    int iDx    = 0;
    int iDy    = 0;
    int iAngle = 0;

    if( SegmentCount <= 0 )
        return;

    if( track == NULL )
        return;

    TRACK* newTrack = track;
    track = track->Back();
    SegmentCount--;

    if( track )
    {
        iDx = end.x - track->m_Start.x;
        iDy = end.y - track->m_Start.y;

        iDx = abs( iDx );
        iDy = abs( iDy );
    }

    TRACK* lastTrack = track ? track->Back() : NULL;

    if( lastTrack )
    {
        if(( (lastTrack->m_End.x == lastTrack->m_Start.x)
           || (lastTrack->m_End.y == lastTrack->m_Start.y) )
		&& !g_Alternate_Track_Posture)
        {
            iAngle = 45;
        }
    }
    else
    {
        if( g_Alternate_Track_Posture )
        {
            iAngle = 45;
        }
    }

    if( iAngle == 0 )
    {
        if( iDx >= iDy )
            iAngle = 0;
        else
            iAngle = 90;
    }

    if( track == NULL )
        iAngle = -1;

    switch( iAngle )
    {
    case -1:
        break;

    case 0:
        if( ( end.x - track->m_Start.x ) < 0 )
            track->m_End.x = end.x + iDy;
        else
            track->m_End.x = end.x - iDy;

        track->m_End.y = track->m_Start.y;
        break;

    case 45:
        iDx = MIN( iDx, iDy );
        iDy = iDx;

        /* Recalculate the signs fo deltax and deltaY. */
        if( ( end.x - track->m_Start.x ) < 0 )
            iDx = -iDx;

        if( ( end.y - track->m_Start.y ) < 0 )
            iDy = -iDy;

        track->m_End.x = track->m_Start.x + iDx;
        track->m_End.y = track->m_Start.y + iDy;
        break;

    case 90:
        if( ( end.y - track->m_Start.y ) < 0 )
            track->m_End.y = end.y + iDx;
        else
            track->m_End.y = end.y - iDx;

        track->m_End.x = track->m_Start.x;
        break;
    }

    if( track )
    {
        if( track->IsNull() )
            track->m_End = end;

        newTrack->m_Start = track->m_End;
    }

    newTrack->m_End = end;
}


/* Delete track segments which have len = 0; after creating a new track
 *  return a pointer on the first segment (start of track list)
 */
void DeleteNullTrackSegments( BOARD* pcb, DLIST<TRACK>& aTrackList )
{
    if( aTrackList.GetCount() == 0 )
        return;

    TRACK*      track = aTrackList.GetFirst();
    TRACK*      firsttrack = track;
    TRACK*      oldtrack;

    BOARD_ITEM* LockPoint = track->start;

    while( track != NULL )
    {
        oldtrack = track;
        track    = track->Next();

        if( !oldtrack->IsNull() )
        {
            continue;
        }

        // NULL segment, delete it
        if( firsttrack == oldtrack )
            firsttrack = track;

        delete aTrackList.Remove( oldtrack );
    }

    if( aTrackList.GetCount() == 0 )
        return;         // all the new track segments have been deleted

    // we must set the pointers on connected items and the connection status
    oldtrack = track = firsttrack;
    firsttrack->start = NULL;

    while( track != NULL )
    {
        oldtrack = track;
        track    = track->Next();
        oldtrack->end = track;

        if( track )
            track->start = oldtrack;

        oldtrack->SetStatus( 0 );
    }

    firsttrack->start = LockPoint;

    if( LockPoint &&  LockPoint->Type()==TYPE_PAD )
        firsttrack->SetState( BEGIN_ONPAD, ON );

    track = firsttrack;

    while( track != NULL )
    {
        TRACK* next_track = track->Next();
        LockPoint = Locate_Pad_Connecte( pcb, track, END );

        if( LockPoint )
        {
            track->end = LockPoint;
            track->SetState( END_ONPAD, ON );

            if( next_track )
            {
                next_track->start = LockPoint;
                next_track->SetState( BEGIN_ONPAD, ON );
            }
        }

        track = next_track;
    }
}


/* Ensure the end point of g_CurrentTrackSegment is on the pad "Pad"
 *  if no, create a new track segment if necessary
 *  and move current (or new) end segment on pad
 */
void EnsureEndTrackOnPad( D_PAD* Pad )
{
    if( g_CurrentTrackSegment->m_End == Pad->m_Pos ) // Ok !
    {
        g_CurrentTrackSegment->end = Pad;
        g_CurrentTrackSegment->SetState( END_ONPAD, ON );
        return;
    }

    TRACK* lasttrack = g_CurrentTrackSegment;

    if( !g_CurrentTrackSegment->IsNull() )
    {
        /* Must create a new segment, from track end to pad center */
        g_CurrentTrackList.PushBack( lasttrack->Copy() );

        lasttrack->end = g_CurrentTrackSegment;
    }

    g_CurrentTrackSegment->m_End = Pad->m_Pos;
    g_CurrentTrackSegment->SetState( END_ONPAD, OFF );

    g_CurrentTrackSegment->end = Pad;
    g_CurrentTrackSegment->SetState( END_ONPAD, ON );
}