/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 1992-2009 jean-pierre.charras@gipsa-lab.inpg.fr
 * Copyright (C) 2010 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 1992-2010 KiCad Developers, see change_log.txt for contributors.
 *
 * 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
 */

/**
 * @file netform.cpp
 * @brief Net list generation code.
 */

#include "fctsys.h"
#include "confirm.h"
#include "kicad_string.h"
#include "gestfich.h"
#include "appl_wxstruct.h"
#include "wxEeschemaStruct.h"

#include "general.h"
#include "netlist.h"
#include "netlist_control.h"
#include "protos.h"
#include "class_library.h"
#include "lib_pin.h"
#include "sch_component.h"
#include "sch_text.h"
#include "sch_sheet.h"

#include <wx/tokenzr.h>



#include "xnode.h"      // also nests: <wx/xml/xml.h>

#include "build_version.h"

#define INTERMEDIATE_NETLIST_EXT wxT("xml")

#include <set>

/**
 * @bug - Every place in this file where fprintf() is used and the return
 *        is not checked is a bug.  The fprintf() function can fail and
 *        returns a value less than 0 when it does.
 */


/**
 * Class UNIQUE_STRINGS
 * tracks unique wxStrings and is useful in telling if a string
 * has been seen before.
 */
class UNIQUE_STRINGS
{
    std::set<wxString>      m_set;    ///< set of wxStrings already found

    typedef std::set<wxString>::iterator us_iterator;

public:
    /**
     * Function Clear
     * erases the record.
     */
    void Clear()  {  m_set.clear();  }

    /**
     * Function Lookup
     * returns true if \a aString already exists in the set, otherwise returns
     * false and adds \a aString to the set for next time.
     */
    bool Lookup( const wxString& aString );
};

bool UNIQUE_STRINGS::Lookup( const wxString& aString )
{
    std::pair<us_iterator, bool> pair = m_set.insert( aString );

    return !pair.second;
}


/**
 * Class EXPORT_HELP
 * is a private implementation class used in this source file to keep track
 * of and recycle datastructures used in the generation of various exported netlist
 * files.  Since it is private it is not in a header file.
 */
class EXPORT_HELP
{
    /// Used to temporary store and filter the list of pins of a schematic component
    /// when generating schematic component data in netlist (comp section)
    NETLIST_OBJECT_LIST m_SortedComponentPinList;

    /// Used for "multi parts per package" components, avoids processing a lib component more than once.
    UNIQUE_STRINGS      m_ReferencesAlreadyFound;

    // share a code generated std::set<void*> to reduce code volume

    std::set<void*>     m_LibParts;     ///< unique library parts used

    std::set<void*>     m_Libraries;    ///< unique libraries used


    /**
     * Function sprintPinNetName
     * formats the net name for \a aPin using \a aNetNameFormat into \a aResult.
     * <p>
     *  Net name is:
     *  <ul>
     * <li> "?" if pin not connected
     * <li> "netname" for global net (like gnd, vcc ..
     * <li> "/path/netname" for the usual nets
     * </ul>
     */
    static void sprintPinNetName( wxString* aResult, const wxString& aNetNameFormat, NETLIST_OBJECT* aPin );

    /**
     * Function findNextComponentAndCreatePinList
     * finds a component from the DrawList and builds
     * its pin list in m_SortedComponentPinList. This list is sorted by pin num.
     * the component is the next actual component after aItem
     * (power symbols and virtual components that have their reference starting by '#'are skipped).
     */
    SCH_COMPONENT* findNextComponentAndCreatePinList( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );

    SCH_COMPONENT* findNextComponent( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath );

    /**
     * Function eraseDuplicatePins
     * erase duplicate Pins from m_SortedComponentPinList (i.e. set pointer in this list to NULL).
     * (This is a list of pins found in the whole schematic, for a single
     * component.) These duplicate pins were put in list because some pins (powers... )
     * are found more than one time when we have a multiple parts per package
     * component. For instance, a 74ls00 has 4 parts, and therefore the VCC pin
     * and GND pin appears 4 times in the list.
     * Note: this list *MUST* be sorted by pin number (.m_PinNum member value)
     * Also set the m_Flag member of "removed" NETLIST_OBJECT pin item to 1
     */
    void eraseDuplicatePins( );

    /**
     * Function addPinToComponentPinList
     * adds a new pin description to the pin list m_SortedComponentPinList.
     * A pin description is a pointer to the corresponding structure
     * created by BuildNetList() in the table g_NetObjectslist.
     */
    bool addPinToComponentPinList( SCH_COMPONENT*  Component,
                                   SCH_SHEET_PATH* sheet,
                                   LIB_PIN*        PinEntry );

    /**
     * Function findAllInstancesOfComponent
     * is used for "multiple parts per package" components.
     * <p>
     * Search the entire design for all instances of \a aComponent based on
     * matching reference designator, and for each part, add all its pins
     * to the temporary sorted pin list.
     */
    void findAllInstancesOfComponent( SCH_COMPONENT*  aComponent,
                                      LIB_COMPONENT*  aEntry,
                                      SCH_SHEET_PATH* aSheetPath );

    /**
     * Function writeGENERICListOfNets
     * writes out nets (ranked by Netcode), and elements that are
     * connected as part of that net.
     */
    bool writeGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjectsList );

    /**
     * Function writeListOfNetsCADSTAR
     * writes a net list (ranked by Netcode), and pins connected to it.
     * <p>
     * Format:
     *   - ADD_TER RR2 6 \"$42\"
     *   - B U1 100
     *   - 6 CA
     * </p>
     */
    void writeListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjectsList );

    /**
     * Function makeGenericRoot
     * builds the entire document tree for the generic export.  This is factored
     * out here so we can write the tree in either S-expression file format
     * or in XML if we put the tree built here into a wxXmlDocument.
     */
    XNODE* makeGenericRoot();

    /**
     * Function makeGenericComponents
     * returns a sub-tree holding all the schematic components.
     */
    XNODE* makeGenericComponents();

    /**
     * Function makeGenericDesignHeader
     * fills out a project "design" header into an XML node.
     * @return XNODE* - the design header
     */
    XNODE* makeGenericDesignHeader();

    /**
     * Function makeGenericLibParts
     * fills out an XML node with the unique library parts and returns it.
     */
    XNODE* makeGenericLibParts();

    /**
     * Function makeGenericListOfNets
     * fills out an XML node with a list of nets and returns it.
     */
    XNODE* makeGenericListOfNets();

    /**
     * Function makeGenericLibraries
     * fills out an XML node with a list of used libraries and returns it.
     * Must have called makeGenericLibParts() before this function.
     */
    XNODE* makeGenericLibraries();

public:

    /**
     * Function WriteGENERICNetList
     * creates a generic netlist, now in XML.
     * @param aOutFileName = the full filename of the file to create
     * @return bool - true if there were no errors, else false.
     */
    bool WriteGENERICNetList( const wxString& aOutFileName );

    /**
     * Function WriteNetListPCBNEW
     * generates a net list file (Format 2 improves ORCAD PCB)
     *
     * @param f = the file to write to
     * @param with_pcbnew if true, then use Pcbnew format (OrcadPcb2 + a list of net),<p>
     *                    else use ORCADPCB2 basic format.
     */
    bool WriteNetListPCBNEW( FILE* f, bool with_pcbnew );

    /**
     * Function WriteNetListCADSTAR
     * generates a netlist file in CADSTAR Format.
     * Header:
     * HEA ..
     * TIM .. 2004 07 29 16 22 17
     * APA .. "Cadstar RINF Output - Version 6.0.2.3"
     * INCH UNI .. 1000.0 in
     * FULL TYP ..
     *
     * List of components:
     * .. ADD_COM X1 "CNT D41612 (48pts CONTOUR TM)"
     * .. ADD_COM U2 "74HCT245D" "74HCT245D"
     *
     * Connections:
     *  .. ADD_TER RR2 * 6 "$ 42"
     * .. B U1 100
     * 6 CA
     *
     * ADD_TER .. U2 * 6 "$ 59"
     * .. B * U7 39
     * U6 17
     * U1 * 122
     *
     * .. ADD_TER P2 * 1 "$ 9"
     * .. B * T3 1
     *U1 * 14
     */
    void WriteNetListCADSTAR( FILE* f );

    /**
     * Function WriteNetListPspice
     * generates a netlist file in PSPICE format.
     * <p>
     * All graphics text starting by  [.-+] PSpice or [.-+] gnucap
     * are seen as spice directives and put in netlist
     * .-PSpice or .-gnucap put at beginning of the netlist
     * .+PSpice or .-genucap are put at end of the netList
     * @param f = the file to write to
     * @param use_netnames = true, to use netnames in netlist,
     *                      false to use net number.
     * @param aUsePrefix = true, adds an 'X' prefix to any reference designator starting with "U" or "IC",
     *                     false to leave reference designator unchanged.
     */
    bool WriteNetListPspice( FILE* f, bool use_netnames, bool aUsePrefix );

    /**
     * Function MakeCommandLine
     * builds up a string that describes a command line for
     * executing a child process. The input and output file names
     * along with any options to the executable are all possibly
     * in the returned string.
     *
     * @param aFormatString holds:
     *   <ul>
     *   <li>the name of the external program
     *   <li>any options needed by that program
     *   <li>formatting sequences, see below.
     *   </ul>
     *
     * @param aTempfile is the name of an input file to the
     *  external program.
     * @param aFinalFile is the name of an output file that
     *  the user expects.
     *
     *  <p> Supported formatting sequences and their meaning:
     *  <ul>
     *  <li> %B => base filename of selected output file, minus
     *       path and extension.
     *  <li> %I => complete filename and path of the temporary
     *       input file.
     *  <li> %O => complete filename and path of the user chosen
     *       output file.
     *  </ul>
     */
    static wxString MakeCommandLine( const wxString& aFormatString,
            const wxString& aTempfile, const wxString& aFinalFile );
};


wxString EXPORT_HELP::MakeCommandLine( const wxString& aFormatString,
            const wxString& aTempfile, const wxString& aFinalFile )
{
    wxString    ret  = aFormatString;
    wxFileName  in   = aTempfile;
    wxFileName  out  = aFinalFile;

    ret.Replace( wxT("%B"), out.GetName().GetData(), true );
    ret.Replace( wxT("%I"), in.GetFullPath().GetData(), true );
    ret.Replace( wxT("%O"), out.GetFullPath().GetData(), true );

    return ret;
}


/**
 * Function  WriteNetListFile
 * creates the netlist file. Netlist info must be existing
 * @param aFormat = netlist format (NET_TYPE_PCBNEW ...)
 * @param aFullFileName = full netlist file name
 * @param aUse_netnames = bool. if true, use net names from labels in schematic
 *                              if false, use net numbers (net codes)
 *   bool aUse_netnames is used only for Spice netlist
 * @param aUsePrefix = true, adds an 'X' prefix to any reference designator starting with "U" or "IC",
 *                     false to leave reference designator unchanged.
 * @return true if success.
 */
bool SCH_EDIT_FRAME::WriteNetListFile( int aFormat, const wxString& aFullFileName,
                                       bool aUse_netnames, bool aUsePrefix )
{
    bool        ret = true;
    FILE*       f = NULL;
    EXPORT_HELP helper;

    if( aFormat < NET_TYPE_CUSTOM1 )
    {
        if( ( f = wxFopen( aFullFileName, wxT( "wt" ) ) ) == NULL )
        {
            wxString msg = _( "Failed to create file " ) + aFullFileName;
            DisplayError( this, msg );
            return false;
        }
    }

    wxBusyCursor Busy;

    switch( aFormat )
    {
    case NET_TYPE_PCBNEW:
        ret = helper.WriteNetListPCBNEW( f, true );
        fclose( f );
        break;

    case NET_TYPE_ORCADPCB2:
        ret = helper.WriteNetListPCBNEW( f, false );
        fclose( f );
        break;

    case NET_TYPE_CADSTAR:
        helper.WriteNetListCADSTAR( f );
        fclose( f );
        break;

    case NET_TYPE_SPICE:
        ret = helper.WriteNetListPspice( f, aUse_netnames, aUsePrefix );
        fclose( f );
        break;

    default:
        {
            wxFileName  tmpFile = aFullFileName;
            tmpFile.SetExt( INTERMEDIATE_NETLIST_EXT );

            D(printf("tmpFile:'%s'\n", TO_UTF8( tmpFile.GetFullPath() ) );)

            ret = helper.WriteGENERICNetList( tmpFile.GetFullPath() );
            if( !ret )
                break;

            // If user provided no plugin command line, return now.
            if( m_netListerCommand.IsEmpty() )
                break;

            // build full command line from user's format string, e.g.:
            // "xsltproc -o %O /usr/local/lib/kicad/plugins/netlist_form_pads-pcb.xsl %I"
            // becomes, after the user selects /tmp/s1.net as the output file from the file dialog:
            // "xsltproc -o /tmp/s1.net /usr/local/lib/kicad/plugins/netlist_form_pads-pcb.xsl /tmp/s1.tmp"
            wxString commandLine = EXPORT_HELP::MakeCommandLine( m_netListerCommand,
                                                                 tmpFile.GetFullPath(),
                                                                 aFullFileName );

            D(printf("commandLine:'%s'\n", TO_UTF8( commandLine ) );)

            ProcessExecute( commandLine, wxEXEC_SYNC );

            // ::wxRemoveFile( tmpFile.GetFullPath() );
        }
        break;
    }

    return ret;
}


/// Comparison routine for sorting by pin numbers.
static bool sortPinsByNum( NETLIST_OBJECT* aPin1, NETLIST_OBJECT* aPin2 )
{
    // return "lhs < rhs"
    return RefDesStringCompare( aPin1->GetPinNumText(), aPin2->GetPinNumText() ) < 0;
}

static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 )
{
    // return "lhs < rhs"
    return RefDesStringCompare( aPin1->GetNumberString(), aPin2->GetNumberString() ) < 0;
}


void EXPORT_HELP::sprintPinNetName( wxString* aResult,
                                    const wxString& aNetNameFormat, NETLIST_OBJECT* aPin )
{
    int netcode = aPin->GetNet();

    // Not wxString::Clear(), which would free memory.  We want the worst
    // case wxString memory to grow to avoid reallocation from within the
    // caller's loop.
    aResult->Empty();

    if( netcode != 0 && aPin->m_FlagOfConnection == PAD_CONNECT )
    {
        NETLIST_OBJECT* netref = aPin->m_NetNameCandidate;
        if( netref )
            *aResult = netref->m_Label;

        if( !aResult->IsEmpty() )
        {
            // prefix non global label names with the sheet path, to avoid name collisions
            if( netref->m_Type != NET_PINLABEL && netref->m_Type != NET_GLOBLABEL )
            {
                wxString lnet = *aResult;

                *aResult = netref->m_SheetList.PathHumanReadable();

                // If sheet path is too long, use the time stamp name instead
                if( aResult->Length() > 32 )
                    *aResult = netref->m_SheetList.Path();

                *aResult += lnet;
            }
        }
        else
        {
            aResult->Printf( aNetNameFormat.GetData(), netcode );
        }
    }
}


SCH_COMPONENT* EXPORT_HELP::findNextComponent( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheetPath )
{
    wxString    ref;

    // continue searching from the middle of a linked list (the draw list)
    for(  ; aItem;  aItem = aItem->Next() )
    {
        if( aItem->Type() != SCH_COMPONENT_T )
            continue;

        // found next component
        SCH_COMPONENT* comp = (SCH_COMPONENT*) aItem;

        // Power symbols and other components which have the reference starting
        // with "#" are not included in netlist (pseudo or virtual components)
        ref = comp->GetRef( aSheetPath );
        if( ref[0] == wxChar( '#' ) )
            continue;

        // if( Component->m_FlagControlMulti == 1 )
        //    continue;                                      /* yes */
        // removed because with multiple instances of one schematic
        // (several sheets pointing to 1 screen), this will be erroneously be
        // toggled.

        LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->GetLibName() );
        if( !entry )
            continue;

        // If component is a "multi parts per package" type
        if( entry->GetPartCount() > 1 )
        {
            // test if this reference has already been processed, and if so skip
            if( m_ReferencesAlreadyFound.Lookup( ref ) )
                continue;
        }

        // record the usage of this library component entry.
        m_LibParts.insert( entry );     // rejects non-unique pointers

        return comp;
    }

    return NULL;
}


SCH_COMPONENT* EXPORT_HELP::findNextComponentAndCreatePinList( EDA_ITEM*       aItem,
                                                              SCH_SHEET_PATH* aSheetPath )
{
    wxString    ref;

    m_SortedComponentPinList.clear();

    // continue searching from the middle of a linked list (the draw list)
    for(  ; aItem;  aItem = aItem->Next() )
    {
        if( aItem->Type() != SCH_COMPONENT_T )
            continue;

        // found next component
        SCH_COMPONENT* comp = (SCH_COMPONENT*) aItem;

        // Power symbols and other components which have the reference starting
        // with "#" are not included in netlist (pseudo or virtual components)
        ref = comp->GetRef( aSheetPath );

        if( ref[0] == wxChar( '#' ) )
            continue;

        // if( Component->m_FlagControlMulti == 1 )
        //    continue;                                      /* yes */
        // removed because with multiple instances of one schematic
        // (several sheets pointing to 1 screen), this will be erroneously be
        // toggled.

        LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->GetLibName() );

        if( !entry )
            continue;

        // If component is a "multi parts per package" type
        if( entry->GetPartCount() > 1 )
        {
            // test if this reference has already been processed, and if so skip
            if( m_ReferencesAlreadyFound.Lookup( ref ) )
                continue;

            // Collect all pins for this reference designator by searching
            // the entire design for other parts with the same reference designator.
            // This is only done once, it would be too expensive otherwise.
            findAllInstancesOfComponent( comp, entry, aSheetPath );
        }

        else    // entry->GetPartCount() <= 1 means one part per package
        {
            LIB_PINS pins;      // constructed once here

            entry->GetPins( pins, comp->GetUnitSelection( aSheetPath ), comp->GetConvert() );

            for( size_t i = 0; i < pins.size(); i++ )
            {
                LIB_PIN* pin = pins[i];

                wxASSERT( pin->Type() == LIB_PIN_T );

                addPinToComponentPinList( comp, aSheetPath, pin );
            }
        }

        // Sort pins in m_SortedComponentPinList by pin number
        sort( m_SortedComponentPinList.begin(),
              m_SortedComponentPinList.end(), sortPinsByNum );

        // Remove duplicate Pins in m_SortedComponentPinList
        eraseDuplicatePins( );

        // record the usage of this library component entry.
        m_LibParts.insert( entry );     // rejects non-unique pointers

        return comp;
    }

    return NULL;
}


/**
 * Function node
 * is a convenience function that creates a new XNODE with an optional textual child.
 * It also provides some insulation from a possible change in XML library.
 *
 * @param aName is the name to associate with a new node of type wxXML_ELEMENT_NODE.
 * @param aTextualContent is optional, and if given is the text to include in a child
 *   of the returned node, and has type wxXML_TEXT_NODE.
 */
static XNODE* node( const wxString& aName, const wxString& aTextualContent = wxEmptyString )
{
    XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );

    if( aTextualContent.Len() > 0 )     // excludes wxEmptyString, the parameter's default value
        n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );

    return n;
}


XNODE* EXPORT_HELP::makeGenericDesignHeader()
{
    XNODE*  xdesign = node( wxT("design") );
    char    date[128];

    DateAndTime( date );

    // the root sheet is a special sheet, call it source
    xdesign->AddChild( node( wxT( "source" ), g_RootSheet->GetScreen()->GetFileName() ) );

    xdesign->AddChild( node( wxT( "date" ), FROM_UTF8( date )) );

    // which Eeschema tool
    xdesign->AddChild( node( wxT( "tool" ), wxGetApp().GetAppName() + wxChar(' ') +
                             GetBuildVersion() ) );

    /*  @todo might do a list of schematic pages

        <page name="">
            <title/>
            <revision/>
            <company/>
            <comments>
                <comment>blah</comment> <!-- comment1 -->
                <comment>blah</comment> <!-- comment2 -->
            </comments>
            <pagesize/>
        </page>
        :

        and a sheet hierarchy report here
        <sheets>
            <sheet name="sheetname1" page="pagenameA">
                <sheet name="sheetname2" page="pagenameB"/>  use recursion to output?
            </sheet>
        </sheets>
    */

    return xdesign;
}


XNODE* EXPORT_HELP::makeGenericLibraries()
{
    XNODE*  xlibs = node( wxT( "libraries" ) );     // auto_ptr

    for( std::set<void*>::iterator it = m_Libraries.begin(); it!=m_Libraries.end();  ++it )
    {
        CMP_LIBRARY*    lib = (CMP_LIBRARY*) *it;
        XNODE*      xlibrary;

        xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
        xlibrary->AddAttribute( wxT( "logical" ), lib->GetLogicalName() );
        xlibrary->AddChild( node( wxT( "uri" ),  lib->GetFullFileName() ) );

        // @todo: add more fun stuff here
    }

    return xlibs;
}


XNODE* EXPORT_HELP::makeGenericLibParts()
{
    XNODE*      xlibparts = node( wxT( "libparts" ) );   // auto_ptr
    wxString    sLibpart  = wxT( "libpart" );
    wxString    sLib      = wxT( "lib" );
    wxString    sPart     = wxT( "part" );
    wxString    sPins     = wxT( "pins" );      // key for library component pins list
    wxString    sPin      = wxT( "pin" );       // key for one library component pin descr
    wxString    sPinNum   = wxT( "num" );       // key for one library component pin num
    wxString    sPinName  = wxT( "name" );      // key for one library component pin name
    wxString    sPinType  = wxT( "type" );      // key for one library component pin electrical type
    wxString    sName     = wxT( "name" );
    wxString    sField    = wxT( "field" );
    wxString    sFields   = wxT( "fields" );
    wxString    sDescr    = wxT( "description" );
    wxString    sDocs     = wxT( "docs" );
    wxString    sFprints  = wxT( "footprints" );
    wxString    sFp       = wxT( "fp" );

    LIB_PINS    pinList;
    LIB_FIELDS  fieldList;

    m_Libraries.clear();

    for( std::set<void*>::iterator it = m_LibParts.begin(); it!=m_LibParts.end();  ++it )
    {
        LIB_COMPONENT*  lcomp = (LIB_COMPONENT*) *it;
        CMP_LIBRARY*    library = lcomp->GetLibrary();

        m_Libraries.insert( library );  // inserts component's library if unique

        XNODE*      xlibpart;
        xlibparts->AddChild( xlibpart = node( sLibpart ) );
        xlibpart->AddAttribute( sLib, library->GetLogicalName() );
        xlibpart->AddAttribute( sPart, lcomp->GetName()  );

        //----- show the important properties -------------------------
        if( !lcomp->GetAlias( 0 )->GetDescription().IsEmpty() )
            xlibpart->AddChild( node( sDescr, lcomp->GetAlias( 0 )->GetDescription() ) );

        if( !lcomp->GetAlias( 0 )->GetDocFileName().IsEmpty() )
            xlibpart->AddChild( node( sDocs,  lcomp->GetAlias( 0 )->GetDocFileName() ) );

        // Write the footprint list
        if( lcomp->GetFootPrints().GetCount() )
        {
            XNODE*  xfootprints;
            xlibpart->AddChild( xfootprints = node( sFprints ) );

            for( unsigned i=0; i<lcomp->GetFootPrints().GetCount(); ++i )
            {
                xfootprints->AddChild( node( sFp, lcomp->GetFootPrints()[i] ) );
            }
        }

        //----- show the fields here ----------------------------------
        fieldList.clear();
        lcomp->GetFields( fieldList );

        XNODE*     xfields;
        xlibpart->AddChild( xfields = node( sFields ) );

        for( unsigned i=0;  i<fieldList.size();  ++i )
        {
            if( !fieldList[i].m_Text.IsEmpty() )
            {
                XNODE*     xfield;
                xfields->AddChild( xfield = node( sField, fieldList[i].m_Text ) );
                xfield->AddAttribute( sName, fieldList[i].GetName(false) );
            }
        }

        //----- show the pins here ------------------------------------
        pinList.clear();
        lcomp->GetPins( pinList, 0, 0 );

        /* we must erase redundant Pins references in pinList
         * These redundant pins exist because some pins
         * are found more than one time when we have a component
         * multiple parts per package or have 2 representations (DeMorgan conversion)
         * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes,
         * and therefore each pin  appears 2 times in the list.
         * Common pins (VCC, GND) can also be found more than once.
         */
        sort( pinList.begin(), pinList.end(), sortPinsByNumber );
        for( unsigned ii = 0; ii < pinList.size()-1; ii++ )
        {
            if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
            {   // 2 pins have the same number, remove the redundant pin at index i+1
                pinList.erase(pinList.begin() + ii + 1);
                ii--;
            }
        }

        if( pinList.size() )
        {
            XNODE*     pins;

            xlibpart->AddChild( pins = node( sPins ) );
            for( unsigned i=0; i<pinList.size();  ++i )
            {
                XNODE*     pin;

                pins->AddChild( pin = node( sPin ) );
                pin->AddAttribute( sPinNum, pinList[i]->GetNumberString() );
                pin->AddAttribute( sPinName, pinList[i]->GetName() );
                pin->AddAttribute( sPinType, pinList[i]->GetTypeString() );

                // caution: construction work site here, drive slowly
            }
        }
    }

    return xlibparts;
}


XNODE* EXPORT_HELP::makeGenericListOfNets()
{
    XNODE*      xnets = node( wxT( "nets" ) );      // auto_ptr if exceptions ever get used.
    wxString    netCodeTxt;
    wxString    netName;
    wxString    ref;

    wxString    sNet  = wxT( "net" );
    wxString    sName = wxT( "name" );
    wxString    sCode = wxT( "code" );
    wxString    sRef  = wxT( "ref" );
    wxString    sPin  = wxT( "pin" );
    wxString    sNode = wxT( "node" );
    wxString    sFmtd = wxT( "%d" );

    XNODE*      xnet = 0;
    int         netCode;
    int         lastNetCode = -1;
    int         sameNetcodeCount = 0;


    /*  output:
        <net code="123" name="/cfcard.sch/WAIT#">
            <node ref="R23" pin="1"/>
            <node ref="U18" pin="12"/>
        </net>
    */

    m_LibParts.clear();     // must call this function before using m_LibParts.

    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
    {
        NETLIST_OBJECT* nitem = g_NetObjectslist[ii];
        SCH_COMPONENT*  comp;

        // New net found, write net id;
        if( ( netCode = nitem->GetNet() ) != lastNetCode )
        {
            sameNetcodeCount = 0;   // item count for this net

            netName.Empty();

            // Find a label for this net, if it exists.
            NETLIST_OBJECT* netref = nitem->m_NetNameCandidate;
            if( netref )
            {
                if( netref->m_Type != NET_PINLABEL && netref->m_Type != NET_GLOBLABEL )
                {
                    // usual net name, prefix it by the sheet path
                    netName = netref->m_SheetList.PathHumanReadable();
                }

                netName += netref->m_Label;
            }

            lastNetCode  = netCode;
        }

        if( nitem->m_Type != NET_PIN )
            continue;

        if( nitem->m_Flag != 0 )     // Redundant pin, skip it
            continue;

        comp = (SCH_COMPONENT*) nitem->m_Link;

        // Get the reference for the net name and the main parent component
        ref = comp->GetRef( &nitem->m_SheetList );
        if( ref[0] == wxChar( '#' ) )
            continue;

        if( ++sameNetcodeCount == 1 )
        {
            xnets->AddChild( xnet = node( sNet ) );
            netCodeTxt.Printf( sFmtd, netCode );
            xnet->AddAttribute( sCode, netCodeTxt );
            xnet->AddAttribute( sName, netName );
        }

        XNODE*      xnode;
        xnet->AddChild( xnode = node( sNode ) );
        xnode->AddAttribute( sRef, ref );
        xnode->AddAttribute( sPin,  nitem->GetPinNumText() );
    }

    return xnets;
}


XNODE* EXPORT_HELP::makeGenericRoot()
{
    XNODE*      xroot = node( wxT( "export" ) );

    xroot->AddAttribute( wxT( "version" ), wxT( "D" ) );

    // add the "design" header
    xroot->AddChild( makeGenericDesignHeader() );

    xroot->AddChild( makeGenericComponents() );

    xroot->AddChild( makeGenericLibParts() );

    // must follow makeGenericLibParts()
    xroot->AddChild( makeGenericLibraries() );

    xroot->AddChild( makeGenericListOfNets() );

    return xroot;
}


XNODE* EXPORT_HELP::makeGenericComponents()
{
    XNODE*      xcomps = node( wxT( "components" ) );

    wxString    timeStamp;

    // some strings we need many times, but don't want to construct more
    // than once for performance.  These are used within loops so the
    // enclosing wxString constructor would fire on each loop iteration if
    // they were in a nested scope.

    // these are actually constructor invocations, not assignments as it appears:
    wxString    sFields     = wxT( "fields" );
    wxString    sField      = wxT( "field" );
    wxString    sComponent  = wxT( "comp" );          // use "part" ?
    wxString    sName       = wxT( "name" );
    wxString    sRef        = wxT( "ref" );
    wxString    sPins       = wxT( "pins" );
    wxString    sPin        = wxT( "pin" );
    wxString    sValue      = wxT( "value" );
    wxString    sSheetPath  = wxT( "sheetpath" );
    wxString    sFootprint  = wxT( "footprint" );
    wxString    sDatasheet  = wxT( "datasheet" );
    wxString    sTStamp     = wxT( "tstamp" );
    wxString    sTStamps    = wxT( "tstamps" );
    wxString    sTSFmt      = wxT( "%8.8lX" );        // comp->m_TimeStamp
    wxString    sLibSource  = wxT( "libsource" );
    wxString    sLibPart    = wxT( "libpart" );
    wxString    sLib        = wxT( "lib" );
    wxString    sPart       = wxT( "part" );
    wxString    sNames      = wxT( "names" );

    m_ReferencesAlreadyFound.Clear();

    SCH_SHEET_LIST sheetList;

    // Output is xml, so there is no reason to remove spaces from the field values.
    // And XML element names need not be translated to various languages.

    for( SCH_SHEET_PATH* path = sheetList.GetFirst();  path;  path = sheetList.GetNext() )
    {
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        {
            SCH_COMPONENT*  comp = findNextComponentAndCreatePinList( schItem, path );
            if( !comp )
                break;  // No component left

            schItem = comp;

            XNODE* xcomp;  // current component being constructed

            // Output the component's elements in order of expected access frequency.
            // This may not always look best, but it will allow faster execution
            // under XSL processing systems which do sequential searching within
            // an element.

            xcomps->AddChild( xcomp = node( sComponent ) );
            xcomp->AddAttribute( sRef, comp->GetRef( path ) );

            xcomp->AddChild( node( sValue, comp->GetField( VALUE )->m_Text ) );

            if( !comp->GetField( FOOTPRINT )->m_Text.IsEmpty() )
                xcomp->AddChild( node( sFootprint, comp->GetField( FOOTPRINT )->m_Text ) );

            if( !comp->GetField( DATASHEET )->m_Text.IsEmpty() )
                xcomp->AddChild( node( sDatasheet, comp->GetField( DATASHEET )->m_Text ) );

            // Export all user defined fields within the component,
            // which start at field index MANDATORY_FIELDS.  Only output the <fields>
            // container element if there are any <field>s.
            if( comp->GetFieldCount() > MANDATORY_FIELDS )
            {
                XNODE* xfields;
                xcomp->AddChild( xfields = node( sFields ) );

                for( int fldNdx = MANDATORY_FIELDS; fldNdx < comp->GetFieldCount(); ++fldNdx )
                {
                    SCH_FIELD*  f = comp->GetField( fldNdx );

                    // only output a field if non empty
                    if( !f->m_Text.IsEmpty() )
                    {
                        XNODE*  xfield;
                        xfields->AddChild( xfield = node( sField, f->m_Text ) );
                        xfield->AddAttribute( sName, f->m_Name );
                    }
                }
            }

            XNODE*  xlibsource;
            xcomp->AddChild( xlibsource = node( sLibSource ) );

            // "logical" library name, which is in anticipation of a better search
            // algorithm for parts based on "logical_lib.part" and where logical_lib
            // is merely the library name minus path and extension.
            LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->GetLibName() );
            if( entry )
                xlibsource->AddAttribute( sLib, entry->GetLibrary()->GetLogicalName() );
            xlibsource->AddAttribute( sPart, comp->GetLibName() );

            XNODE* xsheetpath;
            xcomp->AddChild( xsheetpath = node( sSheetPath ) );
            xsheetpath->AddAttribute( sNames, path->PathHumanReadable() );
            xsheetpath->AddAttribute( sTStamps, path->Path() );

            timeStamp.Printf( sTSFmt, comp->m_TimeStamp );
            xcomp->AddChild( node( sTStamp, timeStamp ) );
        }
    }

    return xcomps;
}

#include <wx/wfstream.h>        // wxFFileOutputStream

bool EXPORT_HELP::WriteGENERICNetList( const wxString& aOutFileName )
{
    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

#if 0

    // this code seems to work now, for S-expression support.

    bool rc = false;
    wxFFileOutputStream os( aOutFileName, wxT( "wt" ) );

    if( !os.IsOk() )
    {
    L_error:
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        DisplayError( NULL, msg );
    }
    else
    {
        XNODE*  xroot = makeGenericRoot();

        try
        {
            STREAM_OUTPUTFORMATTER  outputFormatter( os );
            xroot->Format( &outputFormatter, 0 );
        }
        catch( IO_ERROR ioe )
        {
            delete xroot;
            goto L_error;
        }

        delete xroot;
        rc = true;
    }

    return rc;

#elif 1
    // output the XML format netlist.
    wxXmlDocument   xdoc;

    xdoc.SetRoot( makeGenericRoot() );

    return xdoc.Save( aOutFileName, 2 /* indent bug, today was ignored by wxXml lib */ );

#else   // output the well established/old generic net list format which was not XML.

    wxString        field;
    wxString        footprint;
    wxString        netname;
    FILE*           out;
    int             ret = 0;    // OR on each call, test sign bit at very end.

    if( ( out = wxFopen( aOutFileName, wxT( "wt" ) ) ) == NULL )
    {
        wxString msg = _( "Failed to create file " ) + aOutFileName;
        DisplayError( NULL, msg );
        return false;
    }

    m_ReferencesAlreadyFound.Clear();

    ret |= fprintf( out, "$BeginNetlist\n" );

    // Create netlist module section
    ret |= fprintf( out, "$BeginComponentList\n" );

    SCH_SHEET_LIST sheetList;

    for( SCH_SHEET_PATH* path = sheetList.GetFirst();  path;  path = sheetList.GetNext() )
    {
        for( EDA_ITEM* schItem = path->LastDrawList();  schItem;  schItem = schItem->Next() )
        {
            SCH_COMPONENT*  comp = findNextComponentAndCreatePinList( schItem, path );
            if( !comp )
                break;  // No component left

            schItem = comp;

            footprint.Empty();
            if( !comp->GetField( FOOTPRINT )->IsVoid() )
            {
                footprint = comp->GetField( FOOTPRINT )->m_Text;
                footprint.Replace( wxT( " " ), wxT( "_" ) );
            }

            ret |= fprintf( out, "\n$BeginComponent\n" );
            ret |= fprintf( out, "TimeStamp=%8.8lX\n", comp->m_TimeStamp );
            ret |= fprintf( out, "Footprint=%s\n", TO_UTF8( footprint ) );

            field = wxT( "Reference=" ) + comp->GetRef( path ) + wxT( "\n" );
            field.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fputs( TO_UTF8( field ), out );

            field = comp->GetField( VALUE )->m_Text;
            field.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fprintf( out, "Value=%s\n", TO_UTF8( field ) );

            field = comp->GetLibName();
            field.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fprintf( out, "Libref=%s\n", TO_UTF8( field ) );

            // Write pin list:
            ret |= fprintf( out, "$BeginPinList\n" );
            for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ )
            {
                NETLIST_OBJECT* Pin = m_SortedComponentPinList[ii];
                if( !Pin )
                    continue;

                sprintPinNetName( &netname, wxT( "$-%.6d" ), Pin );
                if( netname.IsEmpty() )
                    netname = wxT( "?" );

                ret |= fprintf( out, "%.4s=%s\n", (char*) &Pin->m_PinNum, TO_UTF8( netname ) );
            }

            ret |= fprintf( out, "$EndPinList\n" );
            ret |= fprintf( out, "$EndComponent\n" );
        }
    }

    ret |= fprintf( out, "$EndComponentList\n" );

    ret |= fprintf( out, "\n$BeginNets\n" );

    if( !writeGENERICListOfNets( out, g_NetObjectslist ) )
        ret = -1;

    ret |= fprintf( out, "$EndNets\n" );

    ret |= fprintf( out, "\n$EndNetlist\n" );
    ret |= fclose( out );

    return ret >= 0;
#endif
}


bool EXPORT_HELP::WriteNetListPspice( FILE* f, bool use_netnames, bool aUsePrefix )
{
    int                 ret = 0;
    char                line[1024];
    int                 nbitems;
    wxString            text;
    wxArrayString       spiceCommandAtBeginFile;
    wxArrayString       spiceCommandAtEndFile;
    wxString            msg;
    wxString            netName;

    #define BUFYPOS_LEN 4
    wxChar              bufnum[BUFYPOS_LEN + 1];
    std::vector<int>    pinSequence;                    // numeric indices into m_SortedComponentPinList
    wxArrayString       stdPinNameArray;                // Array containing Standard Pin Names
    wxString            delimeters = wxT( "{:,; }" );
    wxString            disableStr = wxT( "N" );

    DateAndTime( line );

    ret |= fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, line );

    // Prepare list of nets generation (not used here, but...
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

    ret |= fprintf( f, "* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N\n" );
    ret |= fprintf( f, "* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0\n" );

    // 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;
    SCH_SHEET_LIST sheetList;

    for( SCH_SHEET_PATH* sheet = sheetList.GetFirst(); sheet; sheet = sheetList.GetNext() )
    {
        for( EDA_ITEM* item = sheet->LastDrawList(); item; item = item->Next() )
        {
            wxChar ident;
            if( item->Type() != SCH_TEXT_T )
                continue;

            SCH_TEXT*   drawText = (SCH_TEXT*) item;

            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.
            text.MakeLower();
            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( int ii = 0; ii < BUFYPOS_LEN; ii++ )
                {
                    bufnum[BUFYPOS_LEN - 1 - ii] = (ypos & 63) + ' ';
                    ypos >>= 6;
                }

                text = drawText->m_Text.AfterFirst( ' ' );

                // First BUFYPOS_LEN char are the Y position.
                msg.Printf( wxT( "%s %s" ), bufnum, text.GetData() );
                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( int ii = 0; ii < nbitems; ii++ )
        {
            spiceCommandAtBeginFile[ii].Remove( 0, BUFYPOS_LEN );
            spiceCommandAtBeginFile[ii].Trim( true );
            spiceCommandAtBeginFile[ii].Trim( false );
            ret |= fprintf( f, "%s\n", TO_UTF8( spiceCommandAtBeginFile[ii] ) );
        }
    }
    ret |= fprintf( f, "\n" );

    //  Create component list

    m_ReferencesAlreadyFound.Clear();

    for( SCH_SHEET_PATH* sheet = sheetList.GetFirst();  sheet;  sheet = sheetList.GetNext() )
    {
        fprintf( f, "*Sheet Name:%s\n", TO_UTF8( sheet->PathHumanReadable() ) );

        for( EDA_ITEM* item = sheet->LastDrawList();  item;  item = item->Next() )
        {
            SCH_COMPONENT* comp = findNextComponentAndCreatePinList( item, sheet );
            if( !comp )
                break;

            item = comp;

            // Reset NodeSeqIndex Count:
            pinSequence.clear();

            // Check to see if component should be removed from Spice Netlist:
            SCH_FIELD*  netlistEnabledField = comp->FindField( wxT( "Spice_Netlist_Enabled" ) );
            if( netlistEnabledField )
            {
                wxString netlistEnabled = netlistEnabledField->m_Text;

                if( netlistEnabled.IsEmpty() )
                    break;

                if( netlistEnabled.CmpNoCase( disableStr ) == 0 )
                    continue;
            }

            // Check if Alternative Pin Sequence is Available:
            SCH_FIELD*  spiceSeqField = comp->FindField( wxT( "Spice_Node_Sequence" ) );
            if( spiceSeqField )
            {
                // Get String containing the Sequence of Nodes:
                wxString nodeSeqIndexLineStr = spiceSeqField->m_Text;

                // Verify Field Exists and is not empty:
                if( nodeSeqIndexLineStr.IsEmpty() )
                    break;

                // Create an Array of Standard Pin Names from part definition:
                stdPinNameArray.Clear();
                for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ )
                {
                    NETLIST_OBJECT* pin = m_SortedComponentPinList[ii];
                    if( !pin )
                        continue;
                    stdPinNameArray.Add( pin->GetPinNumText() );
                }

                // Get Alt Pin Name Array From User:
                wxStringTokenizer tkz( nodeSeqIndexLineStr, delimeters );
                while( tkz.HasMoreTokens() )
                {
                    wxString    pinIndex = tkz.GetNextToken();
                    int         seq;

                    // Find PinName In Standard List assign Standard List Index to Name:
                    seq = stdPinNameArray.Index(pinIndex);

                    if( seq != wxNOT_FOUND )
                    {
                        pinSequence.push_back( seq );
                    }
                }
            }

            //Get Standard Reference Designator:
            wxString RefName = comp->GetRef( sheet );

            //Conditionally add Prefix only for devices that begin with U or IC:
            if( aUsePrefix )
            {
                if( RefName.StartsWith( wxT( "U" ) ) || RefName.StartsWith( wxT( "IC" ) ) )
                    RefName = wxT( "X" ) + RefName;
            }

            ret |= fprintf( f, "%s ", TO_UTF8( RefName) );

            // Write pin list:
            int activePinIndex = 0;

            for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ )
            {
                // Case of Alt Sequence definition with Unused/Invalid Node index:
                // Valid used Node Indexes are in the set {0,1,2,...m_SortedComponentPinList.size()-1}
                if( pinSequence.size() )
                {
                    // All Vector values must be less <= max package size
                    // And Total Vector size should be <= package size
                    if( ( (unsigned) pinSequence[ii] < m_SortedComponentPinList.size() ) && ( ii < pinSequence.size() ) )
                    {
                        // Case of Alt Pin Sequence in control good Index:
                        activePinIndex = pinSequence[ii];
                    }
                    else
                    {
                        // Case of Alt Pin Sequence in control Bad Index or not using all pins for simulation:
                        continue;
                    }
                }
                // Case of Standard Pin Sequence in control:
                else
                {
                    activePinIndex = ii;
                }

                NETLIST_OBJECT* pin = m_SortedComponentPinList[activePinIndex];

                if( !pin )
                    continue;

                sprintPinNetName( &netName , wxT( "N-%.6d" ), pin );

                if( netName.IsEmpty() )
                    netName = wxT( "?" );

                if( use_netnames )
                    ret |= fprintf( f, " %s", TO_UTF8( netName ) );

                else    // Use number for net names (net number = 0 for "GND")
                {
                    // NetName = "0" is "GND" net for Spice
                    if( netName == wxT( "0" ) || netName == wxT( "GND" ) )
                        ret |= fprintf( f, " 0" );
                    else
                        ret |= fprintf( f, " %d", pin->GetNet() );
                }
            }

            // Get Component Value Name:
            wxString CompValue = comp->GetField( VALUE )->m_Text;

            // Check if Override Model Name is Provided:
            SCH_FIELD* spiceModelField = comp->FindField( wxT( "spice_model" ) );

            if( spiceModelField )
            {
                // Get Model Name String:
                wxString ModelNameStr = spiceModelField->m_Text;

                // Verify Field Exists and is not empty:
                if( !ModelNameStr.IsEmpty() )
                    CompValue = ModelNameStr;
            }

            // Print Component Value:
            ret |= fprintf( f, " %s\t\t",TO_UTF8( CompValue ) );

            // Show Seq Spec on same line as component using line-comment ";":
            for( unsigned i = 0;  i < pinSequence.size();  ++i )
            {
                if( i==0 )
                    fprintf( f, ";Node Sequence Spec.<" );

                fprintf( f, "%s", TO_UTF8( stdPinNameArray.Item( pinSequence[i] ) ) );

                if( i < pinSequence.size()-1 )
                    fprintf( f, "," );
                else
                    fprintf( f, ">" );
            }

            // Next Netlist line record:
            ret |= fprintf( f, "\n" );
        }
    }

    m_SortedComponentPinList.clear();

    // Print texts starting with [+]pspice or [+]gnucap
    nbitems = spiceCommandAtEndFile.GetCount();
    if( nbitems )
    {
        ret |= fprintf( f, "\n" );
        spiceCommandAtEndFile.Sort();

        for( int ii = 0; ii < nbitems; ii++ )
        {
            spiceCommandAtEndFile[ii].Remove( 0, +BUFYPOS_LEN );
            spiceCommandAtEndFile[ii].Trim( true );
            spiceCommandAtEndFile[ii].Trim( false );
            ret |= fprintf( f, "%s\n", TO_UTF8( spiceCommandAtEndFile[ii] ) );
        }
    }

    ret |= fprintf( f, "\n.end\n" );

    return ret >= 0;
}


bool EXPORT_HELP::WriteNetListPCBNEW( FILE* f, bool with_pcbnew )
{
    wxString    field;
    wxString    footprint;
    char        dateBuf[256];
    int         ret = 0;        // zero now, OR in the sign bit on error
    wxString    netName;

    std::vector< SCH_REFERENCE > cmpList;

    DateAndTime( dateBuf );

    if( with_pcbnew )
        ret |= fprintf( f, "# %s created  %s\n(\n", NETLIST_HEAD_STRING, dateBuf );
    else
        ret |= fprintf( f, "( { %s created  %s }\n", NETLIST_HEAD_STRING, dateBuf );

    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

    // Create netlist module section
    m_ReferencesAlreadyFound.Clear();

    SCH_SHEET_LIST sheetList;

    for( SCH_SHEET_PATH* path = sheetList.GetFirst();  path;  path = sheetList.GetNext() )
    {
        for( EDA_ITEM* item = path->LastDrawList();  item;  item = item->Next() )
        {
            SCH_COMPONENT* comp = findNextComponentAndCreatePinList( item, path );

            if( !comp )
                break;

            item = comp;

            // Get the Component FootprintFilter and put the component in
            // cmpList if filter is present
            LIB_COMPONENT* entry = CMP_LIBRARY::FindLibraryComponent( comp->GetLibName() );

            if( entry )
            {
                if( entry->GetFootPrints().GetCount() != 0 )    // Put in list
                {
                    cmpList.push_back( SCH_REFERENCE( comp, entry, *path ) );
                }
            }

            if( !comp->GetField( FOOTPRINT )->IsVoid() )
            {
                footprint = comp->GetField( FOOTPRINT )->m_Text;
                footprint.Replace( wxT( " " ), wxT( "_" ) );
            }
            else
                footprint = wxT( "$noname" );

            field = comp->GetRef( path );

            ret |= fprintf( f, " ( %s %s",
                            TO_UTF8( comp->GetPath( path ) ),
                            TO_UTF8( footprint ) );

            ret |= fprintf( f, "  %s", TO_UTF8( field ) );

            field = comp->GetField( VALUE )->m_Text;
            field.Replace( wxT( " " ), wxT( "_" ) );
            ret |= fprintf( f, " %s", TO_UTF8( field ) );

            if( with_pcbnew )  // Add the lib name for this component
            {
                field = comp->GetLibName();
                field.Replace( wxT( " " ), wxT( "_" ) );
                ret |= fprintf( f, " {Lib=%s}", TO_UTF8( field ) );
            }
            ret |= fprintf( f, "\n" );

            // Write pin list:
            for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ )
            {
                NETLIST_OBJECT* pin = m_SortedComponentPinList[ii];
                if( !pin )
                    continue;

                sprintPinNetName( &netName, wxT( "N-%.6d" ), pin );
                if( netName.IsEmpty() )
                    netName = wxT( "?" );

                netName.Replace( wxT( " " ), wxT( "_" ) );

                ret |= fprintf( f, "  ( %4.4s %s )\n", (char*) &pin->m_PinNum,
                                TO_UTF8( netName ) );
            }

            ret |= fprintf( f, " )\n" );
        }
    }

    ret |= fprintf( f, ")\n*\n" );

    m_SortedComponentPinList.clear();

    // Write the allowed footprint list for each component
    if( with_pcbnew && cmpList.size() )
    {
        wxString    ref;

        ret |= fprintf( f, "{ Allowed footprints by component:\n" );

        for( unsigned ii = 0; ii < cmpList.size(); ii++ )
        {
            LIB_COMPONENT* entry = cmpList[ii].GetLibComponent();

            ref = cmpList[ii].GetRef();

            ref.Replace( wxT( " " ), wxT( "_" ) );

            ret |= fprintf( f, "$component %s\n", TO_UTF8( ref ) );

            // Write the footprint list
            for( unsigned jj = 0; jj < entry->GetFootPrints().GetCount(); jj++ )
            {
                ret |= fprintf( f, " %s\n", TO_UTF8( entry->GetFootPrints()[jj] ) );
            }

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

        ret |= fprintf( f, "$endfootprintlist\n}\n" );
    }

    if( with_pcbnew )
    {
        ret |= fprintf( f, "{ Pin List by Nets\n" );

        if( !writeGENERICListOfNets( f, g_NetObjectslist ) )
            ret = -1;

        ret |= fprintf( f, "}\n" );
        ret |= fprintf( f, "#End\n" );
    }

    return ret >= 0;
}


bool EXPORT_HELP::addPinToComponentPinList( SCH_COMPONENT* aComponent,
                                      SCH_SHEET_PATH* aSheetPath, LIB_PIN* aPin )
{
    // Search the PIN description for Pin in g_NetObjectslist
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
    {
        NETLIST_OBJECT* pin = g_NetObjectslist[ii];

        if( pin->m_Type != NET_PIN )
            continue;

        if( pin->m_Link != aComponent )
            continue;

        if( pin->m_PinNum != aPin->GetNumber() )
            continue;

        // most expensive test at the end.
        if( pin->m_SheetList != *aSheetPath )
            continue;

        m_SortedComponentPinList.push_back( pin );

        if( m_SortedComponentPinList.size() >= MAXPIN )
        {
            // Log message for Internal error
            DisplayError( NULL, wxT( "addPinToComponentPinList err: MAXPIN reached" ) );
        }

        return true;  // we're done, we appended.
    }

    return false;
}

/*
 * remove duplicate pins from aPinList (list of pins relative to a given component)
 * (i.e. set pointer to duplicate pins to NULL in this list).
 * also set .m_Flag member of "removed" NETLIST_OBJECT pins to 1
 */
void EXPORT_HELP::eraseDuplicatePins( )
{
    if( m_SortedComponentPinList.size() == 0 )  // Trivial case: component with no pin
        return;

    for( unsigned ii = 0; ii < m_SortedComponentPinList.size(); ii++ )
    {
        if( m_SortedComponentPinList[ii] == NULL ) /* already deleted */
            continue;

        /* Search for duplicated pins
         * If found, remove duplicates. The priority is to keep connected pins
         * and remove unconnected
         * - So this allows (for instance when using multi op amps per package
         * - to connect only one op amp to power
         * Because the pin list is sorted by m_PinNum value, duplicated pins
         * are necessary successive in list
         */
        int idxref = ii;
        for( unsigned jj = ii + 1; jj < m_SortedComponentPinList.size(); jj++ )
        {
            if(  m_SortedComponentPinList[jj] == NULL )   // Already removed
                continue;

            // if other pin num, stop search,
            // because all pins having the same number are consecutive in list.
            if( m_SortedComponentPinList[idxref]->m_PinNum != m_SortedComponentPinList[jj]->m_PinNum )
                break;

            if( m_SortedComponentPinList[idxref]->m_FlagOfConnection == PAD_CONNECT )
            {
                m_SortedComponentPinList[jj]->m_Flag = 1;
                m_SortedComponentPinList[jj] = NULL;
            }
            else /* the reference pin is not connected: remove this pin if the
                  * other pin is connected */
            {
                if( m_SortedComponentPinList[jj]->m_FlagOfConnection == PAD_CONNECT )
                {
                    m_SortedComponentPinList[idxref]->m_Flag = 1;
                    m_SortedComponentPinList[idxref] = NULL;
                    idxref = jj;
                }
                else    // the 2 pins are not connected: remove the tested pin,
                {       // and continue ...
                    m_SortedComponentPinList[jj]->m_Flag = 1;
                    m_SortedComponentPinList[jj] = NULL;
                }
            }
        }
    }
}


void EXPORT_HELP::findAllInstancesOfComponent( SCH_COMPONENT*  aComponent,
                                         LIB_COMPONENT*  aEntry,
                                         SCH_SHEET_PATH* aSheetPath )
{
    wxString    ref = aComponent->GetRef( aSheetPath );
    wxString    ref2;

    SCH_SHEET_LIST sheetList;

    for( SCH_SHEET_PATH* sheet = sheetList.GetFirst();  sheet;  sheet = sheetList.GetNext() )
    {
        for( EDA_ITEM* item = sheet->LastDrawList();  item;  item = item->Next() )
        {
            if( item->Type() != SCH_COMPONENT_T )
                continue;

            SCH_COMPONENT*  comp2 = (SCH_COMPONENT*) item;

            ref2 = comp2->GetRef( sheet );
            if( ref2.CmpNoCase( ref ) != 0 )
                continue;

            int unit2 = comp2->GetUnitSelection( sheet );  // slow

            for( LIB_PIN* pin = aEntry->GetNextPin();  pin;  pin = aEntry->GetNextPin( pin ) )
            {
                wxASSERT( pin->Type() == LIB_PIN_T );

                if( pin->GetUnit() && pin->GetUnit() != unit2 )
                    continue;

                if( pin->GetConvert() && pin->GetConvert() != comp2->GetConvert() )
                    continue;

                // A suitable pin is found: add it to the current list
                addPinToComponentPinList( comp2, sheet, pin );
            }
        }
    }
}


bool EXPORT_HELP::writeGENERICListOfNets( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
{
    int         ret = 0;
    int         netCode;
    int         lastNetCode = -1;
    int         sameNetcodeCount = 0;
    wxString    netName;
    wxString    ref;
    wxString    netcodeName;
    char        firstItemInNet[256];

    for( unsigned ii = 0; ii < aObjectsList.size(); ii++ )
    {
        SCH_COMPONENT*  comp;

        // New net found, write net id;
        if( ( netCode = aObjectsList[ii]->GetNet() ) != lastNetCode )
        {
            sameNetcodeCount = 0;              // Items count for this net
            netName.Empty();

            // Find a label (if exists) for this net.
            NETLIST_OBJECT* netref;
            netref = aObjectsList[ii]->m_NetNameCandidate;
            if( netref )
                netName = netref->m_Label;

            netcodeName.Printf( wxT( "Net %d " ), netCode );
            netcodeName += wxT( "\"" );
            if( !netName.IsEmpty() )
            {
                if( ( netref->m_Type != NET_PINLABEL )
                   && ( netref->m_Type != NET_GLOBLABEL ) )
                {
                    // usual net name, prefix it by the sheet path
                    netcodeName += netref->m_SheetList.PathHumanReadable();
                }
                netcodeName += netName;
            }
            netcodeName += wxT( "\"" );

            // Add the netname without prefix, in cases we need only the
            // "short" netname
            netcodeName += wxT( " \"" ) + netName + wxT( "\"" );
            lastNetCode  = netCode;
        }

        if( aObjectsList[ii]->m_Type != NET_PIN )
            continue;

        if( aObjectsList[ii]->m_Flag != 0 )     // Redundant pin, skip it
            continue;

        comp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link;

        // Get the reference for the net name and the main parent component
        ref = comp->GetRef( &aObjectsList[ii]->m_SheetList );
        if( ref[0] == wxChar( '#' ) )
            continue;                 // Pseudo component (Like Power symbol)

        // Print the pin list for this net, use special handling if
        // 2 or more items are connected:

        // if first item for this net found, defer printing this connection
        // until a second item will is found
        if( ++sameNetcodeCount == 1 )
        {
            snprintf( firstItemInNet, sizeof(firstItemInNet), " %s %.4s\n",
                      TO_UTF8( ref ),
                      (const char*) &aObjectsList[ii]->m_PinNum );
        }

        // Second item for this net found, print the Net name, and the
        // first item
        if( sameNetcodeCount == 2 )
        {
            ret |= fprintf( f, "%s\n", TO_UTF8( netcodeName ) );
            ret |= fputs( firstItemInNet, f );
        }

        if( sameNetcodeCount >= 2 )
            ret |= fprintf( f, " %s %.4s\n", TO_UTF8( ref ),
                     (const char*) &aObjectsList[ii]->m_PinNum );
    }

    return ret >= 0;
}


/* Generate CADSTAR net list. */
static wxString StartLine( wxT( "." ) );


void EXPORT_HELP::WriteNetListCADSTAR( FILE* f )
{
    wxString StartCmpDesc = StartLine + wxT( "ADD_COM" );
    wxString msg;
    wxString footprint;
    char Line[1024];
    SCH_SHEET_PATH* sheet;
    EDA_ITEM* DrawList;
    SCH_COMPONENT* Component;
    wxString Title = wxGetApp().GetAppName() + wxT( " " ) + GetBuildVersion();

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

    // Prepare list of nets generation
    for( unsigned ii = 0; ii < g_NetObjectslist.size(); ii++ )
        g_NetObjectslist[ii]->m_Flag = 0;

    // Create netlist module section
    m_ReferencesAlreadyFound.Clear();

    SCH_SHEET_LIST SheetList;

    for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() )
    {
        for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Next() )
        {
            DrawList = Component = findNextComponentAndCreatePinList( DrawList, sheet );
            if( Component == NULL )
                break;

            /*
            doing nothing with footprint
            if( !Component->GetField( FOOTPRINT )->IsVoid() )
            {
                footprint = Component->GetField( FOOTPRINT )->m_Text;
                footprint.Replace( wxT( " " ), wxT( "_" ) );
            }
            else
                footprint = wxT( "$noname" );
            */

            msg = Component->GetRef( sheet );
            fprintf( f, "%s     ", TO_UTF8( StartCmpDesc ) );
            fprintf( f, "%s", TO_UTF8( msg ) );

            msg = Component->GetField( VALUE )->m_Text;
            msg.Replace( wxT( " " ), wxT( "_" ) );
            fprintf( f, "     \"%s\"", TO_UTF8( msg ) );
            fprintf( f, "\n" );
        }
    }

    fprintf( f, "\n" );

    m_SortedComponentPinList.clear();

    writeListOfNetsCADSTAR( f, g_NetObjectslist );

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


void EXPORT_HELP::writeListOfNetsCADSTAR( FILE* f, NETLIST_OBJECT_LIST& aObjectsList )
{
    wxString InitNetDesc  = StartLine + wxT( "ADD_TER" );
    wxString StartNetDesc = StartLine + wxT( "TER" );
    wxString netcodeName, InitNetDescLine;
    unsigned ii;
    int print_ter = 0;
    int NetCode, lastNetCode = -1;
    SCH_COMPONENT* Cmp;
    wxString NetName;

    for( ii = 0; ii < g_NetObjectslist.size(); ii++ )
    {
        // Get the NetName of the current net :
        if( ( NetCode = aObjectsList[ii]->GetNet() ) != lastNetCode )
        {
            NetName.Empty();

            NETLIST_OBJECT* netref;
            netref = aObjectsList[ii]->m_NetNameCandidate;
            if( netref )
                NetName = netref->m_Label;

            netcodeName = wxT( "\"" );
            if( !NetName.IsEmpty() )
            {
                if( ( netref->m_Type != NET_PINLABEL )
                   && ( netref->m_Type != NET_GLOBLABEL ) )
                {
                    // usual net name, prefix it by the sheet path
                    netcodeName +=
                        netref->m_SheetList.PathHumanReadable();
                }
                netcodeName += NetName;
            }
            else  // this net has no name: create a default name $<net number>
                netcodeName << wxT( "$" ) << NetCode;
            netcodeName += wxT( "\"" );
            lastNetCode  = NetCode;
            print_ter    = 0;
        }


        if( aObjectsList[ii]->m_Type != NET_PIN )
            continue;

        if( aObjectsList[ii]->m_Flag != 0 )
            continue;

        Cmp = (SCH_COMPONENT*) aObjectsList[ii]->m_Link;
        wxString refstr = Cmp->GetRef( &(aObjectsList[ii]->m_SheetList) );
        if( refstr[0] == '#' )
            continue;  // Power supply symbols.

        switch( print_ter )
        {
        case 0:
        {
            char buf[5];
            wxString str_pinnum;
            strncpy( buf, (char*) &aObjectsList[ii]->m_PinNum, 4 );
            buf[4]     = 0;
            str_pinnum = FROM_UTF8( buf );
            InitNetDescLine.Printf( wxT( "\n%s   %s   %.4s     %s" ),
                                   GetChars( InitNetDesc ),
                                   GetChars( refstr ),
                                   GetChars( str_pinnum ),
                                   GetChars( netcodeName ) );
        }
            print_ter++;
            break;

        case 1:
            fprintf( f, "%s\n", TO_UTF8( InitNetDescLine ) );
            fprintf( f, "%s       %s   %.4s\n",
                     TO_UTF8( StartNetDesc ),
                     TO_UTF8( refstr ),
                     (char*) &aObjectsList[ii]->m_PinNum );
            print_ter++;
            break;

        default:
            fprintf( f, "            %s   %.4s\n",
                     TO_UTF8( refstr ),
                     (char*) &aObjectsList[ii]->m_PinNum );
            break;
        }

        aObjectsList[ii]->m_Flag = 1;
    }
}
