/*************/
/* solve.cpp */
/*************/

#include "fctsys.h"
#include "common.h"
#include "class_drawpanel.h"
#include "confirm.h"

#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "class_board_design_settings.h"
#include "autorout.h"
#include "protos.h"

#include "cell.h"


static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
                                wxDC*           DC,
                                int             two_sides,
                                int             row_source,
                                int             col_source,
                                int             row_target,
                                int             col_target,
                                RATSNEST_ITEM*  pt_chevelu );
static int Retrace( PCB_EDIT_FRAME* pcbframe,
                    wxDC*           DC,
                    int,
                    int,
                    int,
                    int,
                    int,
                    int              net_code );
static void OrCell_Trace( BOARD* pcb,
                          int    col,
                          int    row,
                          int    side,
                          int    orient,
                          int    current_net_code );
static void Place_Piste_en_Buffer( PCB_EDIT_FRAME* pcbframe, wxDC* DC );


static int            segm_oX, segm_oY;
static int            segm_fX, segm_fY; /* Origin and position of the current
                                         * trace segment. */
static RATSNEST_ITEM* pt_cur_ch;
static int            Ncurrent;     /* measures of progress */
static int            s_Clearance;  // Clearance value used in autorouter


#define NOSUCCESS       0
#define STOP_FROM_ESC   -1
#define ERR_MEMORY      -2
#define SUCCESS         1
#define TRIVIAL_SUCCESS 2

/*
** visit neighboring cells like this (where [9] is on the other side):
**
**	+---+---+---+
**	| 1 | 2 | 3 |
**	+---+---+---+
**	| 4 |[9]| 5 |
**	+---+---+---+
**	| 6 | 7 | 8 |
**	+---+---+---+
*/

/* for visiting neighbors on the same side: increments/decrements coord of
 * [] [0] = row [] (1] = col was added to the coord of the midpoint for
 * Get the coord of the 8 neighboring points.
 */
static int delta[8][2] =
{
    {  1, -1 },     /* northwest	*/
    {  1, 0  },     /* north		*/
    {  1, 1  },     /* northeast	*/
    {  0, -1 },     /* west		*/
    {  0, 1  },     /* east		*/
    { -1, -1 },     /* southwest	*/
    { -1, 0  },     /* south		*/
    { -1, 1  }      /* southeast	*/
};

static int ndir[8] =
{
    /* for building paths back to source */
    FROM_SOUTHEAST, FROM_SOUTH,     FROM_SOUTHWEST,
    FROM_EAST,      FROM_WEST,
    FROM_NORTHEAST, FROM_NORTH,     FROM_NORTHWEST
};

/* blocking masks for neighboring cells */
#define BLOCK_NORTHEAST ( DIAG_NEtoSW | BENT_StoNE | BENT_WtoNE \
                          | ANGLE_NEtoSE | ANGLE_NWtoNE         \
                          | SHARP_NtoNE | SHARP_EtoNE | HOLE )
#define BLOCK_SOUTHEAST ( DIAG_SEtoNW | BENT_NtoSE | BENT_WtoSE \
                          | ANGLE_NEtoSE | ANGLE_SEtoSW         \
                          | SHARP_EtoSE | SHARP_StoSE | HOLE )
#define BLOCK_SOUTHWEST ( DIAG_NEtoSW | BENT_NtoSW | BENT_EtoSW \
                          | ANGLE_SEtoSW | ANGLE_SWtoNW         \
                          | SHARP_StoSW | SHARP_WtoSW | HOLE )
#define BLOCK_NORTHWEST ( DIAG_SEtoNW | BENT_EtoNW | BENT_StoNW \
                          | ANGLE_SWtoNW | ANGLE_NWtoNE         \
                          | SHARP_WtoNW | SHARP_NtoNW | HOLE )
#define BLOCK_NORTH     ( LINE_VERTICAL | BENT_NtoSE | BENT_NtoSW      \
                          | BENT_EtoNW | BENT_WtoNE                    \
                          | BENT_StoNE | BENT_StoNW                    \
                          | CORNER_NORTHEAST | CORNER_NORTHWEST        \
                          | ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_NWtoNE \
                          | DIAG_NEtoSW | DIAG_SEtoNW                  \
                          | SHARP_NtoNE | SHARP_NtoNW                  \
                          | SHARP_EtoNE | SHARP_WtoNW | HOLE )
#define BLOCK_EAST      ( LINE_HORIZONTAL | BENT_EtoSW | BENT_EtoNW    \
                          | BENT_NtoSE | BENT_StoNE                    \
                          | BENT_WtoNE | BENT_WtoSE                    \
                          | CORNER_NORTHEAST | CORNER_SOUTHEAST        \
                          | ANGLE_NEtoSE | ANGLE_SEtoSW | ANGLE_NWtoNE \
                          | DIAG_NEtoSW | DIAG_SEtoNW                  \
                          | SHARP_EtoNE | SHARP_EtoSE                  \
                          | SHARP_NtoNE | SHARP_StoSE | HOLE )
#define BLOCK_SOUTH     ( LINE_VERTICAL | BENT_StoNE | BENT_StoNW      \
                          | BENT_EtoSW | BENT_WtoSE                    \
                          | BENT_NtoSE | BENT_NtoSW                    \
                          | CORNER_SOUTHEAST | CORNER_SOUTHWEST        \
                          | ANGLE_NEtoSE | ANGLE_SWtoNW | ANGLE_SEtoSW \
                          | DIAG_NEtoSW | DIAG_SEtoNW                  \
                          | SHARP_StoSE | SHARP_StoSW                  \
                          | SHARP_EtoSE | SHARP_WtoSW | HOLE )
#define BLOCK_WEST      ( LINE_HORIZONTAL | BENT_WtoNE | BENT_WtoSE    \
                          | BENT_NtoSW | BENT_StoNW                    \
                          | BENT_EtoSW | BENT_EtoNW                    \
                          | CORNER_SOUTHWEST | CORNER_NORTHWEST        \
                          | ANGLE_SWtoNW | ANGLE_SEtoSW | ANGLE_NWtoNE \
                          | DIAG_NEtoSW | DIAG_SEtoNW                  \
                          | SHARP_WtoSW | SHARP_WtoNW                  \
                          | SHARP_NtoNW | SHARP_StoSW | HOLE )

struct block
{
    int  r1, c1;
    long b1;
    int  r2, c2;
    long b2;
};

/* blocking masks for diagonal traces */
static struct block blocking[8] =
{ {
      0, -1,
      BLOCK_NORTHEAST,
      1, 0,
      BLOCK_SOUTHWEST
  },
  {
      0, 0, 0,
      0, 0, 0
  },
  {
      1, 0,
      BLOCK_SOUTHEAST,
      0, 1,
      BLOCK_NORTHWEST
  },
  {
      0, 0, 0,
      0, 0, 0
  },
  {
      0, 0, 0,
      0, 0, 0
  },
  {
      0, -1,
      BLOCK_SOUTHEAST,
      -1, 0,
      BLOCK_NORTHWEST
  },
  {
      0, 0, 0,
      0, 0, 0
  },
  {
      -1, 0,
      BLOCK_NORTHEAST,
      0, 1,
      BLOCK_SOUTHWEST
  } };

/* mask for hole-related blocking effects */
static struct
{
    long trace;
    int  present;
} selfok2[8] =
{
    { HOLE_NORTHWEST, 0 },
    { HOLE_NORTH,     0 },
    { HOLE_NORTHEAST, 0 },
    { HOLE_WEST,      0 },
    { HOLE_EAST,      0 },
    { HOLE_SOUTHWEST, 0 },
    { HOLE_SOUTH,     0 },
    { HOLE_SOUTHEAST, 0 }
};

static long newmask[8] =
{
    /* patterns to mask out in neighbor cells */
    0,
    CORNER_NORTHWEST | CORNER_NORTHEAST,
    0,
    CORNER_NORTHWEST | CORNER_SOUTHWEST,
    CORNER_NORTHEAST | CORNER_SOUTHEAST,
    0,
    CORNER_SOUTHWEST | CORNER_SOUTHEAST,
    0
};


/* Route all traces
 * Return:
 *  1 if OK
 * -1 if escape (stop being routed) request
 * -2 if default memory allocation
 */
int PCB_EDIT_FRAME::Solve( wxDC* DC, int two_sides )
{
    int           current_net_code;
    int           row_source, col_source, row_target, col_target;
    int           success, nbsucces = 0, nbunsucces = 0;
    NETINFO_ITEM* net;
    bool          stop = false;
    wxString      msg;

    DrawPanel->m_AbortRequest = false;
    DrawPanel->m_AbortEnable  = true;

    s_Clearance = GetBoard()->m_NetClasses.GetDefault()->GetClearance();

    Ncurrent = 0;
    /* go until no more work to do */
    GetWork( &row_source, &col_source, &current_net_code,
             &row_target, &col_target, &pt_cur_ch ); // First net to route.

    for( ; row_source != ILLEGAL; GetWork( &row_source, &col_source,
                                           &current_net_code, &row_target,
                                           &col_target,
                                           &pt_cur_ch ) )
    {
        /* Test to stop routing ( escape key pressed ) */
        wxYield();

        if( DrawPanel->m_AbortRequest )
        {
            if( IsOK( this, _( "Abort routing?" ) ) )
            {
                success = STOP_FROM_ESC;
                stop    = true;
                break;
            }
            else
                DrawPanel->m_AbortRequest = 0;
        }

        EraseMsgBox();

        Ncurrent++;
        net = GetBoard()->FindNet( current_net_code );

        if( net )
        {
            msg.Printf( wxT( "[%8.8s]" ), GetChars( net->GetNetname() ) );
            AppendMsgPanel( wxT( "Net route" ), msg, BROWN );
            msg.Printf( wxT( "%d / %d" ), Ncurrent, Ntotal );
            AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
        }

        pt_cur_ch = pt_cur_ch;
        segm_oX = GetBoard()->m_BoundaryBox.m_Pos.x + (Board.m_GridRouting * col_source);
        segm_oY = GetBoard()->m_BoundaryBox.m_Pos.y + (Board.m_GridRouting * row_source);
        segm_fX = GetBoard()->m_BoundaryBox.m_Pos.x + (Board.m_GridRouting * col_target);
        segm_fY = GetBoard()->m_BoundaryBox.m_Pos.y + (Board.m_GridRouting * row_target);

        /* Draw segment. */
        GRLine( &DrawPanel->m_ClipBox,
                DC,
                segm_oX,
                segm_oY,
                segm_fX,
                segm_fY,
                0,
                WHITE | GR_XOR );
        pt_cur_ch->m_PadStart->Draw( DrawPanel, DC, GR_OR | GR_SURBRILL );
        pt_cur_ch->m_PadEnd->Draw( DrawPanel, DC, GR_OR | GR_SURBRILL );

        success = Autoroute_One_Track( this,
                                       DC,
                                       two_sides,
                                       row_source,
                                       col_source,
                                       row_target,
                                       col_target,
                                       pt_cur_ch );

        switch( success )
        {
        case NOSUCCESS:
            pt_cur_ch->m_Status |= CH_UNROUTABLE;
            nbunsucces++;
            break;

        case STOP_FROM_ESC:
            stop = true;
            break;

        case ERR_MEMORY:
            stop = true;
            break;

        default:
            nbsucces++;
            break;
        }

        msg.Printf( wxT( "%d" ), nbsucces );
        AppendMsgPanel( wxT( "Ok" ), msg, GREEN );
        msg.Printf( wxT( "%d" ), nbunsucces );
        AppendMsgPanel( wxT( "Fail" ), msg, RED );
        msg.Printf( wxT( "  %d" ), GetBoard()->m_NbNoconnect );
        AppendMsgPanel( wxT( "Not Connectd" ), msg, CYAN );

        /* Delete routing from display. */
        pt_cur_ch->m_PadStart->Draw( DrawPanel, DC, GR_AND );
        pt_cur_ch->m_PadEnd->Draw( DrawPanel, DC, GR_AND );

        if( stop )
            break;
    }

    DrawPanel->m_AbortEnable = false;

    return SUCCESS;
}


/* Route a trace on the BOARD.
 * Parameters:
 * 1 side / 2 sides (0 / 1)
 * Coord source (row, col)
 * Coord destination (row, col)
 * Net_code
 * Pointer to the ratsnest reference
 *
 * Returns:
 * SUCCESS if routed
 * TRIVIAL_SUCCESS if pads are connected by overlay (no track needed)
 * If failure NOSUCCESS
 * Escape STOP_FROM_ESC if demand
 * ERR_MEMORY if memory allocation failed.
 */
static int Autoroute_One_Track( PCB_EDIT_FRAME* pcbframe,
                                wxDC*           DC,
                                int             two_sides,
                                int             row_source,
                                int             col_source,
                                int             row_target,
                                int             col_target,
                                RATSNEST_ITEM*  pt_chevelu )
{
    int          r, c, side, d, apx_dist, nr, nc;
    int          result, skip;
    int          i;
    long         curcell, newcell, buddy, lastopen, lastclos, lastmove;
    int          newdist, olddir, _self;
    int          current_net_code;
    int          marge;
    int          pad_masque_layer_s;    /* Mask layers belonging to the
                                         *starting pad. */
    int          pad_masque_layer_e;    /* Mask layers belonging to the ending
                                         *pad. */
    int          masque_layer_TOP    = g_TabOneLayerMask[Route_Layer_TOP];
    int          masque_layer_BOTTOM = g_TabOneLayerMask[Route_Layer_BOTTOM];
    int          masque_layers;     /* Mask two layers for routing. */
    int          tab_mask[2];       /* Enables the calculation of the mask
                                     * layer being
                                     * tested. (side = TOP or BOTTOM) */
    int          start_mask_layer = 0;
    wxString     msg;

    wxBusyCursor dummy_cursor;      // Set an hourglass cursor while routing a
                                    // track

    result = NOSUCCESS;

    marge = s_Clearance +
            ( pcbframe->GetBoard()->GetCurrentTrackWidth() / 2 );

    /* clear direction flags */
    i = Nrows * Ncols * sizeof(DIR_CELL);
    memset( Board.m_DirSide[TOP], FROM_NOWHERE, i );
    memset( Board.m_DirSide[BOTTOM], FROM_NOWHERE, i );

    lastopen = lastclos = lastmove = 0;

    /* Set tab_masque[side] for final test of routing. */
    tab_mask[TOP]    = masque_layer_TOP;
    tab_mask[BOTTOM] = masque_layer_BOTTOM;
    /* Set active layers mask. */
    masque_layers = masque_layer_TOP | masque_layer_BOTTOM;

    pt_cur_ch = pt_chevelu;
    current_net_code   = pt_chevelu->GetNet();
    pad_masque_layer_s = pt_cur_ch->m_PadStart->m_Masque_Layer;
    pad_masque_layer_e = pt_cur_ch->m_PadEnd->m_Masque_Layer;


    /* First Test if routing possible ie if the pads are accessible
     * on the routing layers.
     */
    if( ( masque_layers & pad_masque_layer_s ) == 0 )
        goto end_of_route;
    if( ( masque_layers & pad_masque_layer_e ) == 0 )
        goto end_of_route;

    /* Then test if routing possible ie if the pads are accessible
     * On the routing grid (1 grid point must be in the pad)
     */
    {
        int cX = ( Board.m_GridRouting * col_source )
                 + pcbframe->GetBoard()->m_BoundaryBox.m_Pos.x;
        int cY = ( Board.m_GridRouting * row_source )
                 + pcbframe->GetBoard()->m_BoundaryBox.m_Pos.y;
        int dx = pt_cur_ch->m_PadStart->m_Size.x / 2;
        int dy = pt_cur_ch->m_PadStart->m_Size.y / 2;
        int px = pt_cur_ch->m_PadStart->GetPosition().x;
        int py = pt_cur_ch->m_PadStart->GetPosition().y;

        if( ( ( pt_cur_ch->m_PadStart->m_Orient / 900 ) & 1 ) != 0 )
            EXCHG( dx, dy );
        if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
            goto end_of_route;

        cX = ( Board.m_GridRouting * col_target )
             + pcbframe->GetBoard()->m_BoundaryBox.m_Pos.x;
        cY = ( Board.m_GridRouting * row_target )
             + pcbframe->GetBoard()->m_BoundaryBox.m_Pos.y;
        dx = pt_cur_ch->m_PadEnd->m_Size.x / 2;
        dy = pt_cur_ch->m_PadEnd->m_Size.y / 2;
        px = pt_cur_ch->m_PadEnd->GetPosition().x;
        py = pt_cur_ch->m_PadEnd->GetPosition().y;
        if( ( (pt_cur_ch->m_PadEnd->m_Orient / 900) & 1 ) != 0 )
            EXCHG( dx, dy );

        if( ( abs( cX - px ) > dx ) || ( abs( cY - py ) > dy ) )
            goto end_of_route;
    }

    /* Test the trivial case: direct connection overlay pads. */
    if( ( row_source == row_target ) && ( col_source == col_target )
       && ( pad_masque_layer_e & pad_masque_layer_s &
            g_TabAllCopperLayerMask[pcbframe->GetBoard()->GetCopperLayerCount() - 1] ) )
    {
        result = TRIVIAL_SUCCESS;
        goto end_of_route;
    }

    /* Placing the bit to remove obstacles on 2 pads to a link. */
    pcbframe->SetStatusText( wxT( "Gen Cells" ) );

    Place_1_Pad_Board( pcbframe->GetBoard(), pt_cur_ch->m_PadStart,
                       CURRENT_PAD, marge, WRITE_OR_CELL );
    Place_1_Pad_Board( pcbframe->GetBoard(), pt_cur_ch->m_PadEnd,
                       CURRENT_PAD, marge, WRITE_OR_CELL );

    /* Regenerates the remaining barriers (which may encroach on the placement
     * bits precedent)
     */
    i = pcbframe->GetBoard()->GetPadsCount();
    for( unsigned ii = 0; ii < pcbframe->GetBoard()->GetPadsCount(); ii++ )
    {
        D_PAD* ptr = pcbframe->GetBoard()->m_NetInfo->GetPad( ii );
        if( ( pt_cur_ch->m_PadStart != ptr ) && ( pt_cur_ch->m_PadEnd != ptr ) )
        {
            Place_1_Pad_Board( pcbframe->GetBoard(), ptr, ~CURRENT_PAD,
                               marge, WRITE_AND_CELL );
        }
    }

    InitQueue(); /* initialize the search queue */
    apx_dist = GetApxDist( row_source, col_source, row_target, col_target );

    /* Initialize first search. */
    if( two_sides )   /* Preferred orientation. */
    {
        if( abs( row_target - row_source ) > abs( col_target - col_source ) )
        {
            if( pad_masque_layer_s & masque_layer_TOP )
            {
                start_mask_layer = 2;
                if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
            if( pad_masque_layer_s & masque_layer_BOTTOM )
            {
                start_mask_layer |= 1;

                if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
        }
        else
        {
            if( pad_masque_layer_s & masque_layer_BOTTOM )
            {
                start_mask_layer = 1;
                if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
            if( pad_masque_layer_s & masque_layer_TOP )
            {
                start_mask_layer |= 2;

                if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
        }
    }
    else if( pad_masque_layer_s & masque_layer_BOTTOM )
    {
        start_mask_layer = 1;

        if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
                      row_target, col_target ) == 0 )
        {
            return ERR_MEMORY;
        }
    }

    /* search until success or we exhaust all possibilities */
    GetQueue( &r, &c, &side, &d, &apx_dist );
    for( ; r != ILLEGAL; GetQueue( &r, &c, &side, &d, &apx_dist ) )
    {
        curcell = GetCell( r, c, side );
        if( curcell & CURRENT_PAD )
            curcell &= ~HOLE;
        if( (r == row_target) && (c == col_target)  /* success if layer OK */
           && ( tab_mask[side] & pad_masque_layer_e) )
        {
            /* Remove link. */
            GRSetDrawMode( DC, GR_XOR );
            GRLine( &pcbframe->DrawPanel->m_ClipBox,
                    DC,
                    segm_oX,
                    segm_oY,
                    segm_fX,
                    segm_fY,
                    0,
                    WHITE );

            /* Generate trace. */
            if( Retrace( pcbframe, DC, row_source, col_source,
                         row_target, col_target, side, current_net_code ) )
            {
                result = SUCCESS;   /* Success : Route OK */
            }
            break;                  /* Routing complete. */
        }

        if( pcbframe->DrawPanel->m_AbortRequest )
        {
            result = STOP_FROM_ESC;
            break;
        }

        /* report every COUNT new nodes or so */
        #define COUNT 20000
        if( ( OpenNodes - lastopen > COUNT )
           || ( ClosNodes - lastclos > COUNT )
           || ( MoveNodes - lastmove > COUNT ) )
        {
            lastopen = OpenNodes;
            lastclos = ClosNodes;
            lastmove = MoveNodes;
            msg.Printf( wxT( "Activity: Open %d   Closed %d   Moved %d" ),
                        OpenNodes, ClosNodes, MoveNodes );
            pcbframe->SetStatusText( msg );
        }

        _self = 0;
        if( curcell & HOLE )
        {
            _self = 5;
            /* set 'present' bits */
            for( i = 0; i < 8; i++ )
            {
                selfok2[i].present = 0;
                if( curcell & selfok2[i].trace )
                    selfok2[i].present = 1;
            }
        }

        for( i = 0; i < 8; i++ ) /* consider neighbors */
        {
            nr = r + delta[i][0]; nc = c + delta[i][1];

            /* off the edge? */
            if( nr < 0 || nr >= Nrows || nc < 0 || nc >= Ncols )
                continue;  /* off the edge */

            if( _self == 5 && selfok2[i].present )
                continue;
            newcell = GetCell( nr, nc, side );
            if( newcell & CURRENT_PAD )
                newcell &= ~HOLE;

            /* check for non-target hole */
            if( newcell & HOLE )
            {
                if( nr != row_target || nc != col_target )
                    continue;
            }
            /* check for traces */
            else if( newcell & HOLE & ~(newmask[i]) )
                continue;

            /* check blocking on corner neighbors */
            if( delta[i][0] && delta[i][1] )
            {
                /* check first buddy */
                buddy = GetCell( r + blocking[i].r1, c + blocking[i].c1, side );
                if( buddy & CURRENT_PAD )
                    buddy &= ~HOLE;
                if( buddy & HOLE )
                    continue;

//				if (buddy & (blocking[i].b1)) continue;
                /* check second buddy */
                buddy = GetCell( r + blocking[i].r2, c + blocking[i].c2, side );
                if( buddy & CURRENT_PAD )
                    buddy &= ~HOLE;
                if( buddy & HOLE )
                    continue;

//				if (buddy & (blocking[i].b2)) continue;
            }

            olddir  = GetDir( r, c, side );
            newdist = d + CalcDist( ndir[i], olddir,
                                    ( olddir == FROM_OTHERSIDE ) ?
                                    GetDir( r, c, 1 - side ) : 0, side );

            /* if (a) not visited yet, or (b) we have */
            /* found a better path, add it to queue */
            if( !GetDir( nr, nc, side ) )
            {
                SetDir( nr, nc, side, ndir[i] );
                SetDist( nr, nc, side, newdist );
                if( SetQueue( nr, nc, side, newdist,
                              GetApxDist( nr, nc, row_target, col_target ),
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
            else if( newdist < GetDist( nr, nc, side ) )
            {
                SetDir( nr, nc, side, ndir[i] );
                SetDist( nr, nc, side, newdist );
                ReSetQueue( nr, nc, side, newdist,
                            GetApxDist( nr, nc, row_target, col_target ),
                            row_target, col_target );
            }
        }

        /** Test the other layer. **/
        if( two_sides )
        {
            olddir = GetDir( r, c, side );
            if( olddir == FROM_OTHERSIDE )
                continue;   /* useless move, so don't bother */
            if( curcell )   /* can't drill via if anything here */
                continue;
            /* check for holes or traces on other side */
            if( ( newcell = GetCell( r, c, 1 - side ) ) != 0 )
                continue;
            /* check for nearby holes or traces on both sides */
            for( skip = 0, i = 0; i < 8; i++ )
            {
                nr = r + delta[i][0]; nc = c + delta[i][1];

                if( nr < 0 || nr >= Nrows || nc < 0 || nc >= Ncols )
                    continue;  /* off the edge !! */

                if( GetCell( nr, nc, side ) /* & blocking2[i]*/ )
                {
                    skip = 1; /* can't drill via here */
                    break;
                }

                if( GetCell( nr, nc, 1 - side ) /* & blocking2[i]*/ )
                {
                    skip = 1; /* can't drill via here */
                    break;
                }
            }

            if( skip )      /* neighboring hole or trace? */
                continue;   /* yes, can't drill via here */

            newdist = d + CalcDist( FROM_OTHERSIDE, olddir, 0, side );

            /*  if (a) not visited yet,
             *  or (b) we have found a better path,
             *  add it to queue */
            if( !GetDir( r, c, 1 - side ) )
            {
                SetDir( r, c, 1 - side, FROM_OTHERSIDE );
                SetDist( r, c, 1 - side, newdist );
                if( SetQueue( r, c, 1 - side, newdist, apx_dist, row_target,
                              col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
            else if( newdist < GetDist( r, c, 1 - side ) )
            {
                SetDir( r, c, 1 - side, FROM_OTHERSIDE );
                SetDist( r, c, 1 - side, newdist );
                ReSetQueue( r,
                            c,
                            1 - side,
                            newdist,
                            apx_dist,
                            row_target,
                            col_target );
            }
        }     /* Finished attempt to route on other layer. */
    }

end_of_route:
    Place_1_Pad_Board( pcbframe->GetBoard(), pt_cur_ch->m_PadStart,
                       ~CURRENT_PAD, marge, WRITE_AND_CELL );
    Place_1_Pad_Board( pcbframe->GetBoard(), pt_cur_ch->m_PadEnd,
                       ~CURRENT_PAD, marge, WRITE_AND_CELL );

    msg.Printf( wxT( "Activity: Open %d   Closed %d   Moved %d"),
                OpenNodes, ClosNodes, MoveNodes );
    pcbframe->SetStatusText( msg );

    return result;
}


static long bit[8][9] =
{
    /* OT=Otherside */
    /* N, NE, E, SE, S, SW, W, NW, OT */
/* N */
    { LINE_VERTICAL,
      BENT_StoNE,
      CORNER_SOUTHEAST,
      SHARP_StoSE,
      0,
      SHARP_StoSW,
      CORNER_SOUTHWEST,
      BENT_StoNW,
      ( HOLE | HOLE_SOUTH )
    },
/* NE */
    {
        BENT_NtoSW,
        DIAG_NEtoSW,
        BENT_EtoSW,
        ANGLE_SEtoSW,
        SHARP_StoSW,
        0,
        SHARP_WtoSW,
        ANGLE_SWtoNW,
        ( HOLE | HOLE_SOUTHWEST )
    },
/* E */
    {
        CORNER_NORTHWEST,
        BENT_WtoNE,
        LINE_HORIZONTAL,
        BENT_WtoSE,
        CORNER_SOUTHWEST,
        SHARP_WtoSW,
        0,
        SHARP_WtoNW,
        ( HOLE | HOLE_WEST )
    },
/* SE */
    {
        SHARP_NtoNW,
        ANGLE_NWtoNE,
        BENT_EtoNW,
        DIAG_SEtoNW,
        BENT_StoNW,
        ANGLE_SWtoNW,
        SHARP_WtoNW,
        0,
        ( HOLE | HOLE_NORTHWEST )
    },
/* S */
    {
        0,
        SHARP_NtoNE,
        CORNER_NORTHEAST,
        BENT_NtoSE,
        LINE_VERTICAL,
        BENT_NtoSW,
        CORNER_NORTHWEST,
        SHARP_NtoNW,
        ( HOLE | HOLE_NORTH )
    },
/* SW */
    {
        SHARP_NtoNE,
        0,
        SHARP_EtoNE,
        ANGLE_NEtoSE,
        BENT_StoNE,
        DIAG_NEtoSW,
        BENT_WtoNE,
        ANGLE_NWtoNE,
        ( HOLE | HOLE_NORTHEAST )
    },
/* W */
    {
        CORNER_NORTHEAST,
        SHARP_EtoNE,
        0,
        SHARP_EtoSE,
        CORNER_SOUTHEAST,
        BENT_EtoSW,
        LINE_HORIZONTAL,
        BENT_EtoNW,
        ( HOLE | HOLE_EAST )
    },
/* NW */
    {
        BENT_NtoSE,
        ANGLE_NEtoSE,
        SHARP_EtoSE,
        0,
        SHARP_StoSE,
        ANGLE_SEtoSW,
        BENT_WtoSE,
        DIAG_SEtoNW,
        ( HOLE | HOLE_SOUTHEAST )
    }
};


/* work from target back to source, actually laying the traces
 *  Parameters:
 *      start on side target_side, of coordinates row_target, col_target.
 *      arrive on side masque_layer_start, coordinate row_source, col_source
 * The search is done in reverse routing, the point of arrival (target) to
 * the starting point (source)
 * The router.
 *
 * Target_side = symbol (TOP / BOTTOM) of departure
 * = Mask_layer_source mask layers Arrival
 *
 * Returns:
 * 0 if error
 * > 0 if Ok
 */
static int Retrace( PCB_EDIT_FRAME* pcbframe, wxDC* DC,
                    int row_source, int col_source,
                    int row_target, int col_target, int target_side,
                    int current_net_code )
{
    int  r0, c0, s0;
    int  r1, c1, s1;    /* row, col, starting side. */
    int  r2, c2, s2;    /* row, col, ending side. */
    int  x, y = -1;
    long b;

    r1 = row_target;
    c1 = col_target;    /* start point is target ( end point is source )*/
    s1 = target_side;
    r0 = c0 = s0 = ILLEGAL;

    wxASSERT( g_CurrentTrackList.GetCount() == 0 );

    do {
        /* find where we came from to get here */
        r2 = r1; c2 = c1; s2 = s1;
        x  = GetDir( r1, c1, s1 );

        switch( x )
        {
        case FROM_NORTH:
            r2++;
            break;

        case FROM_EAST:
            c2++;
            break;

        case FROM_SOUTH:
            r2--;
            break;

        case FROM_WEST:
            c2--;
            break;

        case FROM_NORTHEAST:
            r2++;
            c2++;
            break;

        case FROM_SOUTHEAST:
            r2--;
            c2++;
            break;

        case FROM_SOUTHWEST:
            r2--;
            c2--;
            break;

        case FROM_NORTHWEST:
            r2++;
            c2--;
            break;

        case FROM_OTHERSIDE:
            s2 = 1 - s2;
            break;

        default:
            DisplayError( pcbframe,
                          wxT( "Retrace: internal error: no way back" ) );
            return 0;
        }

        if( r0 != ILLEGAL )
            y = GetDir( r0, c0, s0 );

        /* see if target or hole */
        if( ( ( r1 == row_target ) && ( c1 == col_target ) )
           || ( s1 != s0 ) )
        {
            int p_dir;

            switch( x )
            {
            case FROM_NORTH:
                p_dir = HOLE_NORTH;
                break;

            case FROM_EAST:
                p_dir = HOLE_EAST;
                break;

            case FROM_SOUTH:
                p_dir = HOLE_SOUTH;
                break;

            case FROM_WEST:
                p_dir = HOLE_WEST;
                break;

            case FROM_NORTHEAST:
                p_dir = HOLE_NORTHEAST;
                break;

            case FROM_SOUTHEAST:
                p_dir = HOLE_SOUTHEAST;
                break;

            case FROM_SOUTHWEST:
                p_dir = HOLE_SOUTHWEST;
                break;

            case FROM_NORTHWEST:
                p_dir = HOLE_NORTHWEST;
                break;

            case FROM_OTHERSIDE:
            default:
                DisplayError( pcbframe, wxT( "Retrace: error 1" ) );
                return 0;
            }

            OrCell_Trace(
                pcbframe->GetBoard(), r1, c1, s1, p_dir, current_net_code );
        }
        else
        {
            if( ( y == FROM_NORTH || y == FROM_NORTHEAST
                  || y == FROM_EAST || y == FROM_SOUTHEAST
                  || y == FROM_SOUTH || y == FROM_SOUTHWEST
                  || y == FROM_WEST || y == FROM_NORTHWEST )
               && ( x == FROM_NORTH || x == FROM_NORTHEAST
                    || x == FROM_EAST || x == FROM_SOUTHEAST
                    || x == FROM_SOUTH || x == FROM_SOUTHWEST
                    || x == FROM_WEST || x == FROM_NORTHWEST
                    || x == FROM_OTHERSIDE )
               && ( ( b = bit[y - 1][x - 1] ) != 0 ) )
            {
                OrCell_Trace( pcbframe->GetBoard(), r1, c1, s1, b,
                              current_net_code );
                if( b & HOLE )
                    OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, HOLE,
                                  current_net_code );
            }
            else
            {
                DisplayError( pcbframe, wxT( "Retrace: error 2" ) );
                return 0;
            }
        }

        if( ( r2 == row_source ) && ( c2 == col_source ) ) /* see if source */
        {
            int p_dir;

            switch( x )
            {
            case FROM_NORTH:
                p_dir = HOLE_SOUTH;
                break;

            case FROM_EAST:
                p_dir = HOLE_WEST;
                break;

            case FROM_SOUTH:
                p_dir = HOLE_NORTH;
                break;

            case FROM_WEST:
                p_dir = HOLE_EAST;
                break;

            case FROM_NORTHEAST:
                p_dir = HOLE_SOUTHWEST;
                break;

            case FROM_SOUTHEAST:
                p_dir = HOLE_NORTHWEST;
                break;

            case FROM_SOUTHWEST:
                p_dir = HOLE_NORTHEAST;
                break;

            case FROM_NORTHWEST:
                p_dir = HOLE_SOUTHEAST;
                break;

            case FROM_OTHERSIDE:
            default:
                DisplayError( pcbframe, wxT( "Retrace: error 3" ) );
                return 0;
            }

            OrCell_Trace( pcbframe->GetBoard(), r2, c2, s2, p_dir,
                          current_net_code );
        }
        /* move to next cell */
        r0 = r1;
        c0 = c1;
        s0 = s1;
        r1 = r2;
        c1 = c2;
        s1 = s2;
    } while( !( ( r2 == row_source ) && ( c2 == col_source ) ) );

    Place_Piste_en_Buffer( pcbframe, DC );
    return 1;
}


/* This function is used by Retrace and read the autorouting matrix data cells to create
 * the real track on the physical board
 */
static void OrCell_Trace( BOARD* pcb, int col, int row,
                          int side, int orient, int current_net_code )
{
    int    dx0, dy0, dx1, dy1;
    TRACK* newTrack;

    if( orient == HOLE )  // placement of a via
    {
        newTrack = new SEGVIA( pcb );

        g_CurrentTrackList.PushBack( newTrack );

        g_CurrentTrackSegment->SetState( TRACK_AR, ON );
        g_CurrentTrackSegment->SetLayer( 0x0F );

        g_CurrentTrackSegment->m_Start.x   =
            g_CurrentTrackSegment->m_End.x = pcb->m_BoundaryBox.m_Pos.x +
                                             ( Board.m_GridRouting * row );

        g_CurrentTrackSegment->m_Start.y   =
            g_CurrentTrackSegment->m_End.y = pcb->m_BoundaryBox.m_Pos.y +
                                             ( Board.m_GridRouting * col );

        g_CurrentTrackSegment->m_Width = pcb->GetCurrentViaSize();
        g_CurrentTrackSegment->m_Shape = pcb->GetBoardDesignSettings()->m_CurrentViaType;

        g_CurrentTrackSegment->SetNet( current_net_code );
    }
    else    // placement of a standard segment
    {
        newTrack = new TRACK( pcb );

        g_CurrentTrackList.PushBack( newTrack );

        g_CurrentTrackSegment->SetLayer( Route_Layer_BOTTOM );
        if( side == TOP )
            g_CurrentTrackSegment->SetLayer( Route_Layer_TOP );

        g_CurrentTrackSegment->SetState( TRACK_AR, ON );
        g_CurrentTrackSegment->m_End.x = pcb->m_BoundaryBox.m_Pos.x +
                                         ( Board.m_GridRouting * row );
        g_CurrentTrackSegment->m_End.y = pcb->m_BoundaryBox.m_Pos.y +
                                         ( Board.m_GridRouting * col );
        g_CurrentTrackSegment->SetNet( current_net_code );

        if( g_CurrentTrackSegment->Back() == NULL ) /* Start trace. */
        {
            g_CurrentTrackSegment->m_Start.x = segm_fX;
            g_CurrentTrackSegment->m_Start.y = segm_fY;

            /* Placement on the center of the pad if outside grid. */
            dx1 = g_CurrentTrackSegment->m_End.x -
                  g_CurrentTrackSegment->m_Start.x;
            dy1 = g_CurrentTrackSegment->m_End.y -
                  g_CurrentTrackSegment->m_Start.y;

            dx0 = pt_cur_ch->m_PadEnd->GetPosition().x -
                  g_CurrentTrackSegment->m_Start.x;
            dy0 = pt_cur_ch->m_PadEnd->GetPosition().y -
                  g_CurrentTrackSegment->m_Start.y;

            /* If aligned, change the origin point. */
            if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
            {
                g_CurrentTrackSegment->m_Start =
                    pt_cur_ch->m_PadEnd->GetPosition();
            }
            else    // Creation of a supplemental segment
            {
                g_CurrentTrackSegment->m_Start =
                    pt_cur_ch->m_PadEnd->GetPosition();

                newTrack = g_CurrentTrackSegment->Copy();
                newTrack->m_Start = g_CurrentTrackSegment->m_End;

                g_CurrentTrackList.PushBack( newTrack );
            }
        }
        else
        {
            if( g_CurrentTrackSegment->Back() )
            {
                g_CurrentTrackSegment->m_Start =
                    g_CurrentTrackSegment->Back()->m_End;
            }
        }
        g_CurrentTrackSegment->m_Width = pcb->GetCurrentTrackWidth();

        if( g_CurrentTrackSegment->m_Start != g_CurrentTrackSegment->m_End )
        {
            /* Reduce aligned segments by one. */
            TRACK* oldTrack = g_CurrentTrackSegment->Back();
            if( oldTrack &&  oldTrack->Type() != TYPE_VIA )
            {
                dx1 = g_CurrentTrackSegment->m_End.x -
                      g_CurrentTrackSegment->m_Start.x;
                dy1 = g_CurrentTrackSegment->m_End.y -
                      g_CurrentTrackSegment->m_Start.y;

                dx0 = oldTrack->m_End.x - oldTrack->m_Start.x;
                dy0 = oldTrack->m_End.y - oldTrack->m_Start.y;

                if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
                {
                    oldTrack->m_End = g_CurrentTrackSegment->m_End;

                    delete g_CurrentTrackList.PopBack();
                }
            }
        }
    }
}


/* Insert the new track created in the list of tracks.
 * amend the points of beginning and end of the track so that they are
 * connected
 * Center on pads even if they are off grid.
 */
static void Place_Piste_en_Buffer( PCB_EDIT_FRAME* pcbframe, wxDC* DC )
{
    if( g_FirstTrackSegment == NULL )
        return;

    int dx0, dy0, dx1, dy1;
    int marge, via_marge;
    EDA_DRAW_PANEL* panel = pcbframe->DrawPanel;
    PCB_SCREEN* screen = pcbframe->GetScreen();

    marge = s_Clearance + ( pcbframe->GetBoard()->GetCurrentTrackWidth() / 2 );
    via_marge = s_Clearance + ( pcbframe->GetBoard()->GetCurrentViaSize() / 2 );

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

    /* Place on center of pad if off grid. */
    dx0 = pt_cur_ch->m_PadStart->GetPosition().x - g_CurrentTrackSegment->m_Start.x;
    dy0 = pt_cur_ch->m_PadStart->GetPosition().y - g_CurrentTrackSegment->m_Start.y;

    /* If aligned, change the origin point. */
    if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
    {
        g_CurrentTrackSegment->m_End = pt_cur_ch->m_PadStart->GetPosition();
    }
    else
    {
        TRACK* newTrack = g_CurrentTrackSegment->Copy();

        newTrack->m_End   = pt_cur_ch->m_PadStart->GetPosition();
        newTrack->m_Start = g_CurrentTrackSegment->m_End;

        g_CurrentTrackList.PushBack( newTrack );
    }

    g_FirstTrackSegment->start = Locate_Pad_Connecte( pcbframe->GetBoard(),
                                                      g_FirstTrackSegment, START );

    if( g_FirstTrackSegment->start )
        g_FirstTrackSegment->SetState( BEGIN_ONPAD, ON );

    g_CurrentTrackSegment->end = Locate_Pad_Connecte( pcbframe->GetBoard(),
                                                      g_CurrentTrackSegment, END );

    if( g_CurrentTrackSegment->end )
        g_CurrentTrackSegment->SetState( END_ONPAD, ON );

    /* Out the new track on the matrix board */
    for( TRACK* track = g_FirstTrackSegment; track; track = track->Next() )
    {
        TraceSegmentPcb( pcbframe->GetBoard(), track, HOLE, marge, WRITE_CELL );
        TraceSegmentPcb( pcbframe->GetBoard(), track, VIA_IMPOSSIBLE,
                         via_marge, WRITE_OR_CELL );
    }

    // Insert new segments in  real board
    int    netcode    = g_FirstTrackSegment->GetNet();
    TRACK* firstTrack = g_FirstTrackSegment;
    int    newCount   = g_CurrentTrackList.GetCount();

    // Put entire new current segment list in BOARD
    TRACK* track;
    TRACK* insertBeforeMe = g_CurrentTrackSegment->GetBestInsertPoint( pcbframe->GetBoard() );

    while( ( track = g_CurrentTrackList.PopFront() ) != NULL )
    {
        pcbframe->GetBoard()->m_Track.Insert( track, insertBeforeMe );
    }

    Trace_Une_Piste( panel, DC, firstTrack, newCount, GR_OR );

    pcbframe->test_1_net_connexion( DC, netcode );

    screen->SetModify();
}
