//
//  main.c
//  BulletTest
//
//  Copyright (c) 2011 Apple Inc.
//
#include <stdio.h>
#ifdef __APPLE__
#include <libgen.h>
#endif //__APPLE__

#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include "main.h"
#include "Utils.h"
#include "TestList.h"
#include "LinearMath/btScalar.h"

#if defined (BT_USE_NEON) || defined (BT_USE_SSE_IN_API)

#ifdef _WIN32
#define strcasecmp _stricmp
#define basename(A) A
#endif

#define EXIT_NO_ERROR INT_MIN

//int gReportNanoseconds = 0;    // in Utils.c

int gReportAverageTimes = 0;
int gExitOnError = 0;
char *gFullPath = NULL;
const char *gAppName = NULL;
int gArgc;
const char **gArgv;

typedef struct TestNode
{
    struct TestNode *next;
    const char      *name;
}TestNode;

TestNode *gNodeList = NULL;

static int ParseArgs( int argc, const char *argv[] );
static void PrintUsage( void );
static int Init( void );
static void ListTests(void );

const char *gArch = 
#ifdef __i386__ 
    "i386";
#elif defined __x86_64__
    "x86_64";
#elif defined __arm__
    "arm";
#elif defined _WIN64
	"win64";
#elif defined _WIN32
	"win32";
#else
    #error unknown arch
#endif





#include <stdio.h>

int main (int argc, const char * argv[])
{

   // Enable just one test programatically (instead of command-line param)
   // TestNode *node = (TestNode*) malloc( sizeof( TestNode ) );
   // node->name = "btDbvt";
   // node->next = 0;
   // gNodeList = node;
    
	srand(0.f);

    int numPassedTests=0;
	int numFailedTests= 0;

    int err;
    
    // Parse arguments. Build gNodeList.
    if( (err = ParseArgs( argc, argv ) ) )
    {
        if( EXIT_NO_ERROR == err )
            return 0;
        
        PrintUsage();        
        return err;
    }
    
    printf("Arch: %s\n", gArch );
    
    if( gReportAverageTimes )
        printf( "Reporting average times.\n" );
    else
        printf( "Reporting best times.\n" );
    
    // Set a few things up
    if( (err = Init() ))
    {
        printf( "Init failed.\n" );
        return err;
    }
    
    if( NULL == gNodeList )
    { // test everything
        printf( "No function list found. Testing everything...\n" );
        size_t i;
        for( i = 0; NULL != gTestList[i].test_func; i++ )
        {
            printf( "\n----------------------------------------------\n" );
            printf( "Testing %s:\n", gTestList[i].name );
            printf( "----------------------------------------------\n" );
            uint64_t startTime = ReadTicks();
            int local_error = gTestList[i].test_func();
            uint64_t currentTime = ReadTicks() - startTime;
            if( local_error )
            {
				numFailedTests++;
                printf( "*** %s test failed with error: %d\n", gTestList[i].name, local_error );
                if( gExitOnError )
                    return local_error;
                if( 0 == err )
                    err = local_error;
            }
            else
			{
				numPassedTests++;
                printf("%s Passed.\t\t\t(%2.2gs)\n", gTestList[i].name, TicksToSeconds(currentTime));
			}
        }
    }
    else
    { // test just the list
        while( NULL != gNodeList )
        {
            TestNode *currentNode = gNodeList;
            gNodeList = gNodeList->next;
            
            // Find the test with that name
            size_t i;
            for( i = 0; NULL != gTestList[i].test_func; i++ )
                if( 0 == strcasecmp( currentNode->name, gTestList[i].name ) )
                    break;
            
            if( NULL != gTestList[i].test_func )
            {
                printf( "\n----------------------------------------------\n" );
                printf( "Testing %s:\n", gTestList[i].name );
                printf( "----------------------------------------------\n" );
                uint64_t startTime = ReadTicks();
                int local_error = gTestList[i].test_func();
                uint64_t currentTime = ReadTicks() - startTime;
                if( local_error )
                {
					numFailedTests++;
                    printf( "*** %s test failed with error: %d\n", gTestList[i].name, local_error );
                    if( gExitOnError )
                        return local_error;
                    if( 0 == err )
                        err = local_error;
                }
                else
				{
					numPassedTests++;
                    printf("%s Passed.\t\t\t(%2.2gs)\n", gTestList[i].name, TicksToSeconds(currentTime));
				}
            }
            else
            {
                printf( "\n***Error: Test name \"%s\" not found! Skipping.\n", currentNode->name );
                err = -1;
                if( gExitOnError )
                    return -1;
            }
            
            free( currentNode );
        }
    }
	printf( "\n----------------------------------------------\n" );
	printf("numPassedTests = %d, numFailedTests = %d\n",numPassedTests,numFailedTests);
    
    free(gFullPath);
    return err;
}

static int Init( void )
{
    // init the timer
    TicksToCycles(0);
    
    return 0;
}

static int ParseArgs( int argc, const char *argv[] )
{
    int listTests = 0;
    TestNode *list = NULL;
    
    gArgc = argc;
    gArgv = argv;
    gFullPath = (char*)malloc( strlen(argv[0]) + 1);
    strcpy(gFullPath, argv[0]);
    gAppName = basename( gFullPath );
    if( NULL == gAppName )
        gAppName = "<unknown app name>";
    
    printf( "%s ", gAppName );
    int skipremaining=0;
    
    size_t i;
    for( i = 1; i < argc; i++ )
    {
        const char *arg = argv[i];
        printf( "\t%s", arg );
        if( arg[0] == '-' )
        {
            arg++;
            while( arg[0] != '\0' )
            {
                int stop = 0;
                switch( arg[0] )
                {
                    case 'a':
                        gReportAverageTimes ^= 1;
                        break;
                    case 'e':
                        gExitOnError ^= 1;
                        break;
                    case 'h':
                        PrintUsage();
                        return EXIT_NO_ERROR;
                    case 'l':
                        listTests ^= 1;
                        return EXIT_NO_ERROR;
                    case 's':
                        gReportNanoseconds ^= 1;
                        break;
                    case ' ':
                        stop = 1;
                        break;
                    case 'N'://ignore the -NSDocumentRevisionsDebugMode argument from XCode 4.3.2
                        skipremaining = 1;
                       stop = 1;
                        break;
                    default:
                        printf( "\nError: Unknown flag \'%c\'\n", arg[0] );
                        return -1;
                }
                if( stop )
                    break;
                arg++;
            }
        }
        else
        { // add function name to the list
            TestNode *node = (TestNode*) malloc( sizeof( TestNode ) );
            node->name = arg;
            node->next = list;
            list = node;
        }
        if (skipremaining)
            break;
    }
    
    // reverse the list of test names, and stick on gNodeList
    while( list )
    {
        TestNode *node = list;
        TestNode *next = node->next;
        node->next = gNodeList;
        gNodeList = node;
        list = next;
    }
    
    printf( "\n" );
    if( listTests )
        ListTests();
    
    return 0;
}


static void PrintUsage( void )
{
    printf("\nUsage:\n" );
    printf("%s: <-aehls> <test names>", gAppName);
    printf("Options:\n");
    printf("\t-a\tToggle report average times vs. best times. (Default: best times)\n");
    printf("\t-e\tToggle exit immediately on error behavior. (Default: off)\n");
    printf("\t-h\tPrint this message.\n");
    printf("\t-l\tToggle list available test names.  (Default: off)\n");
    printf("\t-s\tToggle report times in cycles or nanoseconds. (Default: cycles)\n\n");
    printf("\tOptions may be followed by one or more test names. If no test names \n" );
    printf("\tare provided, then all tests are run.\n\n");
}

static void ListTests(void )
{
    size_t i;
    
    printf("\nTests:\n");
    for( i = 0; NULL != gTestList[i].test_func; i++ )
    {
        printf( "%19s", gTestList[i].name );
        if( NULL != gTestList[i].test_func )
            printf( "," );
        if( 3 == (i&3) )
            printf( "\n" );
    }
}
#else
#include <stdio.h>
int main(int argc, char* argv[])
{
	printf("error: no SIMD enabled through BT_USE_NEON or BT_USE_SSE_IN_API \n(enable in LinearMath/btScalar.h or through build system)\n");
	return 0;
}
#endif
