/**********************************/
/* Netlist generator for pads-pcb */
/**********************************/

/* read the generic netlist created by eeschema and convert it to a pads-pcb form
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <ctype.h>

/* Pads-pcb sample:
 *PADS-PCB*
 *PART*
 *  C1 CHIP_D
 *  C2 C1206
 *  C3 CHIP_B
 *  C4 CHIP_B
 *  D1 CHIP_B
 *  JP1 unknown
 *
 *NET*
 *SIGNAL* N03791
 *  C3.1 R1.1 JP2.7 U1.3
 *SIGNAL* VCC
 *  U2.14 Y1.8 JP5.2 U3.2 C1.1 U1.20 JP1.8 JP1.3
 *  C5.2 C6.1 C7.2 U1.7 Y1.7
 *SIGNAL* N01384
 *  JP5.1 U1.1
 *SIGNAL* N02594
 *END*
 */


/* Generic netlist sample:
 *  $BeginNetlist
 *  $BeginComponentList
 *
 *  $BeginComponent
 *  TimeStamp=32568D1E
 *  Footprint=
 *  Reference=JP1
 *  Value=CONN_8X2
 *  Libname=CONN_8X2
 *  $BeginPinList
 *  1=GND
 *  2=REF10_1
 *  3=GND
 *  4=REF11_1
 *  5=GND
 *  6=REF7_1
 *  7=GND
 *  8=REF9_1
 *  9=GND
 *  10=REF6_1
 *  11=GND
 *  12=REF8_1
 *  13=GND
 *  14=REF4_1
 *  15=GND
 *  16=REF5_1
 *  $EndPinList
 *  $EndComponent
 *
 *  $BeginComponent
 *  TimeStamp=325679C1
 *  Footprint=
 *  Reference=RR1
 *  Value=9x1K
 *  Libref=RR9
 *  $BeginPinList
 *  1=VCC
 *  2=REF5_1
 *  3=REF4_1
 *  4=REF8_1
 *  5=REF6_1
 *  6=REF9_1
 *  7=REF7_1
 *  8=REF11_1
 *  9=REF10_1
 *  10=?
 *  $EndPinList
 *  $EndComponent
 *  $EndComponentList
 *
 *  $BeginNets
 *  Net 0 ""
 *  Net 1 "GND"
 *  BUS1 31
 *  U3 19
 *  U3 10
 *  U3 1
 *  Net 172 ""
 *  BUS1 32
 *  Net 173 ""
 *  BUS1 30
 *  $EndNets
 *
 *  $EndNetlist
 */

char*   GetLine( FILE* File, char* Line, int* LineNum, int SizeLine );
int     ReadAndWriteComponentDataSection( FILE* InFile, FILE* OutFile, int* LineNumber );
int     ReadAndWriteNetsDataSection( FILE* InFile, FILE* OutFile, int* LineNumber );
int AreStringsEqual( const char * src, const char * ref );


class ComponentDataClass
{
public:
    char m_Reference[256];
    char m_Value[256];
    char m_Footprint[256];
    char m_LibRef[256];
    long m_TimeStamp;

public:
    ComponentDataClass()
    {
        InitData();
    }


    void InitData()
    {
        m_TimeStamp    = 0;
        m_Reference[0] = 0;
        m_Value[0]     = 0;
        m_Footprint[0] = 0;
        m_LibRef[0]    = 0;
    }
};


/********************************/
int main( int argc, char** argv )
/********************************/
{
    char* InputfileName, * OutputFilename;
    FILE* InFile, * OutFile;
    int   LineNumber;
    char  Line[1024];

    if( argc < 3 )
    {
        printf( "\nUsage; netlist_form_pads-pcb infile outfile\n" );
        return -1;
    }

    InputfileName  = argv[1];
    OutputFilename = argv[2];

    if( ( InFile = fopen( InputfileName, "rt" ) ) == NULL )
    {
        printf( "Failed to open file %s", InputfileName );
        return -2;
    }
    if( ( OutFile = fopen( OutputFilename, "wt" ) ) == NULL )
    {
        printf( "Failed to create file %s", OutputFilename );
        return -3;
    }


    /* Write header: */
    fprintf( OutFile, "*PADS-PCB*\n*PART*\n" );

    /* Read and write data lines */
    while( GetLine( InFile, Line, &LineNumber, sizeof(Line) ) )
    {
        if( AreStringsEqual( Line, "$BeginComponent" ) )
        {
            ReadAndWriteComponentDataSection( InFile, OutFile, &LineNumber );
            continue;
        }
        if( AreStringsEqual( Line, "$BeginNets" )  )
        {
            fprintf( OutFile, "\n*NET*\n" );
            ReadAndWriteNetsDataSection( InFile, OutFile, &LineNumber );
            continue;
        }
    }


    fprintf( OutFile, "*END*\n" );

    fclose( InFile );
    fclose( OutFile );

    return 0;
}


/****************************************************************/
int ReadAndWriteComponentDataSection( FILE* InFile, FILE* OutFile, int* LineNumber )
/****************************************************************/

/* Read the Components Section from the Generic Netlist and create Components section in Pads-Pcb format
 *  For the component section only reference and footprint are used.
 *  Create lines like:
 *  C1 CHIP_D
 *  C2 unknown
 */
{
    char  Line[1024];

    class ComponentDataClass ComponentData;
    char* ident, * data;

    while( GetLine( InFile, Line, LineNumber, sizeof(Line) ) )
    {
        if( AreStringsEqual( Line, "$BeginPinList" )  )
        {
            while( GetLine( InFile, Line, LineNumber, sizeof(Line) ) )
                if( AreStringsEqual( Line, "$EndPinList" )  )
                    break;

            continue;
        }

        if( AreStringsEqual( Line, "$EndComponent" )  ) // Create the output for the component:
        {
            /* Create the line like: C2 unknown */

            fprintf( OutFile, "%s ", ComponentData.m_Reference );
            fprintf( OutFile, "%s\n",
                     strlen( ComponentData.m_Footprint ) ? ComponentData.m_Footprint : "unknown" );
            return 0;
        }
        ident = strtok( Line, "=\n\r" );
        data  = strtok( NULL, "=\n\r" );
        if( data == NULL )
            continue;
        if( AreStringsEqual( Line, "TimeStamp" )  )
        {
            ComponentData.m_TimeStamp = atol( data );
            continue;
        }
        if( AreStringsEqual( Line, "Footprint" ) )
        {
            strncpy( ComponentData.m_Footprint, data, 255 );
            continue;
        }
        if( AreStringsEqual( Line, "Reference" )  )
        {
            strncpy( ComponentData.m_Reference, data, 255 );
            continue;
        }
        if( AreStringsEqual( Line, "Value" )  )
        {
            strncpy( ComponentData.m_Value, data, 255 );
            continue;
        }
        if( AreStringsEqual( Line, "Libref" )  )
        {
            strncpy( ComponentData.m_LibRef, data, 255 );
            continue;
        }
    }

    return 1;
}


/****************************************************************/
int ReadAndWriteNetsDataSection( FILE* InFile, FILE* OutFile, int* LineNumber )
/****************************************************************/

/* Read the Nets Section from the Generic Netlist and create Nets section in Pads-Pcb format
 *  create info type:
 *SIGNAL* N03791
 *  C3.1 R1.1 JP2.7 U1.3
 */
{
    char  Line[1024];
    char* ident, * netnum, * netname, * pin;

    while( GetLine( InFile, Line, LineNumber, sizeof(Line) ) )
    {
        if( AreStringsEqual( Line, "$EndNets" )  )
            return 0;
        ident = strtok( Line, " \n\r" );
        if( AreStringsEqual( ident, "Net" )  )
        {
            netnum  = strtok( NULL, " \n\r" );
            netname = strtok( NULL, " \"\n\r" );
            if( (netname == NULL) || strlen( netname ) == 0 )
            {   // Create the line like: *SIGNAL* N03791
                int num = atoi( netnum );
                sprintf( Line, "N-%6.6d", num );
                netname = Line;
            }
            fprintf( OutFile, "*SIGNAL* %s\n", netname );
        }
        else
        {
            // Create the line like:   C3.1 R1.1 JP2.7 U1.3
            pin = strtok( NULL, " \n\r" );
            fprintf( OutFile, " %s.%s\n", ident, pin );
        }
    }

    return 1;
}


/*****************************************************************/
char* GetLine( FILE* File, char* Line, int* LineNum, int SizeLine )
/*****************************************************************/

/* Return a non empty line
 *  increment *LineNum for each read line
 *  Comment lines (starting by '#') are skipped
 */
{
    do  {
        if( fgets( Line, SizeLine, File ) == NULL )
            return NULL;
        if( LineNum )
            *LineNum += 1;
    } while( Line[0] == '#' || Line[0] == '\n' ||  Line[0] == '\r'
             || Line[0] == 0 );

    strtok( Line, "\n\r" );
    return Line;
}

/***************************************************/
int AreStringsEqual( const char * src, const char * ref )
/***************************************************/
/* Compare 2 strings (case insensitive),
 * and return 1 (true) if equal or 0 (false) if different
 * stricmp does the same job, but does not exist under all systems
 */
{
    int match = 1;
    while (src && ref )
    {
        unsigned char c1 = *src;
        unsigned char c2 = *ref;
        if ( c1 >= 'a' && c1 <= 'z' )
            c1 += 'A' - 'a';
        if ( c2 >= 'a' && c2 <= 'z' )
            c2 += 'A' - 'a';
         if ( c1 != c2 )
        {
            match = 0;
            break;
        }
        if ( c1 == 0 || c2 == 0 )
            break;
        src++; ref++;
    }

    return match;
}
