/*******************************************************/
/* Module de generation de la Netliste , selon Formats */
/*******************************************************/

#include "fctsys.h"
#include "gr_basic.h"

#include "common.h"
#include "program.h"
#include "libcmp.h"
#include "general.h"
#include "netlist.h"

#include "protos.h"


/* Routines locales */
static void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& FullFileName );
static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f,
                                bool with_pcbnew );
static void WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f );
static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet );
static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, bool use_netnames );

static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet );
static void AddPinToComponentPinList( EDA_SchComponentStruct* Component,
                                      LibDrawPin*             PinEntry );
static void FindOthersUnits( EDA_SchComponentStruct* Component );
static int  SortPinsByNum( ObjetNetListStruct** Pin1, ObjetNetListStruct** Pin2 );
static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin );

static void ClearUsedFlags( WinEDA_SchematicFrame* frame );


/* Variable locales */
static int s_SortedPinCount;
static ObjetNetListStruct** s_SortedComponentPinList;


/******************************************************************************/
void WriteNetList( WinEDA_SchematicFrame* frame, const wxString& FileNameNL,
                   bool use_netnames )
/*******************************************************************************/

/* Create the netlist file ( Format is given by g_NetFormat )
 *  bool use_netnames is used only for Spice netlist
 */
{
    FILE*        f = NULL;

    if( g_NetFormat < NET_TYPE_CUSTOM1 )
    {
        if( ( f = wxFopen( FileNameNL, wxT( "wt" ) ) ) == NULL )
        {
            wxString msg = _( "Failed to create file " ) + FileNameNL;
            DisplayError( frame, msg );
            return;
        }
    }

    wxBusyCursor Busy;

    switch( g_NetFormat )
    {
    case NET_TYPE_PCBNEW:
        WriteNetListPCBNEW( frame, f, TRUE );
        fclose( f );
        break;

    case NET_TYPE_ORCADPCB2:
        WriteNetListPCBNEW( frame, f, FALSE );
        fclose( f );
        break;

    case NET_TYPE_CADSTAR:
        WriteNetListCADSTAR( frame, f );
        fclose( f );
        break;

    case NET_TYPE_SPICE:
        WriteNetListPspice( frame, f, use_netnames );
        fclose( f );
        break;

     default:
       Write_GENERIC_NetList( frame, FileNameNL );
        break;
   }
}


/****************************************************************************/
static EDA_SchComponentStruct* FindNextComponentAndCreatPinList(
    EDA_BaseStruct* DrawList )
/****************************************************************************/

/*	Find a "suitable" component from the DrawList
 *  build its pin list s_SortedComponentPinList.
 *  The list is sorted by pin num
 *  A suitable component is a "new" real component (power symbols are not considered)
 * 
 *  alloc memory for s_SortedComponentPinList if s_SortedComponentPinList == NULL
 *  Must be deallocated by the user
 */
{
    EDA_SchComponentStruct* Component = NULL;
    EDA_LibComponentStruct* Entry;
    LibEDA_BaseStruct*      DEntry;

    s_SortedPinCount = 0;

    for( ; DrawList != NULL; DrawList = DrawList->Next() )
    {
        if( DrawList->Type() != DRAW_LIB_ITEM_STRUCT_TYPE )
            continue;
        Component = (EDA_SchComponentStruct*) DrawList;

        /* already tested ? : */
        if( Component->m_FlagControlMulti == 1 )
            continue;                                      /* yes */

        Entry = FindLibPart( Component->m_ChipName.GetData(), wxEmptyString, FIND_ROOT );
        if( Entry  == NULL )
            continue;

        /* Power symbol and other component which have the reference starting by
         *  "#" are not included in netlist (pseudo components) */
        if( Component->m_Field[REFERENCE].m_Text[0] == '#' )
            continue;

        /* Create the pin table for this component */
        int ii = sizeof(ObjetNetListStruct) * MAXPIN;
        if( s_SortedComponentPinList == NULL )
            s_SortedComponentPinList = (ObjetNetListStruct**) MyMalloc( ii );
        memset( s_SortedComponentPinList, 0, ii );

        DEntry = Entry->m_Drawings;
        for( ; DEntry != NULL; DEntry = DEntry->Next() )
        {
            if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE )
                continue;
            if( DEntry->m_Unit
               && (DEntry->m_Unit != Component->m_Multi) )
                continue;
            if( DEntry->m_Convert
               && (DEntry->m_Convert != Component->m_Convert) )
                continue;
            {
                AddPinToComponentPinList( Component, (LibDrawPin*) DEntry );
            }
        }

        Component->m_FlagControlMulti = 1;

        if( Entry->m_UnitCount > 1 )
            FindOthersUnits( Component );

        /* Tri sur le numero de Pin de TabListePin */
        qsort( s_SortedComponentPinList, s_SortedPinCount, sizeof(ObjetNetListStruct*),
               ( int( * ) ( const void*, const void* ) )SortPinsByNum );

        /* Elimination des Pins redondantes du s_SortedComponentPinList */
        EraseDuplicatePins( s_SortedComponentPinList, s_SortedPinCount );

        return Component;
    }

    return NULL;
}


/**************************************************************************************/
static wxString ReturnPinNetName( ObjetNetListStruct* Pin,
                                  const wxString&     DefaultFormatNetname )
/**************************************************************************************/

/* Return the net name for the pin Pin.
 *  Net name is:
 *  "?" if pin not connected
 *  "netname" for global net (like gnd, vcc ..
 *  "netname_sheetnumber" for the usual nets
 */
{
    int      netcode = Pin->GetNet();
    wxString NetName;

    if( (netcode == 0 ) || ( Pin->m_FlagOfConnection != PAD_CONNECT ) )
    {
        return NetName;
    }
    else
    {
        int jj;
        for( jj = 0; jj < g_NbrObjNet; jj++ )
        {
            if( g_TabObjNet[jj].GetNet() != netcode )
                continue;
            if( ( g_TabObjNet[jj].m_Type != NET_GLOBLABEL)
               && ( g_TabObjNet[jj].m_Type != NET_LABEL)
               && ( g_TabObjNet[jj].m_Type != NET_PINLABEL) )
                continue;

            NetName = *g_TabObjNet[jj].m_Label;
            break;
        }

        if( !NetName.IsEmpty() )
        {
            if( g_TabObjNet[jj].m_Type != NET_PINLABEL )
                NetName << wxT( "_" ) << g_TabObjNet[jj].m_SheetNumber;
        }
        else
        {
            NetName.Printf( DefaultFormatNetname.GetData(), netcode );
        }
    }
    return NetName;
}


/***********************************************************************/
void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame,
                            const wxString&        FullFileName )
/***********************************************************************/

/* Create a generic netlist, and call an external netlister
 *  to change the netlist syntax and create the file
 */
{
    wxString                Line, FootprintName;
    BASE_SCREEN*            CurrScreen;
    EDA_BaseStruct*         DrawList;
    EDA_SchComponentStruct* Component;
    wxString                netname;
    int ii;
    FILE* tmpfile;
    wxString                TmpFullFileName = FullFileName;

    ChangeFileNameExt( TmpFullFileName, wxT( ".tmp" ) );

    if( ( tmpfile = wxFopen( TmpFullFileName, wxT( "wt" ) ) ) == NULL )
    {
        wxString msg = _( "Failed to create file " ) + TmpFullFileName;
        DisplayError( frame, msg );
        return;
    }

    ClearUsedFlags( frame );  /* Reset the flags FlagControlMulti in all schematic files*/
    fprintf( tmpfile, "$BeginNetlist\n" );

    /* Create netlist module section */
    fprintf( tmpfile, "$BeginComponentList\n" );
    EDA_ScreenList ScreenList( NULL );
    for( CurrScreen = ScreenList.GetFirst(); CurrScreen != NULL; CurrScreen = ScreenList.GetNext() )
    {
        for( DrawList = CurrScreen->EEDrawList; DrawList != NULL; DrawList = DrawList->Pnext )
        {
            DrawList = Component = FindNextComponentAndCreatPinList( DrawList );
            if( Component == NULL )
                break;                      // No component left

            FootprintName.Empty();
            if( !Component->m_Field[FOOTPRINT].IsVoid() )
            {
                FootprintName = Component->m_Field[FOOTPRINT].m_Text;
                FootprintName.Replace( wxT( " " ), wxT( "_" ) );
            }

            fprintf( tmpfile, "\n$BeginComponent\n" );
            fprintf( tmpfile, "TimeStamp=%8.8lX\n", Component->m_TimeStamp );
            fprintf( tmpfile, "Footprint=%s\n", CONV_TO_UTF8( FootprintName ) );
            Line = wxT( "Reference=" ) + Component->m_Field[REFERENCE].m_Text + wxT( "\n" );
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( tmpfile, CONV_TO_UTF8( Line ) );

            Line = Component->m_Field[VALUE].m_Text;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( tmpfile, "Value=%s\n", CONV_TO_UTF8( Line ) );

            Line = Component->m_ChipName;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( tmpfile, "Libref=%s\n", CONV_TO_UTF8( Line ) );

            // Write pin list:
            fprintf( tmpfile, "$BeginPinList\n" );
            for( ii = 0; ii < s_SortedPinCount; ii++ )
            {
                ObjetNetListStruct* Pin = s_SortedComponentPinList[ii];
                if( !Pin )
                    continue;
                netname = ReturnPinNetName( Pin, wxT( "$-%.6d" ) );
                if( netname.IsEmpty() )
                    netname = wxT( "?" );
                fprintf( tmpfile, "%.4s=%s\n", (char*) &Pin->m_PinNum,
                        CONV_TO_UTF8( netname ) );
            }

            fprintf( tmpfile, "$EndPinList\n" );
            fprintf( tmpfile, "$EndComponent\n" );
        }
    }

    MyFree( s_SortedComponentPinList );
    s_SortedComponentPinList = NULL;

    fprintf( tmpfile, "$EndComponentList\n" );

    fprintf( tmpfile, "\n$BeginNets\n" );
    WriteGENERICListOfNets( tmpfile, g_TabObjNet );
    fprintf( tmpfile, "$EndNets\n" );
    fprintf( tmpfile, "\n$EndNetlist\n" );
    fclose( tmpfile );

    // Call the external module (plug in )

    if( g_NetListerCommandLine.IsEmpty() )
        return;

    wxString CommandFile;
    if( wxIsAbsolutePath( g_NetListerCommandLine ) )
        CommandFile = g_NetListerCommandLine;
    else
        CommandFile = FindKicadFile( g_NetListerCommandLine );

    CommandFile += wxT( " " ) + TmpFullFileName;
    CommandFile += wxT( " " ) + FullFileName;

    wxExecute( CommandFile, wxEXEC_SYNC );
}


/********************************************************/
static void ClearUsedFlags( WinEDA_SchematicFrame* frame )
/********************************************************/
/* Clear flag FlagControlMulti, used in netlist generation */
{
    SCH_SCREEN*     screen;
    EDA_BaseStruct* DrawList;

    EDA_ScreenList ScreenList( NULL );

    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
    {
        DrawList = screen->EEDrawList;
        while( DrawList )
        {
            if( DrawList->Type() == DRAW_LIB_ITEM_STRUCT_TYPE )
            {
                EDA_SchComponentStruct* Component = (EDA_SchComponentStruct*) DrawList;
                Component->m_FlagControlMulti = 0;
            }
            DrawList = DrawList->Pnext;
        }
    }
}


/*************************************************************/
static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f,
                                bool use_netnames )
/*************************************************************/

/* Routine de generation du fichier netliste ( Format PSPICE )
 *  si use_netnames = TRUE
 *      les nodes sont identifies par le netname
 *  sinon	les nodes sont identifies par le netnumber
 * 
 *  tous les textes graphiques commençant par [.-+]pspice ou  [.-+]gnucap
 *  sont considérés comme des commandes a placer dans la netliste
 *      [.-]pspice ou gnucap sont en debut
 +pspice et +gnucap sont en fin de netliste
 */
{
    char                    Line[1024];
    SCH_SCREEN*             screen;
    EDA_BaseStruct*         DrawList;
    EDA_SchComponentStruct* Component;
    int ii, nbitems;
    wxString                text;
    wxArrayString           SpiceCommandAtBeginFile, SpiceCommandAtEndFile;
    wxString                msg;

#define BUFYPOS_LEN 4
    wxChar                  bufnum[BUFYPOS_LEN + 1];

    DateAndTime( Line );
    fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, Line );

    /* Create text list starting by [.-]pspice , or [.-]gnucap (simulator commands) */
    /* and create text list starting by [+]pspice , or [+]gnucap (simulator commands) */
    bufnum[BUFYPOS_LEN] = 0;
    EDA_ScreenList ScreenList( NULL );

    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
    {
        for( DrawList = screen->EEDrawList; DrawList != NULL; DrawList = DrawList->Pnext )
        {
            wxChar ident;
            if( DrawList->Type() != DRAW_TEXT_STRUCT_TYPE )
                continue;
            #define DRAWTEXT ( (DrawTextStruct*) DrawList )
            text  = DRAWTEXT->m_Text; if( text.IsEmpty() )
                continue;
            ident = text.GetChar( 0 );
            if( ident != '.' && ident != '-' && ident != '+' )
                continue;
            text.Remove( 0, 1 );    //Remove the first char.
            text.Remove( 6 );       //text contains 6 char.
            if( ( text == wxT( "pspice" ) ) || ( text == wxT( "gnucap" ) ) )
            {
                /* Put the Y position as an ascii string, for sort by vertical position,
                 *  using usual sort string by alphabetic value */
                int ypos = DRAWTEXT->m_Pos.y;
                for( ii = 0; ii < BUFYPOS_LEN; ii++ )
                {
                    bufnum[BUFYPOS_LEN - 1 - ii] = (ypos & 63) + ' '; ypos >>= 6;
                }

                text = DRAWTEXT->m_Text.AfterFirst( ' ' );
                msg.Printf( wxT( "%s %s" ), bufnum, text.GetData() );    // First BUFYPOS_LEN char are the Y position
                if( ident == '+' )
                    SpiceCommandAtEndFile.Add( msg );
                else
                    SpiceCommandAtBeginFile.Add( msg );
            }
        }
    }

    /* Print texts starting by [.-]pspice , ou [.-]gnucap (of course, without the Y position string)*/
    nbitems = SpiceCommandAtBeginFile.GetCount();
    if( nbitems )
    {
        SpiceCommandAtBeginFile.Sort();
        for( ii = 0; ii < nbitems; ii++ )
        {
            SpiceCommandAtBeginFile[ii].Remove( 0, BUFYPOS_LEN );
            SpiceCommandAtBeginFile[ii].Trim( TRUE );
            SpiceCommandAtBeginFile[ii].Trim( FALSE );
            fprintf( f, "%s\n", CONV_TO_UTF8( SpiceCommandAtBeginFile[ii] ) );
        }
    }
    fprintf( f, "\n" );


    /* Create component list */
    ClearUsedFlags( frame );  /* Reset the flags FlagControlMulti in all schematic files*/
    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
    {
        for( DrawList = screen->EEDrawList; DrawList != NULL; DrawList = DrawList->Pnext )
        {
            DrawList = Component = FindNextComponentAndCreatPinList( DrawList );
            if( Component == NULL )
                break;

            fprintf( f, "%s ", CONV_TO_UTF8( Component->m_Field[REFERENCE].m_Text ) );

            // Write pin list:
            for( ii = 0; ii < s_SortedPinCount; ii++ )
            {
                ObjetNetListStruct* Pin = s_SortedComponentPinList[ii];
                if( !Pin )
                    continue;
                wxString            NetName = ReturnPinNetName( Pin, wxT( "N-%.6d" ) );
                if( NetName.IsEmpty() )
                    NetName = wxT( "?" );
                if( use_netnames )
                    fprintf( f, " %s", CONV_TO_UTF8( NetName ) );
                else    // Use number for net names (with net number = 0 for "GND"
                {
                    // NetName = "0" is "GND" net for Spice
                    if( NetName == wxT( "0" ) || NetName == wxT( "GND" ) )
                        fprintf( f, " 0" );
                    else
                        fprintf( f, " %d", Pin->GetNet() );
                }
            }

            fprintf( f, " %s\n", CONV_TO_UTF8( Component->m_Field[VALUE].m_Text ) );
        }
    }

    MyFree( s_SortedComponentPinList );
    s_SortedComponentPinList = NULL;

    /* Print texts starting by [+]pspice , ou [+]gnucap */
    nbitems = SpiceCommandAtEndFile.GetCount();
    if( nbitems )
    {
        fprintf( f, "\n" );
        SpiceCommandAtEndFile.Sort();
        for( ii = 0; ii < nbitems; ii++ )
        {
            SpiceCommandAtEndFile[ii].Remove( 0, +BUFYPOS_LEN );
            SpiceCommandAtEndFile[ii].Trim( TRUE );
            SpiceCommandAtEndFile[ii].Trim( FALSE );
            fprintf( f, "%s\n", CONV_TO_UTF8( SpiceCommandAtEndFile[ii] ) );
        }
    }

    fprintf( f, "\n.end\n" );
}


/*****************************************************************************************/
static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with_pcbnew )
/*****************************************************************************************/

/* Routine de generation du fichier netliste ( Format ORCAD PCB 2 ameliore )
 *  si with_pcbnew = FALSE
 *      format PCBNEW (OrcadPcb2 + commentaires et liste des nets)
 *  si with_pcbnew = FALSE
 *      Format ORCADPCB2 strict
 */
{
    wxString Line, FootprintName;
    char Buf[256];
    SCH_SCREEN* CurrScreen;
    EDA_BaseStruct* DrawList;
    EDA_SchComponentStruct* Component;
    int ii;
    EDA_SchComponentStruct** CmpList = NULL;
    int CmpListCount = 0, CmpListSize = 1000;

    DateAndTime( Buf );
    if( with_pcbnew )
        fprintf( f, "# %s created  %s\n(\n", NETLIST_HEAD_STRING, Buf );
    else
        fprintf( f, "( { %s created  %s }\n", NETLIST_HEAD_STRING, Buf );


    /* Create netlist module section */
    ClearUsedFlags( frame );  /* Reset the flags FlagControlMulti in all schematic files*/

    EDA_ScreenList ScreenList( NULL );

    for( CurrScreen = ScreenList.GetFirst(); CurrScreen != NULL; CurrScreen = ScreenList.GetNext() )
    {
        for( DrawList = CurrScreen->EEDrawList; DrawList != NULL; DrawList = DrawList->Pnext )
        {
            DrawList = Component = FindNextComponentAndCreatPinList( DrawList );
            if( Component == NULL )
                break;

            /* Get the Component FootprintFilter and put the component in CmpList if filter is not void */
            EDA_LibComponentStruct* Entry;
            if( ( Entry = FindLibPart( Component->m_ChipName.GetData(), wxEmptyString,
                                       FIND_ROOT ) ) != NULL )
            {
                if( Entry->m_FootprintList.GetCount() != 0 )   /* Put in list */
                {
                    if( CmpList == NULL )
                        CmpList = (EDA_SchComponentStruct**) MyZMalloc( sizeof(
                                                                            EDA_SchComponentStruct
                                                                            *) * CmpListSize );
                    if( CmpListCount >= CmpListSize )
                    {
                        CmpListSize += 1000;
                        CmpList = (EDA_SchComponentStruct**) realloc(
                            CmpList,
                            sizeof(
                                EDA_SchComponentStruct*)
                            * CmpListSize );
                    }
                    CmpList[CmpListCount] = Component;
                    CmpListCount++;
                }
            }

            if( !Component->m_Field[FOOTPRINT].IsVoid() )
            {
                FootprintName = Component->m_Field[FOOTPRINT].m_Text;
                FootprintName.Replace( wxT( " " ), wxT( "_" ) );
            }
            else
                FootprintName = wxT( "$noname" );

            Line = Component->m_Field[REFERENCE].m_Text;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, " ( %8.8lX %s",
                    Component->m_TimeStamp,
                    CONV_TO_UTF8( FootprintName ) );
            fprintf( f, "  %s", CONV_TO_UTF8( Line ) );

            Line = Component->m_Field[VALUE].m_Text;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, " %s", CONV_TO_UTF8( Line ) );

            if( with_pcbnew )  // Add the lib name for this component
            {
                Line = Component->m_ChipName;
                Line.Replace( wxT( " " ), wxT( "_" ) );
                fprintf( f, " {Lib=%s}", CONV_TO_UTF8( Line ) );
            }
            fprintf( f, "\n" );

            // Write pin list:
            for( ii = 0; ii < s_SortedPinCount; ii++ )
            {
                ObjetNetListStruct* Pin = s_SortedComponentPinList[ii];
                if( !Pin )
                    continue;
                wxString netname = ReturnPinNetName( Pin, wxT( "N-%.6d" ) );
                if( netname.IsEmpty() )
                    netname = wxT( " ?" );
                fprintf( f, "  ( %4.4s %s )\n", (char*) &Pin->m_PinNum,
                        CONV_TO_UTF8( netname ) );
            }

            fprintf( f, " )\n" );
        }
    }

    fprintf( f, ")\n*\n" );

    MyFree( s_SortedComponentPinList );
    s_SortedComponentPinList = NULL;

    /* Write the allowed footprint list for each component */
    if( with_pcbnew && CmpList )
    {
        fprintf( f, "{ Allowed footprints by component:\n" );
        EDA_LibComponentStruct* Entry;
        for( ii = 0; ii < CmpListCount; ii++ )
        {
            Component = CmpList[ii];
            Entry = FindLibPart( Component->m_ChipName.GetData(), wxEmptyString, FIND_ROOT );
            Line  = Component->m_Field[REFERENCE].m_Text;
            Line.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, "$component %s\n", CONV_TO_UTF8( Line ) );
            /* Write the footprint list */
            for( unsigned int jj = 0; jj < Entry->m_FootprintList.GetCount(); jj++ )
            {
                fprintf( f, " %s\n", CONV_TO_UTF8( Entry->m_FootprintList[jj] ) );
            }

            fprintf( f, "$endlist\n" );
        }

        fprintf( f, "$endfootprintlist\n}\n" );
    }
    if( CmpList )
        free( CmpList );

    if( with_pcbnew )
    {
        fprintf( f, "{ Pin List by Nets\n" );
        WriteGENERICListOfNets( f, g_TabObjNet );
        fprintf( f, "}\n" );
        fprintf( f, "#End\n" );
    }
}


/*************************************************************************************/
static void AddPinToComponentPinList( EDA_SchComponentStruct* Component, LibDrawPin* Pin )
/*************************************************************************************/

/* Add a new pin description in the pin list s_SortedComponentPinList
 *  a pin description is a pointer to the corresponding structure
 *  created by BuildNetList() in the table g_TabObjNet
 */
{
    int ii;

    /* Search the PIN description for Pin in g_TabObjNet*/
    for( ii = 0; ii < g_NbrObjNet; ii++ )
    {
        if( g_TabObjNet[ii].m_Type != NET_PIN )
            continue;
        if( g_TabObjNet[ii].m_Link != Component )
            continue;
        if( g_TabObjNet[ii].m_PinNum != Pin->m_PinNum )
            continue;
        {
            s_SortedComponentPinList[s_SortedPinCount] = &g_TabObjNet[ii];
            s_SortedPinCount++;
            if( s_SortedPinCount >= MAXPIN )
            {
                /* Log message for Internal error */
                DisplayError( NULL, wxT( "AddPinToComponentPinList err: MAXPIN reached" ) );
                return;
            }
        }
    }
}


/**********************************************************************/
static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin )
/**********************************************************************/

/*
 *  Routine qui elimine les Pins de meme Numero de la liste des objets
 *  (Liste des Pins d'un boitier) passee en entree
 *  Ces pins redondantes proviennent des pins (alims... ) communes a plusieurs
 *  elements d'un boitier a multiple parts.
 */
{
    int ii, jj;

    for( ii = 0; ii < NbrPin - 1; ii++ )
    {
        if( TabPin[ii] == NULL )
            continue;                       /* Deja supprime */
        if( TabPin[ii]->m_PinNum != TabPin[ii + 1]->m_PinNum )
            continue;
        /* 2 Pins doublees */
        for( jj = ii + 1; jj < NbrPin; jj++ )
        {
            if( TabPin[ii]->m_PinNum != TabPin[jj]->m_PinNum )
                break;
            TabPin[jj] = NULL;
        }
    }
}


/**********************************************************************/
static void FindOthersUnits( EDA_SchComponentStruct* Component )
/**********************************************************************/

/* Recherche les autres parts du boitier auquel appartient la part Component,
 *  pour les boitiers a parts multiples.
 *  Appelle AddPinToComponentPinList() pour classer les pins des parts trouvees
 */
{
    EDA_BaseStruct* DrawList;
    EDA_SchComponentStruct* Component2;
    EDA_LibComponentStruct* Entry;
    LibEDA_BaseStruct* DEntry;
    SCH_SCREEN* screen;

    EDA_ScreenList ScreenList( NULL );

    for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() )
    {
        DrawList = screen->EEDrawList;
        while( DrawList )
        {
            switch( DrawList->Type() )
            {
            case DRAW_LIB_ITEM_STRUCT_TYPE:
                Component2 = (EDA_SchComponentStruct*) DrawList;

                if( Component2->m_FlagControlMulti == 1 )
                    break;

                if( Component2->m_Field[REFERENCE].m_Text.CmpNoCase(
                        Component->m_Field[REFERENCE].m_Text ) != 0 )
                    break;

                Entry = FindLibPart( Component2->m_ChipName.GetData(), wxEmptyString, FIND_ROOT );
                if( Entry  == NULL )
                    break;
                if( Component2->m_Field[REFERENCE].m_Text[0] == '#' )
                    break;

                if( Entry->m_Drawings != NULL )
                {
                    DEntry = Entry->m_Drawings;
                    for( ; DEntry != NULL; DEntry = DEntry->Next() )
                    {
                        if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE )
                            continue;
                        if( DEntry->m_Unit
                           && (DEntry->m_Unit != Component2->m_Multi) )
                            continue;
                        if( DEntry->m_Convert
                           && (DEntry->m_Convert != Component2->m_Convert) )
                            continue;
                        {
                            AddPinToComponentPinList( Component2, (LibDrawPin*) DEntry );
                        }
                    }
                }
                Component2->m_FlagControlMulti = 1;
                break;

            default:
                break;
            }

            DrawList = DrawList->Pnext;
        }
    }
}


/**************************************************************************/
static int SortPinsByNum( ObjetNetListStruct** Pin1, ObjetNetListStruct** Pin2 )
/**************************************************************************/

/* Routine de comparaison pour le tri des pins par numero croissant
 *  du tableau des pins s_SortedComponentPinList par qsort()
 */
{
    ObjetNetListStruct* Obj1, * Obj2;
    int Num1, Num2;
    char Line[5];

    Obj1    = *Pin1; Obj2 = *Pin2;
    Num1    = Obj1->m_PinNum; Num2 = Obj2->m_PinNum;
    Line[4] = 0; memcpy( Line, &Num1, 4 ); Num1 = atoi( Line );
    memcpy( Line, &Num2, 4 ); Num2 = atoi( Line );
    return Num1 - Num2;
}


/*************************************************************************/
static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet )
/*************************************************************************/

/* Ecrit dans le fichier f la liste des nets ( classee par NetCodes ), et des
 *  elements qui y sont connectes
 */
{
    int ii, jj;
    int NetCode, LastNetCode = -1;
    int SameNetcodeCount = 0;
    EDA_SchComponentStruct* Cmp;
    wxString NetName, CmpRef;
    wxString NetcodeName;
    char FirstItemInNet[1024];

    for( ii = 0; ii < g_NbrObjNet; ii++ )
    {
        if( (NetCode = ObjNet[ii].GetNet()) != LastNetCode )   // New net found, write net id;
        {
            SameNetcodeCount = 0;                               // Items count for this net
            NetName.Empty();
            for( jj = 0; jj < g_NbrObjNet; jj++ )               // Find a label (if exists) for this net
            {
                if( ObjNet[jj].GetNet() != NetCode )
                    continue;
                if( ( ObjNet[jj].m_Type != NET_GLOBLABEL)
                   && ( ObjNet[jj].m_Type != NET_LABEL)
                   && ( ObjNet[jj].m_Type != NET_PINLABEL) )
                    continue;

                NetName = *g_TabObjNet[jj].m_Label; break;
            }

            NetcodeName.Printf( wxT( "Net %d " ), NetCode );
            NetcodeName += wxT( "\"" );
            if( !NetName.IsEmpty() )
            {
                NetcodeName += NetName;
                if( g_TabObjNet[jj].m_Type != NET_PINLABEL )  // usual net name, add it the sheet number
                    NetcodeName << wxT( "_" ) << g_TabObjNet[jj].m_SheetNumber;
            }
            NetcodeName += wxT( "\"" );
            LastNetCode  = NetCode;
        }

        if( ObjNet[ii].m_Type != NET_PIN )
            continue;

        Cmp    = (EDA_SchComponentStruct*) ObjNet[ii].m_Link;
        CmpRef = Cmp->m_Field[REFERENCE].m_Text;
        if( CmpRef.StartsWith( wxT( "#" ) ) )
            continue;                                   // Pseudo component (Like Power symbol)

        // Print the pin list for this net, if  2 or more items are connected:
        SameNetcodeCount++;
        if( SameNetcodeCount == 1 )     /* first item for this net found,
                                         *  Print this connection, when a second item will be found */
        {
            sprintf( FirstItemInNet, " %s %.4s\n", CONV_TO_UTF8( CmpRef ),
                     (const char*) &ObjNet[ii].m_PinNum );
        }
        if( SameNetcodeCount == 2 )    // Second item for this net found, Print the Net name, and the first item
        {
            fprintf( f, "%s\n", CONV_TO_UTF8( NetcodeName ) );
            fputs( FirstItemInNet, f );
        }

        if( SameNetcodeCount >= 2 )
            fprintf( f, " %s %.4s\n", CONV_TO_UTF8( CmpRef ),
                     (const char*) &ObjNet[ii].m_PinNum );
    }
}


/* Generation des netlistes au format CadStar */
wxString StartLine( wxT( "." ) );

/*********************************************************/
static void WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f )
/*********************************************************/

/* Routine de generation du fichier netliste ( Format CADSTAR )
 *  Entete:
 *  ..HEA
 *  ..TIM 2004 07 29 16 22 17
 *  ..APP "Cadstar RINF Output - Version 6.0.2.3"
 *  ..UNI INCH 1000.0 in
 *  ..TYP FULL
 * 
 *  liste des composants:
 *  ..ADD_COM    X1         "CNT D41612 (48PTS MC CONTOUR)"
 *  ..ADD_COM    U2         "74HCT245D" "74HCT245D"
 * 
 *  Connexions:
 *  ..ADD_TER    RR2        6          "$42"
 *  ..TER        U1         100
 *          CA         6
 * 
 *  ..ADD_TER    U2         6          "$59"
 *  ..TER        U7         39
 *          U6         17
 *          U1         122
 * 
 *  ..ADD_TER    P2         1          "$9"
 *  ..TER        T3         1
 *          U1         14
 */
{
    wxString StartCmpDesc = StartLine + wxT( "ADD_COM" );
    wxString msg;
    wxString FootprintName;
    char Line[1024];
    BASE_SCREEN* CurrScreen;
    EDA_BaseStruct* DrawList;
    EDA_SchComponentStruct* Component;
    wxString Title = g_Main_Title + wxT( " " ) + GetBuildVersion();

    fprintf( f, "%sHEA\n", CONV_TO_UTF8( StartLine ) );
    DateAndTime( Line );
    fprintf( f, "%sTIM %s\n", CONV_TO_UTF8( StartLine ), Line );
    fprintf( f, "%sAPP ", CONV_TO_UTF8( StartLine ) );
    fprintf( f, "\"%s\"\n", CONV_TO_UTF8( Title ) );
    fprintf( f, "\n" );

    /* Create netlist module section */
    ClearUsedFlags( frame );  /* Reset the flags FlagControlMulti in all schematic files*/
    EDA_ScreenList ScreenList( NULL );

    for( CurrScreen = ScreenList.GetFirst(); CurrScreen != NULL; CurrScreen = ScreenList.GetNext() )
    {
        for( DrawList = CurrScreen->EEDrawList; DrawList != NULL; DrawList = DrawList->Pnext )
        {
            DrawList = Component = FindNextComponentAndCreatPinList( DrawList );
            if( Component == NULL )
                break;

            if( !Component->m_Field[FOOTPRINT].IsVoid() )
            {
                FootprintName = Component->m_Field[FOOTPRINT].m_Text;
                FootprintName.Replace( wxT( " " ), wxT( "_" ) );
            }
            else
                FootprintName = wxT( "$noname" );

            msg = Component->m_Field[REFERENCE].m_Text;
            msg.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, "%s     ", CONV_TO_UTF8( StartCmpDesc ) );
            fprintf( f, "%s", CONV_TO_UTF8( msg ) );

            msg = Component->m_Field[VALUE].m_Text;
            msg.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, "     \"%s\"", CONV_TO_UTF8( msg ) );
            fprintf( f, "\n" );
        }
    }

    fprintf( f, "\n" );

    MyFree( s_SortedComponentPinList );
    s_SortedComponentPinList = NULL;

    WriteListOfNetsCADSTAR( f, g_TabObjNet );

    fprintf( f, "\n%sEND\n", CONV_TO_UTF8( StartLine ) );
}


/*************************************************************************/
static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet )
/*************************************************************************/

/* Ecrit dans le fichier f la liste des nets ( classee par NetCodes ), et des
 *  pins qui y sont connectes
 *  format:
 *  .ADD_TER    RR2        6          "$42"
 *  .TER        U1         100
 *          CA         6
 */
{
    wxString InitNetDesc  = StartLine + wxT( "ADD_TER" );
    wxString StartNetDesc = StartLine + wxT( "TER" );
    wxString NetcodeName, InitNetDescLine;
    int ii, jj, print_ter = 0;
    int NetCode, LastNetCode = -1;
    EDA_SchComponentStruct* Cmp;
    wxString NetName;

    for( ii = 0; ii < g_NbrObjNet; ii++ )
        ObjNet[ii].m_Flag = 0;

    for( ii = 0; ii < g_NbrObjNet; ii++ )
    {
        // Get the NetName of the current net :
        if( (NetCode = ObjNet[ii].GetNet()) != LastNetCode )
        {
            NetName.Empty();
            for( jj = 0; jj < g_NbrObjNet; jj++ )
            {
                if( ObjNet[jj].GetNet() != NetCode )
                    continue;
                if( ( ObjNet[jj].m_Type != NET_GLOBLABEL)
                   && ( ObjNet[jj].m_Type != NET_LABEL)
                   && ( ObjNet[jj].m_Type != NET_PINLABEL) )
                    continue;

                NetName = *ObjNet[jj].m_Label; break;
            }

            NetcodeName = wxT( "\"" );
            if( !NetName.IsEmpty() )
            {
                NetcodeName += NetName;
                if( g_TabObjNet[jj].m_Type != NET_PINLABEL )
                    NetcodeName << wxT( "_" ) << g_TabObjNet[jj].m_SheetNumber;
            }
            else  // this net has no name: create a default name $<net number>
                NetcodeName << wxT( "$" ) << NetCode;
            NetcodeName += wxT( "\"" );
            LastNetCode  = NetCode;
            print_ter    = 0;
        }


        if( ObjNet[ii].m_Type != NET_PIN )
            continue;

        if( ObjNet[ii].m_Flag != 0 )
            continue;

        Cmp = (EDA_SchComponentStruct*) ObjNet[ii].m_Link;

        if( Cmp->m_Field[REFERENCE].m_Text[0] == '#' )
            continue; // Pseudo composant (symboles d'alims)

        switch( print_ter )
        {
        case 0:
        {
            char buf[5];
            wxString str_pinnum;
            strncpy( buf, (char*) &ObjNet[ii].m_PinNum, 4 ); buf[4] = 0;
            str_pinnum = CONV_FROM_UTF8( buf );
            InitNetDescLine.Printf( wxT( "\n%s   %s   %.4s     %s" ),
                                   InitNetDesc.GetData(),
                                   Cmp->m_Field[REFERENCE].m_Text.GetData(),
                                   str_pinnum.GetData(), NetcodeName.GetData() );
        }
            print_ter++;
            break;

        case 1:
            fprintf( f, "%s\n", CONV_TO_UTF8( InitNetDescLine ) );
            fprintf( f, "%s       %s   %.4s\n",
                     CONV_TO_UTF8( StartNetDesc ),
                     CONV_TO_UTF8( Cmp->m_Field[REFERENCE].m_Text ),
                     (char*) &ObjNet[ii].m_PinNum );
            print_ter++;
            break;

        default:
            fprintf( f, "            %s   %.4s\n",
                     CONV_TO_UTF8( Cmp->m_Field[REFERENCE].m_Text ),
                     (char*) &ObjNet[ii].m_PinNum );
            break;
        }

        ObjNet[ii].m_Flag = 1;

        // Recherche des pins redondantes et mise a 1 de m_Flag,
        //	pour ne pas generer plusieurs fois la connexion
        for( jj = ii + 1; jj < g_NbrObjNet; jj++ )
        {
            if( ObjNet[jj].GetNet() != NetCode )
                break;
            if( ObjNet[jj].m_Type != NET_PIN )
                continue;
            EDA_SchComponentStruct* tstcmp =
                (EDA_SchComponentStruct*) ObjNet[jj].m_Link;
            if( Cmp->m_Field[REFERENCE].m_Text != tstcmp->m_Field[REFERENCE].m_Text )
                continue;

            if( ObjNet[jj].m_PinNum == ObjNet[ii].m_PinNum )
                ObjNet[jj].m_Flag = 1;
        }
    }
}
