/*
 * 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 <class_board.h>
#include <class_track.h>

#include <pcbnew.h>
#include <protos.h>
#include <autorout.h>
#include <cell.h>

static int Autoroute_One_Track( AUTOROUTER_CONTEXT& ctx,
                                int             two_sides,
                                int             row_source,
                                int             col_source,
                                int             row_target,
                                int             col_target,
                                RATSNEST_ITEM*  pt_rat );

static int Retrace( AUTOROUTER_CONTEXT& ctx,
                    int,
                    int,
                    int,
                    int,
                    int,
                    int              net_code );

static void OrCell_Trace( AUTOROUTER_CONTEXT& ctx,
                          int    col,
                          int    row,
                          int    side,
                          int    orient,
                          int    current_net_code );

static void AddNewTrace( AUTOROUTER_CONTEXT& ctx );


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
 * :
 *  1 if OK
 * -1 if escape (stop being routed) request
 * -2 if default memory allocation
 */
int PCB_EDIT_FRAME::Solve( AUTOROUTER_CONTEXT& aCtx, 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 = aCtx.board->GetDesignSettings().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 = aCtx.board->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 );
        }

        segm_oX = aCtx.bbox.GetX() + ( RoutingMatrix.m_GridRouting * col_source );
        segm_oY = aCtx.bbox.GetY() + ( RoutingMatrix.m_GridRouting * row_source );
        segm_fX = aCtx.bbox.GetX() + ( RoutingMatrix.m_GridRouting * col_target );
        segm_fY = aCtx.bbox.GetY() + ( RoutingMatrix.m_GridRouting * row_target );

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

        success = Autoroute_One_Track( aCtx, 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" ), aCtx.board->GetUnconnectedNetCount() );
        AppendMsgPanel( wxT( "Not Connected" ), msg, CYAN );

        // Delete routing from display.
        pt_cur_ch->m_PadStart->Draw( m_canvas, aCtx.dc, GR_AND );
        pt_cur_ch->m_PadEnd->Draw( m_canvas, aCtx.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( AUTOROUTER_CONTEXT& ctx,
                                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;
    LSET         padLayerMaskStart;    // Mask layers belonging to the starting pad.
    LSET         padLayerMaskEnd;      // Mask layers belonging to the ending pad.

    LSET         topLayerMask( g_Route_Layer_TOP );

    LSET         bottomLayerMask( g_Route_Layer_BOTTOM );

    LSET         routeLayerMask;       // Mask two layers for routing.

    LSET         tab_mask[2];           // Enables the calculation of the mask layer being
                                        // tested. (side = TOP or BOTTOM)
    int          start_mask_layer = 0;
    wxString     msg;

    // @todo this could be a bottle neck
    LSET all_cu = LSET::AllCuMask( ctx.board->GetCopperLayerCount() );

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

    result = NOSUCCESS;

    marge = s_Clearance + ( ctx.pcbframe->GetDesignSettings().GetCurrentTrackWidth() / 2 );

    // clear direction flags
    i = RoutingMatrix.m_Nrows * RoutingMatrix.m_Ncols * sizeof(DIR_CELL);

    if( two_sides )
        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.
    if( two_sides )
        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->GetLayerSet();

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


    /* 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 ) + ctx.bbox.GetX();
        int cY = ( RoutingMatrix.m_GridRouting * row_source ) + ctx.bbox.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 )
            std::swap( dx, dy );

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

        cX = ( RoutingMatrix.m_GridRouting * col_target ) + ctx.bbox.GetX();
        cY = ( RoutingMatrix.m_GridRouting * row_target ) + ctx.bbox.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 )
            std::swap( 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 & all_cu ).any() )
    {
        result = TRIVIAL_SUCCESS;
        goto end_of_route;
    }

    // Placing the bit to remove obstacles on 2 pads to a link.
    ctx.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 = ctx.board->GetPadCount();

    for( unsigned ii = 0; ii < ctx.board->GetPadCount(); ii++ )
    {
        D_PAD* ptr = ctx.board->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 ).any() )
            {
                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 ).any() )
            {
                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 ).any() )
            {
                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 ).any() )
            {
                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 ).any() )
    {
        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).any() )
        {
            // Remove link.
            GRSetDrawMode( ctx.dc, GR_XOR );
            GRLine( ctx.pcbframe->GetCanvas()->GetClipBox(),
                    ctx.dc,
                    segm_oX, segm_oY,
                    segm_fX, segm_fY,
                    0,
                    WHITE );

            // Generate trace.
            if( Retrace( ctx, row_source, col_source,
                         row_target, col_target, side, current_net_code ) )
            {
                result = SUCCESS;   // Success : Route OK
            }

            break;                  // Routing complete.
        }

        if( ctx.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 );
            ctx.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 );
    ctx.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( AUTOROUTER_CONTEXT& ctx,
                    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( ctx.pcbframe, wxT( "Retrace: error 1" ) );
                return 0;
            }

            OrCell_Trace( ctx, 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( ctx, r1, c1, s1, b, current_net_code );

                if( b & HOLE )
                    OrCell_Trace( ctx, 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( ctx, 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( ctx );
    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( AUTOROUTER_CONTEXT& ctx, int col, int row,
                          int side, int orient, int current_net_code )
{
    if( orient == HOLE )  // placement of a via
    {
        VIA* newVia = new VIA( ctx.board );

        g_CurrentTrackList.PushBack( newVia );

        g_CurrentTrackSegment->SetState( TRACK_AR, true );
        g_CurrentTrackSegment->SetLayer( F_Cu );

        g_CurrentTrackSegment->SetStart( wxPoint( ctx.bbox.GetX() + RoutingMatrix.m_GridRouting * row,
                            ctx.bbox.GetY() + RoutingMatrix.m_GridRouting * col ) );
        g_CurrentTrackSegment->SetEnd( g_CurrentTrackSegment->GetStart() );

        g_CurrentTrackSegment->SetWidth( ctx.board->GetDesignSettings().GetCurrentViaSize() );
        newVia->SetViaType( ctx.board->GetDesignSettings().m_CurrentViaType );

        g_CurrentTrackSegment->SetNetCode( current_net_code );
    }
    else    // placement of a standard segment
    {
        TRACK* newTrack = new TRACK( ctx.board );
        int    dx0, dy0, dx1, dy1;

        g_CurrentTrackList.PushBack( newTrack );

        g_CurrentTrackSegment->SetLayer( g_Route_Layer_BOTTOM );

        if( side == TOP )
            g_CurrentTrackSegment->SetLayer( g_Route_Layer_TOP );

        g_CurrentTrackSegment->SetState( TRACK_AR, true );
        g_CurrentTrackSegment->SetEnd( wxPoint( ctx.bbox.GetX() + RoutingMatrix.m_GridRouting * row,
                            ctx.bbox.GetY() + RoutingMatrix.m_GridRouting * col ) );
        g_CurrentTrackSegment->SetNetCode( current_net_code );

        if( g_CurrentTrackSegment->Back() == NULL ) // Start trace.
        {
            g_CurrentTrackSegment->SetStart( wxPoint( segm_fX, segm_fY ) );

            // Placement on the center of the pad if outside grid.
            dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
            dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;

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

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

                newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
                newTrack->SetStart( g_CurrentTrackSegment->GetEnd());

                g_CurrentTrackList.PushBack( newTrack );
            }
        }
        else
        {
            if( g_CurrentTrackSegment->Back() )
            {
                g_CurrentTrackSegment->SetStart( g_CurrentTrackSegment->Back()->GetEnd() );
            }
        }

        g_CurrentTrackSegment->SetWidth( ctx.board->GetDesignSettings().GetCurrentTrackWidth() );

        if( g_CurrentTrackSegment->GetStart() != g_CurrentTrackSegment->GetEnd() )
        {
            // Reduce aligned segments by one.
            TRACK* oldTrack = g_CurrentTrackSegment->Back();

            if( oldTrack &&  oldTrack->Type() != PCB_VIA_T )
            {
                dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
                dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;

                dx0 = oldTrack->GetEnd().x - oldTrack->GetStart().x;
                dy0 = oldTrack->GetEnd().y - oldTrack->GetStart().y;

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

                    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( AUTOROUTER_CONTEXT& ctx )
{
    if( g_FirstTrackSegment == NULL )
        return;

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

    marge = s_Clearance + ( ctx.pcbframe->GetDesignSettings().GetCurrentTrackWidth() / 2 );
    via_marge = s_Clearance + ( ctx.pcbframe->GetDesignSettings().GetCurrentViaSize() / 2 );

    dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
    dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;

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

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

        newTrack->SetEnd( pt_cur_ch->m_PadStart->GetPosition() );
        newTrack->SetStart( g_CurrentTrackSegment->GetEnd() );

        g_CurrentTrackList.PushBack( newTrack );
    }

    g_FirstTrackSegment->start = ctx.board->GetPad( g_FirstTrackSegment,
            ENDPOINT_START );

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

    g_CurrentTrackSegment->end = ctx.board->GetPad( g_CurrentTrackSegment,
            ENDPOINT_END );

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

    // 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->GetNetCode();
    TRACK* firstTrack = g_FirstTrackSegment;
    int    newCount   = g_CurrentTrackList.GetCount();

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

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

    DrawTraces( panel, ctx.dc, firstTrack, newCount, GR_OR );

    ctx.pcbframe->TestNetConnection( ctx.dc, netcode );

    screen->SetModify();
}
