/* Creates the footprint shape list.
 * Since module shape is customizable after the placement we cannot share them;
 * instead we opt for the one-module-one-shape-one-component-one-device approach
 */
static void CreateShapesSection( FILE* aFile, BOARD* aPcb )
{
    MODULE*     module;
    D_PAD*      pad;
    const char* layer;
    wxString    pinname;
    const char* mirror = "0";

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

    const LSET all_cu = LSET::AllCuMask();

    for( module = aPcb->m_Modules; module; module = module->Next() )
    {
        FootprintWriteShape( aFile, module );

        for( pad = module->Pads(); pad; pad = pad->Next() )
        {
            /* Funny thing: GenCAD requires the pad side even if you use
             *  padstacks (which are theorically optional but gerbtools
             *requires* them). Now the trouble thing is that 'BOTTOM'
             *  is interpreted by someone as a padstack flip even
             *  if the spec explicitly says it's not... */
            layer = "ALL";

            if( ( pad->GetLayerSet() & all_cu ) == LSET( B_Cu ) )
            {
                layer = module->GetFlag() ? "TOP" : "BOTTOM";
            }
            else if( ( pad->GetLayerSet() & all_cu ) == LSET( F_Cu ) )
            {
                layer = module->GetFlag() ? "BOTTOM" : "TOP";
            }

            pad->StringPadName( pinname );

            if( pinname.IsEmpty() )
                pinname = wxT( "none" );

            double orient = pad->GetOrientation() - module->GetOrientation();
            NORMALIZE_ANGLE_POS( orient );

            // Bottom side modules use the flipped padstack
            fprintf( aFile, (module->GetFlag()) ?
                     "PIN %s PAD%dF %g %g %s %g %s\n" :
                     "PIN %s PAD%d %g %g %s %g %s\n",
                     TO_UTF8( pinname ), pad->GetSubRatsnest(),
                     pad->GetPos0().x / SCALE_FACTOR,
                     -pad->GetPos0().y / SCALE_FACTOR,
                     layer, orient / 10.0, mirror );
        }
    }

    fputs( "$ENDSHAPES\n\n", aFile );
}
/**
 * Function PickModule
 * find the "best" module place
 * The criteria are:
 * - Maximum ratsnest with modules already placed
 * - Max size, and number of pads max
 */
static MODULE* PickModule( PCB_EDIT_FRAME* pcbframe, wxDC* DC )
{
    MODULE* Module;
    std::vector <MODULE*> moduleList;

    // Build sorted footprints list (sort by decreasing size )
    Module = pcbframe->GetBoard()->m_Modules;

    for( ; Module != NULL; Module = Module->Next() )
    {
        Module->CalculateBoundingBox();
        moduleList.push_back( Module );
    }

    sort( moduleList.begin(), moduleList.end(), Tri_PlaceModules );

    for( unsigned ii = 0; ii < moduleList.size(); ii++ )
    {
        Module = moduleList[ii];
        Module->SetFlag( 0 );

        if( !Module->NeedsPlaced() )
            continue;

        pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK;
        pcbframe->SetMsgPanel( Module );
        pcbframe->build_ratsnest_module( Module );

        // Calculate external ratsnest.
        for( unsigned ii = 0; ii < pcbframe->GetBoard()->m_LocalRatsnest.size(); ii++ )
        {
            if( ( pcbframe->GetBoard()->m_LocalRatsnest[ii].m_Status &
                  LOCAL_RATSNEST_ITEM ) == 0 )
                Module->IncrementFlag();
        }
    }

    pcbframe->GetBoard()->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK;

    sort( moduleList.begin(), moduleList.end(), sortFootprintsByRatsnestSize );

    // Search for "best" module.
    MODULE* bestModule  = NULL;
    MODULE* altModule   = NULL;

    for( unsigned ii = 0; ii < moduleList.size(); ii++ )
    {
        Module = moduleList[ii];

        if( !Module->NeedsPlaced() )
            continue;

        altModule = Module;

        if( Module->GetFlag() == 0 )
            continue;

        bestModule = Module;
        break;
    }

    if( bestModule )
        return bestModule;
    else
        return altModule;
}
/* Creates the section $COMPONENTS (Footprints placement)
 * Bottom side components are difficult to handle: shapes must be mirrored or
 * flipped, silk layers need to be handled correctly and so on. Also it seems
 * that *noone* follows the specs...
 */
static void CreateComponentsSection( FILE* aFile, BOARD* aPcb )
{
    fputs( "$COMPONENTS\n", aFile );

    int cu_count = aPcb->GetCopperLayerCount();

    for( MODULE* module = aPcb->m_Modules; module; module = module->Next() )
    {
        const char*   mirror;
        const char*   flip;
        double        fp_orient = module->GetOrientation();

        if( module->GetFlag() )
        {
            mirror = "0";
            flip   = "FLIP";
            NEGATE_AND_NORMALIZE_ANGLE_POS( fp_orient );
        }
        else
        {
            mirror = "0";
            flip   = "0";
        }

        fprintf( aFile, "\nCOMPONENT %s\n",
                 TO_UTF8( module->GetReference() ) );
        fprintf( aFile, "DEVICE %s_%s\n",
                 TO_UTF8( module->GetReference() ),
                 TO_UTF8( module->GetValue() ) );
        fprintf( aFile, "PLACE %g %g\n",
                 MapXTo( module->GetPosition().x ),
                 MapYTo( module->GetPosition().y ) );
        fprintf( aFile, "LAYER %s\n",
                 (module->GetFlag()) ? "BOTTOM" : "TOP" );
        fprintf( aFile, "ROTATION %g\n",
                 fp_orient / 10.0 );
        fprintf( aFile, "SHAPE %s %s %s\n",
                 TO_UTF8( module->GetReference() ),
                 mirror, flip );

        // Text on silk layer: RefDes and value (are they actually useful?)
        TEXTE_MODULE *textmod = &module->Reference();

        for( int ii = 0; ii < 2; ii++ )
        {
            double      txt_orient = textmod->GetOrientation();
            std::string layer  = GenCADLayerName( cu_count, module->GetFlag() ? B_SilkS : F_SilkS );

            fprintf( aFile, "TEXT %g %g %g %g %s %s \"%s\"",
                     textmod->GetPos0().x / SCALE_FACTOR,
                    -textmod->GetPos0().y / SCALE_FACTOR,
                     textmod->GetSize().x / SCALE_FACTOR,
                     txt_orient / 10.0,
                     mirror,
                     layer.c_str(),
                     TO_UTF8( textmod->GetText() ) );

            // Please note, the width is approx
            fprintf( aFile, " 0 0 %g %g\n",
                     ( textmod->GetSize().x * textmod->GetLength() ) / SCALE_FACTOR,
                     textmod->GetSize().y / SCALE_FACTOR );

            textmod = &module->Value(); // Dirty trick for the second iteration
        }

        // The SHEET is a 'generic description' for referencing the component
        fprintf( aFile, "SHEET \"RefDes: %s, Value: %s\"\n",
                 TO_UTF8( module->GetReference() ),
                 TO_UTF8( module->GetValue() ) );
    }

    fputs( "$ENDCOMPONENTS\n\n", aFile );
}
/* Driver function: processing starts here */
void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& aEvent )
{
    wxFileName  fn = GetBoard()->GetFileName();
    FILE*       file;

    wxString    ext = wxT( "cad" );
    wxString    wildcard = _( "GenCAD 1.4 board files (.cad)|*.cad" );

    fn.SetExt( ext );

    wxString pro_dir = wxPathOnly( Prj().GetProjectFullName() );

    wxFileDialog dlg( this, _( "Save GenCAD Board File" ), pro_dir,
                      fn.GetFullName(), wildcard,
                      wxFD_SAVE | wxFD_OVERWRITE_PROMPT );

    if( dlg.ShowModal() == wxID_CANCEL )
        return;

    if( ( file = wxFopen( dlg.GetPath(), wxT( "wt" ) ) ) == NULL )
    {
        wxString    msg;

        msg.Printf( _( "Unable to create <%s>" ), GetChars( dlg.GetPath() ) );
        DisplayError( this, msg ); return;
    }

    SetLocaleTo_C_standard(); // No pesky decimal separators in gencad

    // Update some board data, to ensure a reliable gencad export
    GetBoard()->ComputeBoundingBox();

    // Save the auxiliary origin for the rest of the module
    GencadOffsetX = GetAuxOrigin().x;
    GencadOffsetY = GetAuxOrigin().y;

    // No idea on *why* this should be needed... maybe to fix net names?
    Compile_Ratsnest( NULL, true );

    /* Temporary modification of footprints that are flipped (i.e. on bottom
     * layer) to convert them to non flipped footprints.
     *  This is necessary to easily export shapes to GenCAD,
     *  that are given as normal orientation (non flipped, rotation = 0))
     * these changes will be undone later
     */
    BOARD*  pcb = GetBoard();
    MODULE* module;

    for( module = pcb->m_Modules; module; module = module->Next() )
    {
        module->SetFlag( 0 );

        if( module->GetLayer() == B_Cu )
        {
            module->Flip( module->GetPosition() );
            module->SetFlag( 1 );
        }
    }

    /* Gencad has some mandatory and some optional sections: some importer
     *  need the padstack section (which is optional) anyway. Also the
     *  order of the section *is* important */

    CreateHeaderInfoData( file, this );     // Gencad header
    CreateBoardSection( file, pcb );        // Board perimeter

    CreatePadsShapesSection( file, pcb );   // Pads and padstacks
    CreateArtworksSection( file );          // Empty but mandatory

    /* Gencad splits a component info in shape, component and device.
     *  We don't do any sharing (it would be difficult since each module is
     *  customizable after placement) */
    CreateShapesSection( file, pcb );
    CreateComponentsSection( file, pcb );
    CreateDevicesSection( file, pcb );

    // In a similar way the netlist is split in net, track and route
    CreateSignalsSection( file, pcb );
    CreateTracksInfoData( file, pcb );
    CreateRoutesSection( file, pcb );

    fclose( file );
    SetLocaleTo_Default();  // revert to the current locale

    // Undo the footprints modifications (flipped footprints)
    for( module = pcb->m_Modules; module; module = module->Next() )
    {
        if( module->GetFlag() )
        {
            module->Flip( module->GetPosition() );
            module->SetFlag( 0 );
        }
    }
}