// Emit PADS and PADSTACKS. They are sorted and emitted uniquely.
// Via name is synthesized from their attributes, pads are numbered
static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
{
    std::vector<D_PAD*> pads;
    std::vector<D_PAD*> padstacks;
    std::vector<VIA*>   vias;
    std::vector<VIA*>   viastacks;

    padstacks.resize( 1 ); // We count pads from 1

    // The master layermask (i.e. the enabled layers) for padstack generation
    LSET    master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
    int     cu_count = aPcb->GetCopperLayerCount();

    fputs( "$PADS\n", aFile );

    // Enumerate and sort the pads
    if( aPcb->GetPadCount() > 0 )
    {
        pads = aPcb->GetPads();
        qsort( &pads[0], aPcb->GetPadCount(), sizeof( D_PAD* ),
               PadListSortByShape );
    }

    // The same for vias
    for( VIA* via = GetFirstVia( aPcb->m_Track ); via;
            via = GetFirstVia( via->Next() ) )
    {
        vias.push_back( via );
    }

    qsort( &vias[0], vias.size(), sizeof(VIA*), ViaSort );

    // Emit vias pads
    TRACK* old_via = 0;

    for( unsigned i = 0; i < vias.size(); i++ )
    {
        VIA* via = vias[i];

        if( old_via && 0 == ViaSort( &old_via, &via ) )
            continue;

        old_via = via;
        viastacks.push_back( via );
        fprintf( aFile, "PAD V%d.%d.%s ROUND %g\nCIRCLE 0 0 %g\n",
                via->GetWidth(), via->GetDrillValue(),
                fmt_mask( via->GetLayerSet() ).c_str(),
                via->GetDrillValue() / SCALE_FACTOR,
                via->GetWidth() / (SCALE_FACTOR * 2) );
    }

    // Emit component pads
    D_PAD* old_pad = 0;
    int    pad_name_number = 0;

    for( unsigned i = 0; i<pads.size(); ++i )
    {
        D_PAD* pad = pads[i];

        pad->SetSubRatsnest( pad_name_number );

        if( old_pad && 0==D_PAD::Compare( old_pad, pad ) )
            continue;  // already created

        old_pad = pad;

        pad_name_number++;
        pad->SetSubRatsnest( pad_name_number );

        fprintf( aFile, "PAD P%d", pad->GetSubRatsnest() );

        padstacks.push_back( pad ); // Will have its own padstack later
        int dx = pad->GetSize().x / 2;
        int dy = pad->GetSize().y / 2;

        switch( pad->GetShape() )
        {
        default:
        case PAD_SHAPE_CIRCLE:
            fprintf( aFile, " ROUND %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );
            /* Circle is center, radius */
            fprintf( aFile, "CIRCLE %g %g %g\n",
                    pad->GetOffset().x / SCALE_FACTOR,
                    -pad->GetOffset().y / SCALE_FACTOR,
                    pad->GetSize().x / (SCALE_FACTOR * 2) );
            break;

        case PAD_SHAPE_RECT:
            fprintf( aFile, " RECTANGULAR %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );

            // Rectangle is begin, size *not* begin, end!
            fprintf( aFile, "RECTANGLE %g %g %g %g\n",
                    (-dx + pad->GetOffset().x ) / SCALE_FACTOR,
                    (-dy - pad->GetOffset().y ) / SCALE_FACTOR,
                    dx / (SCALE_FACTOR / 2), dy / (SCALE_FACTOR / 2) );
            break;

        case PAD_SHAPE_OVAL:     // Create outline by 2 lines and 2 arcs
            {
                // OrCAD Layout call them OVAL or OBLONG - GenCAD call them FINGERs
                fprintf( aFile, " FINGER %g\n",
                         pad->GetDrillSize().x / SCALE_FACTOR );
                int dr = dx - dy;

                if( dr >= 0 )       // Horizontal oval
                {
                    int radius = dy;
                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR );

                    // GenCAD arcs are (start, end, center)
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             -pad->GetOffset().y / SCALE_FACTOR );

                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             -pad->GetOffset().y / SCALE_FACTOR );
                }
                else        // Vertical oval
                {
                    dr = -dr;
                    int radius = dx;
                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (-radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             (-radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (-radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             (radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             pad->GetOffset().x / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR );

                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             (-radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             pad->GetOffset().x / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR );
                }
            }
            break;

        case PAD_SHAPE_TRAPEZOID:
            fprintf( aFile, " POLYGON %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );

            // XXX TO BE IMPLEMENTED! and I don't know if it could be actually imported by something
            break;
        }
    }

    fputs( "\n$ENDPADS\n\n", aFile );

    // Now emit the padstacks definitions, using the combined layer masks
    fputs( "$PADSTACKS\n", aFile );

    // Via padstacks
    for( unsigned i = 0; i < viastacks.size(); i++ )
    {
        VIA* via = viastacks[i];

        LSET mask = via->GetLayerSet() & master_layermask;

        fprintf( aFile, "PADSTACK VIA%d.%d.%s %g\n",
                 via->GetWidth(), via->GetDrillValue(),
                 fmt_mask( mask ).c_str(),
                 via->GetDrillValue() / SCALE_FACTOR );

        for( LSEQ seq = mask.Seq( gc_seq, DIM( gc_seq ) );  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD V%d.%d.%s %s 0 0\n",
                    via->GetWidth(), via->GetDrillValue(),
                    fmt_mask( mask ).c_str(),
                    GenCADLayerName( cu_count, layer ).c_str()
                    );
        }
    }

    /* Component padstacks
     *  CAM350 don't apply correctly the FLIP semantics for padstacks, i.e. doesn't
     *  swap the top and bottom layers... so I need to define the shape as MIRRORX
     *  and define a separate 'flipped' padstack... until it appears yet another
     *  noncompliant importer */
    for( unsigned i = 1; i < padstacks.size(); i++ )
    {
        D_PAD* pad = padstacks[i];

        // Straight padstack
        fprintf( aFile, "PADSTACK PAD%u %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );

        LSET pad_set = pad->GetLayerSet() & master_layermask;

        // the special gc_seq
        for( LSEQ seq = pad_set.Seq( gc_seq, DIM( gc_seq ) );  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerName( cu_count, layer ).c_str() );
        }

        // Flipped padstack
        fprintf( aFile, "PADSTACK PAD%uF %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );

        // the normal LAYER_ID sequence is inverted from gc_seq[]
        for( LSEQ seq = pad_set.Seq();  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerNameFlipped( cu_count, layer ).c_str() );
        }
    }

    fputs( "$ENDPADSTACKS\n\n", aFile );
}
bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
{
    FILE_OUTPUTFORMATTER    out( aFullFileName );

    static const char separator[] =
        "    =============================================================\n";

    wxASSERT( m_pcb );

    unsigned    totalHoleCount;
    wxString    brdFilename = m_pcb->GetFileName();

    std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();

    out.Print( 0, "Drill report for %s\n", TO_UTF8( brdFilename ) );
    out.Print( 0, "Created on %s\n\n", TO_UTF8( DateAndTime() ) );

    // Output the cu layer stackup, so layer name references make sense.
    out.Print( 0, "Copper Layer Stackup:\n" );
    out.Print( 0, separator );

    LSET cu = m_pcb->GetEnabledLayers() & LSET::AllCuMask();

    int conventional_layer_num = 1;

    for( LSEQ seq = cu.Seq();  seq;  ++seq, ++conventional_layer_num )
    {
        out.Print( 0, "    L%-2d:  %-25s %s\n",
            conventional_layer_num,
            TO_UTF8( m_pcb->GetLayerName( *seq ) ),
            layerName( *seq ).c_str()       // generic layer name
            );
    }

    out.Print( 0, "\n\n" );

    /* output hole lists:
     * 1 - through holes
     * 2 - for partial holes only: by layer starting and ending pair
     * 3 - Non Plated through holes
     */

    bool buildNPTHlist = false;     // First pass: build PTH list only

    // in this loop are plated only:
    for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size();  ++pair_ndx )
    {
        DRILL_LAYER_PAIR  pair = hole_sets[pair_ndx];

        buildHolesList( pair, buildNPTHlist );

        if( pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
        {
            out.Print( 0, "Drill file '%s' contains\n",
                TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );

            out.Print( 0, "    plated through holes:\n" );
            out.Print( 0, separator );
            totalHoleCount = printToolSummary( out, false );
            out.Print( 0, "    Total plated holes count %u\n", totalHoleCount );
        }
        else    // blind/buried
        {
            out.Print( 0, "Drill file '%s' contains\n",
                TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );

            out.Print( 0, "    holes connecting layer pair: '%s and %s' (%s vias):\n",
                TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.first ) ) ),
                TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.second ) ) ),
                pair.first == F_Cu || pair.second == B_Cu ? "blind" : "buried"
                );

            out.Print( 0, separator );
            totalHoleCount = printToolSummary( out, false );
            out.Print( 0, "    Total plated holes count %u\n", totalHoleCount );
        }

        out.Print( 0, "\n\n" );
    }

    // NPTHoles. Generate the full list (pads+vias) if PTH and NPTH are merged,
    // or only the NPTH list (which never has vias)
    if( !m_merge_PTH_NPTH )
        buildNPTHlist = true;

    buildHolesList( DRILL_LAYER_PAIR( F_Cu, B_Cu ), buildNPTHlist );

    // nothing wrong with an empty NPTH file in report.
    if( m_merge_PTH_NPTH )
        out.Print( 0, "Not plated through holes are merged with plated holes\n" );
    else
        out.Print( 0, "Drill file '%s' contains\n",
                   TO_UTF8( getDrillFileName( DRILL_LAYER_PAIR( F_Cu, B_Cu ),
                   true, m_merge_PTH_NPTH ) ) );

    out.Print( 0, "    unplated through holes:\n" );
    out.Print( 0, separator );
    totalHoleCount = printToolSummary( out, true );
    out.Print( 0, "    Total unplated holes count %u\n", totalHoleCount );

    return true;
}
/* Plot outlines of copper, for copper layer
 */
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
                        LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
{

    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerSet( aLayerMask );

    SHAPE_POLY_SET outlines;

    for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) );  seq;  ++seq )
    {
        LAYER_ID layer = *seq;

        outlines.RemoveAllContours();
        aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );

        outlines.Simplify();

        // Plot outlines
        std::vector< wxPoint > cornerList;

        // Now we have one or more basic polygons: plot each polygon
        for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
        {
            for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ )
            {
                cornerList.clear();
                const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 );

                for( int jj = 0; jj < path.PointCount(); jj++ )
                    cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );


                // Ensure the polygon is closed
                if( cornerList[0] != cornerList[cornerList.size() - 1] )
                    cornerList.push_back( cornerList[0] );

                aPlotter->PlotPoly( cornerList, NO_FILL );
            }
        }

        // Plot pad holes
        if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
        {
            for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
            {
                for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
                {
                    wxSize hole = pad->GetDrillSize();

                    if( hole.x == 0 || hole.y == 0 )
                        continue;

                    if( hole.x == hole.y )
                        aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL );
                    else
                    {
                        wxPoint drl_start, drl_end;
                        int width;
                        pad->GetOblongDrillGeometry( drl_start, drl_end, width );
                        aPlotter->ThickSegment( pad->GetPosition() + drl_start,
                                                pad->GetPosition() + drl_end, width, SKETCH );
                    }
                }
            }
        }

        // Plot vias holes
        for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
        {
            const VIA* via = dyn_cast<const VIA*>( track );

            if( via && via->IsOnLayer( layer ) )    // via holes can be not through holes
            {
                aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL );
            }
        }
    }
}