/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 *
 * Copyright (C) 1992-2012 KiCad Developers, see change_log.txt for contributors.
 *
 * First copyright (C) Randy Nevin, 1989 (see PCBCA package)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

/* see "Autorouting With the A* Algorithm" (Dr.Dobbs journal)
*/

/**
 * @file solve.cpp
 */

#include <fctsys.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <wxPcbStruct.h>
#include <gr_basic.h>
#include <macros.h>
#include <pcbcommon.h>

#include <class_board.h>
#include <class_track.h>

#include <pcbnew.h>
#include <protos.h>
#include <autorout.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_rat );

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 AddNewTrace( 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            s_Clearance;  // Clearance value used in autorouter

static PICKED_ITEMS_LIST s_ItemsListPicker;

int OpenNodes;       /* total number of nodes opened */
int ClosNodes;       /* total number of nodes closed */
int MoveNodes;       /* total number of nodes moved */
int MaxNodes;        /* maximum number of nodes opened at one time */

#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 const 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 const 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 aLayersCount )
{
    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;
    int           routedCount = 0;      // routed ratsnest count
    bool          two_sides = aLayersCount == 2;

    m_canvas->SetAbortRequest( false );

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

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

    /* 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( m_canvas->GetAbortRequest() )
        {
            if( IsOK( this, _( "Abort routing?" ) ) )
            {
                success = STOP_FROM_ESC;
                stop    = true;
                break;
            }
            else
            {
                m_canvas->SetAbortRequest( false );
            }
        }

        EraseMsgBox();

        routedCount++;
        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" ), routedCount, RoutingMatrix.m_RouteCount );
            AppendMsgPanel( wxT( "Activity" ), msg, BROWN );
        }

        pt_cur_ch = pt_cur_ch;
        segm_oX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_source);
        segm_oY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_source);
        segm_fX = GetBoard()->GetBoundingBox().GetX() + (RoutingMatrix.m_GridRouting * col_target);
        segm_fY = GetBoard()->GetBoundingBox().GetY() + (RoutingMatrix.m_GridRouting * row_target);

        /* Draw segment. */
        GRLine( m_canvas->GetClipBox(), DC,
                segm_oX, segm_oY, segm_fX, segm_fY,
                0, WHITE | GR_XOR );
        pt_cur_ch->m_PadStart->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );
        pt_cur_ch->m_PadEnd->Draw( m_canvas, DC, GR_OR | GR_HIGHLIGHT );

        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 Connected" ), msg, CYAN );

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

        if( stop )
            break;
    }

    SaveCopyInUndoList( s_ItemsListPicker, UR_UNSPECIFIED );
    s_ItemsListPicker.ClearItemsList(); // s_ItemsListPicker is no more owner of picked items

    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_rat )
{
    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          padLayerMaskStart;    /* Mask layers belonging to the starting pad. */
    int          padLayerMaskEnd;      /* Mask layers belonging to the ending pad. */
    int          topLayerMask = GetLayerMask( Route_Layer_TOP );
    int          bottomLayerMask = GetLayerMask( Route_Layer_BOTTOM );
    int          routeLayerMask;       /* 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 = RoutingMatrix.m_Nrows * RoutingMatrix.m_Ncols * sizeof(DIR_CELL);
    memset( RoutingMatrix.m_DirSide[TOP], FROM_NOWHERE, i );
    memset( RoutingMatrix.m_DirSide[BOTTOM], FROM_NOWHERE, i );

    lastopen = lastclos = lastmove = 0;

    /* Set tab_masque[side] for final test of routing. */
    tab_mask[TOP]    = topLayerMask;
    tab_mask[BOTTOM] = bottomLayerMask;

    /* Set active layers mask. */
    routeLayerMask = topLayerMask | bottomLayerMask;

    pt_cur_ch = pt_rat;

    current_net_code  = pt_rat->GetNet();
    padLayerMaskStart = pt_cur_ch->m_PadStart->GetLayerMask();

    padLayerMaskEnd = pt_cur_ch->m_PadEnd->GetLayerMask();


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

    if( ( routeLayerMask & padLayerMaskEnd ) == 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 = ( RoutingMatrix.m_GridRouting * col_source )
                 + pcbframe->GetBoard()->GetBoundingBox().GetX();
        int cY = ( RoutingMatrix.m_GridRouting * row_source )
                 + pcbframe->GetBoard()->GetBoundingBox().GetY();
        int dx = pt_cur_ch->m_PadStart->GetSize().x / 2;
        int dy = pt_cur_ch->m_PadStart->GetSize().y / 2;
        int px = pt_cur_ch->m_PadStart->GetPosition().x;
        int py = pt_cur_ch->m_PadStart->GetPosition().y;

        if( ( ( int( pt_cur_ch->m_PadStart->GetOrientation() ) / 900 ) & 1 ) != 0 )
            EXCHG( dx, dy );

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

        cX = ( RoutingMatrix.m_GridRouting * col_target )
             + pcbframe->GetBoard()->GetBoundingBox().GetX();
        cY = ( RoutingMatrix.m_GridRouting * row_target )
             + pcbframe->GetBoard()->GetBoundingBox().GetY();
        dx = pt_cur_ch->m_PadEnd->GetSize().x / 2;
        dy = pt_cur_ch->m_PadEnd->GetSize().y / 2;
        px = pt_cur_ch->m_PadEnd->GetPosition().x;
        py = pt_cur_ch->m_PadEnd->GetPosition().y;

        if( ( ( int( pt_cur_ch->m_PadEnd->GetOrientation() ) / 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 )
       && ( padLayerMaskEnd & padLayerMaskStart &
            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" ) );

    PlacePad( pt_cur_ch->m_PadStart, CURRENT_PAD, marge, WRITE_OR_CELL );
    PlacePad( 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()->GetPadCount();

    for( unsigned ii = 0; ii < pcbframe->GetBoard()->GetPadCount(); ii++ )
    {
        D_PAD* ptr = pcbframe->GetBoard()->GetPad( ii );

        if( ( pt_cur_ch->m_PadStart != ptr ) && ( pt_cur_ch->m_PadEnd != ptr ) )
        {
            PlacePad( ptr, ~CURRENT_PAD, marge, WRITE_AND_CELL );
        }
    }

    InitQueue(); /* initialize the search queue */
    apx_dist = RoutingMatrix.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( padLayerMaskStart & topLayerMask )
            {
                start_mask_layer = 2;

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

            if( padLayerMaskStart & bottomLayerMask )
            {
                start_mask_layer |= 1;

                if( SetQueue( row_source, col_source, BOTTOM, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
        }
        else
        {
            if( padLayerMaskStart & bottomLayerMask )
            {
                start_mask_layer = 1;

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

            if( padLayerMaskStart & topLayerMask )
            {
                start_mask_layer |= 2;

                if( SetQueue( row_source, col_source, TOP, 0, apx_dist,
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
        }
    }
    else if( padLayerMaskStart & bottomLayerMask )
    {
        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 = RoutingMatrix.GetCell( r, c, side );

        if( curcell & CURRENT_PAD )
            curcell &= ~HOLE;

        if( (r == row_target) && (c == col_target)  /* success if layer OK */
           && ( tab_mask[side] & padLayerMaskEnd) )
        {
            /* Remove link. */
            GRSetDrawMode( DC, GR_XOR );
            GRLine( pcbframe->GetCanvas()->GetClipBox(),
                    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->GetCanvas()->GetAbortRequest() )
        {
            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 >= RoutingMatrix.m_Nrows ||
                nc < 0 || nc >= RoutingMatrix.m_Ncols )
                continue;  /* off the edge */

            if( _self == 5 && selfok2[i].present )
                continue;

            newcell = RoutingMatrix.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 = RoutingMatrix.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 = RoutingMatrix.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  = RoutingMatrix.GetDir( r, c, side );
            newdist = d + RoutingMatrix.CalcDist( ndir[i], olddir,
                                    ( olddir == FROM_OTHERSIDE ) ?
                                    RoutingMatrix.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( !RoutingMatrix.GetDir( nr, nc, side ) )
            {
                RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
                RoutingMatrix.SetDist( nr, nc, side, newdist );

                if( SetQueue( nr, nc, side, newdist,
                              RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
                              row_target, col_target ) == 0 )
                {
                    return ERR_MEMORY;
                }
            }
            else if( newdist < RoutingMatrix.GetDist( nr, nc, side ) )
            {
                RoutingMatrix.SetDir( nr, nc, side, ndir[i] );
                RoutingMatrix.SetDist( nr, nc, side, newdist );
                ReSetQueue( nr, nc, side, newdist,
                            RoutingMatrix.GetApxDist( nr, nc, row_target, col_target ),
                            row_target, col_target );
            }
        }

        /** Test the other layer. **/
        if( two_sides )
        {
            olddir = RoutingMatrix.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 = RoutingMatrix.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 >= RoutingMatrix.m_Nrows ||
                    nc < 0 || nc >= RoutingMatrix.m_Ncols )
                    continue;  /* off the edge !! */

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

                if( RoutingMatrix.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 + RoutingMatrix.CalcDist( FROM_OTHERSIDE, olddir, 0, side );

            /*  if (a) not visited yet,
             *  or (b) we have found a better path,
             *  add it to queue */
            if( !RoutingMatrix.GetDir( r, c, 1 - side ) )
            {
                RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
                RoutingMatrix.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 < RoutingMatrix.GetDist( r, c, 1 - side ) )
            {
                RoutingMatrix.SetDir( r, c, 1 - side, FROM_OTHERSIDE );
                RoutingMatrix.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:
    PlacePad( pt_cur_ch->m_PadStart, ~CURRENT_PAD, marge, WRITE_AND_CELL );
    PlacePad( 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  = RoutingMatrix.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:
            wxMessageBox( wxT( "Retrace: internal error: no way back" ) );
            return 0;
        }

        if( r0 != ILLEGAL )
            y = RoutingMatrix.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
            {
                wxMessageBox( 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:
                wxMessageBox( 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 ) ) );

    AddNewTrace( 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->GetBoundingBox().GetX() +
                                             ( RoutingMatrix.m_GridRouting * row );

        g_CurrentTrackSegment->m_Start.y   =
            g_CurrentTrackSegment->m_End.y = pcb->GetBoundingBox().GetY() +
                                             ( RoutingMatrix.m_GridRouting * col );

        g_CurrentTrackSegment->m_Width = pcb->GetCurrentViaSize();
        g_CurrentTrackSegment->m_Shape = pcb->GetDesignSettings().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->GetBoundingBox().GetX() +
                                         ( RoutingMatrix.m_GridRouting * row );
        g_CurrentTrackSegment->m_End.y = pcb->GetBoundingBox().GetY() +
                                         ( RoutingMatrix.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 = (TRACK*)g_CurrentTrackSegment->Clone();
                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() != PCB_VIA_T )
            {
                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 AddNewTrace( 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->GetCanvas();
    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 = (TRACK*)g_CurrentTrackSegment->Clone();

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

        g_CurrentTrackList.PushBack( newTrack );
    }

    g_FirstTrackSegment->start = pcbframe->GetBoard()->GetPad( g_FirstTrackSegment, FLG_START );

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

    g_CurrentTrackSegment->end = pcbframe->GetBoard()->GetPad( g_CurrentTrackSegment, FLG_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( track, HOLE, marge, WRITE_CELL );
        TraceSegmentPcb( 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 )
    {
        ITEM_PICKER picker( track, UR_NEW );
        s_ItemsListPicker.PushItem( picker );
        pcbframe->GetBoard()->m_Track.Insert( track, insertBeforeMe );
    }

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

    pcbframe->TestNetConnection( DC, netcode );

    screen->SetModify();
}
