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

FILE *fOut = NULL;
bool bHasDevNull;

bool testCpp( const char *prog )
{
    FILE *pop = popen(
        bHasDevNull?"g++ -x c++ - -o /dev/null":"g++ -x c++ - -o trash.del",
        "w");
    fwrite( prog, 1, strlen( prog ), pop );
    bool bFound = (pclose(pop) == 0);
    if( !bHasDevNull )
        unlink("trash.del");
    return bFound;
}

bool testLib( const char *lib, const char *symname, const char *humname )
{
    printf("Detecting library %s (-l%s)...", humname, lib );
    fflush( stdout );
    static const char *prog = "int main () { return 0; }";
    char *cmdline = (char *)malloc( 29+strlen(lib) );
    strcpy( cmdline, "g++ -x c++ -l");
    strcat( cmdline, lib );
    if( bHasDevNull )
        strcat( cmdline, " - -o /dev/null");
    else
        strcat( cmdline, " - -o trash.del");

    FILE *pop = popen( cmdline, "w");
    fwrite( prog, 1, strlen( prog ), pop );
    free( cmdline );
    bool bFound = (pclose(pop) == 0);
    if( bFound )
    {
        printf("found.\n");
        fprintf( fOut, "#define BU_FEATURE_%s 1\n", symname );
        fprintf( fOut, "#define BU_HAS_%s\n", symname );
    }
    else
    {
        printf("missing.\n");
        fprintf( fOut, "#define BU_FEATURE_%s 0\n", symname );
        fprintf( fOut, "#define BU_MISSING_%s\n", symname );
    }

    if( !bHasDevNull )
        unlink("trash.del");
    return bFound;
}

void detectEndianness()
{
    printf("Detecting endian support...");
    fflush( stdout );
    if( testCpp("#include <endian.h>\n\nint main() { return BYTE_ORDER; }\n") )
    {
        fprintf( fOut, "#include <endian.h>\n\n");
        printf("header file endian.h found, using that.\n");
    }
    else
    {
        uint16_t x=0x0100;
        fprintf( fOut,
            "#define LITTLE_ENDIAN 0\n"
            "#define BIG_ENDIAN 1\n"
            "#define BYTE_ORDER %d\n\n",
            ((uint8_t *)&x)[0]
            );
        printf("no header file found, faking it...\n"
            "\tArchetecture is: %s Endian\n",
            ((uint8_t *)&x)[0]?"Big":"Little"
            );
    }
}

bool fileExists( const char *filename )
{
    FILE *fTmp = fopen(filename, "rw");
    if( fTmp == NULL )
        return false;
    fclose( fTmp );
    return true;
}

int main( int argc, char *argv[] )
{
    if( argc == 1 )
    {
        fprintf( stderr,
            "Invalid usage: specify a file to generate:\n"
            "    src/autoconfig.h\n"
            "    src/version.h\n"
            "\n"
            );
        return 127;
    }
    bHasDevNull = fileExists("/dev/null");
    if( strcmp( argv[1], "src/autoconfig.h" ) == 0 )
    {
        fOut = fopen( argv[1], "w" );
        fprintf( fOut,
            "#ifndef BU_AUTO_CONFIG_H\n"
            "#define BU_AUTO_CONFIG_H\n\n"
            );

        detectEndianness();
        testLib("z", "DEFLATE", "Deflate");
        testLib("bz2", "BZIP2", "BZip2");
        testLib("lzma", "LZMA", "Lzma");

        fprintf( fOut, "#endif\n");
    }
    else if( strcmp( argv[1], "src/version.h" ) == 0 )
    {
        FILE *fVer = fopen("version", "rt");
        char buf[1024];
        buf[fread( buf, 1, 1024, fVer )] = '\0';
        for( int j = 0; buf[j]; j++ )
            if( buf[j] == '\n' )
                buf[j] = '\0';
        fclose( fVer );

        char *end;
        int32_t iVer = strtol( buf, &end, 10 );
        for(; (*end < '1' || *end > '9') && *end; end++ ) { }
        int32_t iRev = strtol( end, NULL, 10 );

        fOut = fopen( argv[1], "w" );
        fprintf( fOut,
            "#ifndef BU_VERSION_H\n"
            "#define BU_VERSION_H\n\n"
            "#define LIBBU_VERSION      %d\n"
            "#define LIBBU_REVISION     %d\n"
            "#define LIBBU_VERSION_STR  \"%s\"\n"
            "#define LIBBU_API          0\n"
            "#define LIBBU_VC_ID        \"",
            iVer, iRev,
			buf
			);

		FILE *psub = popen("git describe --always", "r");
        buf[fread( buf, 1, 1024, psub )] = '\0';
        for( int j = 0; buf[j]; j++ )
        {
            if( buf[j] == '\n' )
            {
                buf[j] = '\0';
            }
        }
		fwrite( buf, strlen(buf), 1, fOut );
		pclose( psub );
		fprintf( fOut, "\"\n\n#endif\n");
    }

    return 0;
}

