// (c) 1990-2007 Electronic Arts Inc.

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                            Math v1.15                            ",
"                                                                  ",
"                     by FrANK G. Barchard, EAC                    ",
"                                                                  ",
"                    Main program - Mar 10, 2006                   ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
"    1.00 911126 FB based on XL                                    ",
"    1.01 920909 FB spit 2.0                                       ",
"    1.02 951025 FB Expression API, Win95, renamed math <- calc    ",
"    1.03 990210 FB EXPR is double based                           ",
"    1.04 000331 FB ranger based, INT64 hex, slopy decimal points  ",
"    1.05 010405 FB t for time supported                           ",
"    1.06 011012 FB time in minutes                                ",
"    1.07 020814 FB h for float as hex                             ",
"    1.08 020822 FB e for scientific, -return, remove year         ",
"    1.09 030508 FB removed mathx                                  ",
"    1.10 030908 FB added TOOL_VERSION, SPIT_VERSION               ",
"    1.11 030930 FB hex output add 0x prefix                       ",
"    1.12 031001 FB rewritten with spit for rsp files, -base       ",
"    1.13 050409 FB << more than 64                                ",
"    1.14 051019 FB pi variable added                              ",
"    1.15 060310 FB preparse , out from numbers 1,000 -> 1000      ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

/****************************************************************/
/*  Spit Setup                                                  */
/****************************************************************/

#include "spit.h"
#include "stdgimex.h"
#include "ranger.h"
// for floor()
#include <math.h>

const char TextVersion[]="1.15 (Samba) 2007.12.4";
#define TOOL_VERSION 115
#define TOOL_NAME "math"

char OPTreturn[SPITMAXLINE];
int OPTbase;

#define MATHEPSILON 0.00000001

static __inline double ROUNDDOUBLE(double f)
{
    double r;

#if defined(_MSC_VER) && !defined(_WIN64)
    __asm
    {
        fld     f
        frndint
        fstp    r
    }
#else
    if (f<0.0)
        r = -floor(-f+0.5);
    else
        r = floor(f+0.5);
#endif
    return(r);
}

/*
;
; ABSTRACT
;
; math - an example spit application that tests command line parsing
;
; SUMMARY
;
; math -options [files]
;     @file          response file, containing a list of options and files
;     =file          specify a output file ie source=dest
;     -b0            disable banner
;     -v0            no console output
;     -v1            less output
;     -v2            normal output (default)
;     -v             more output
;     -w0            dont output file
;     -r             recurse subdirectories to find files
;     -r%ENV%        traverse environment variable ENV to find files
;     -time          show execution time
;     -base<n>       select base for input (default 10)
;     -return<expr>  return expression (default expression result)
;                    variables available:
;                      pi, mode, argc, i, errorcount
;                      TOOL_VERSION
;                      SPIT_VERSION
;
; DESCRIPTION
;
; Math is does spit wildcards & command line parsing, displaying
; the list of files and options, but doing no operations on them.
; The tool is intended to be used as the starting point for new spit
; tools
;
; RETURNS
;
; error count by default (0 for no errors)
; -return value if specified
;
; SEE ALSO
;
; huff, Spit
;
; SAMPLE OUTPUT
;
; >math spit*.?
;
;   Math v1.00 (text) 930101 by FrANK Barchard, EAC
;   spiti.c: Reading, Analysing, Writing
;   spit.h: Reading, Analysing, Writing
;
; END ABSTRACT
;
*/

/****************************************************************/
/*  SPIT Routines                                               */
/****************************************************************/

void SPIT_reset(void)
{
    OPTkeepopts = 1;
    OPTbanner = 0;
    OPTtime = 0;
    OPTbase = 10;
    strcpy(OPTreturn,"");

    return;
}

char *optionhelp[]=
{
    "    @file          response file, containing a list of options and files\n",
    "    =file          specify a output file ie source=dest\n",
    "    -b0            disable banner\n",
    "    -v0            no console output\n"
    "    -v1            less output\n"
    "    -v2            normal output (default)\n"
    "    -v             more output\n",
    "    -w0            dont output file\n",
    "    -r             recurse subdirectories to find files\n"
    "    -r%ENV%        traverse environment variable ENV to find files\n",
    "    -time          show execution time\n",
    "    -base<n>       select base for input (default 10)\n",
    "    -return<expr>  return expression (default expression result)\n"
    "                   variables available:\n"
    "                     pi, mode, argc, i, errorcount\n"
    "                     TOOL_VERSION\n"
    "                     SPIT_VERSION\n",
    "\n",
    "RANGE SYNTAX:\n"
    "    [[!][<start>][..][<end>][+=<step>][,]]*\n"
    "    ie -f             all frames\n"
    "       -f5            frame 5\n"
    "       -f5..9         frames 5..9 inclusive\n"
    "       -f!10          all frames except\n"
    "       -f5,7,9        frames 5,7 and 9\n"
    "       -f10..98+=2    even frames in range between 10 and 98\n",
    "\n",
    0,
};

void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    char **hlp;

    printf("Math v%s by FrANK G. Barchard\n",TextVersion);

    if (utype)
        printf("USAGE: %s -options [files]\n", prgname);

    switch (utype)
    {
        case 0:
        case 1:
            break;

        case 2:
            puts("    -m my option              -base bases option");
            puts("    -comment option           -time time execution");
            puts("    -v verbose                   -w write file");
            puts("     @ response file              = output file");
          printf("type '%s -h' for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname, urlname);
            break;

        case 3:
            puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPIT_MACHINE,TextVersion,(float)SPIT_VERSION/100.0f);
            puts("SUMMARY: An example spit application that tests command line parsing");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");

            hlp = optionhelp;
            while (*hlp)
            {
                printf("%s",*hlp);
                ++hlp;
            }
            break;

         case 4:
            puts("");
            {
                hlp = optionhelp;
                while (*hlp)
                {
                    if (SPIT_strstr(*hlp,envname))
                        printf("%s",*hlp);
                    ++hlp;
                }
                puts("");
            }
            break;

        default:
            break;
    }
}
bool gIgnoreUnknownOptions=true;

char *SPIT_opt(const char* optptr)
{
    char *d=NULL;
    switch (tolower(*optptr))
    {
        case 'b':
            if (!strnicmp(optptr,"base",4))
                d = SPIT_getopt( &OPTbase,optptr+4, "base", 2, 16);
            break;

        case 'r':
            if (!strnicmp(optptr,"return",6))
            {
                d = SPIT_getfield( OPTreturn,optptr+6,1,0);
            }
            break;

        // note this is unusual, but we're going to pretend its an option
        default:
            if (gIgnoreUnknownOptions)
            {
                char Number[SPITMAXLINE];
                d = SPIT_getfield( Number,optptr,0,1);
            }
            break;
    }
    return(d);
}

int main(int argc, char* argv[])
{
    int more;
    int i;
    int isopt;
    int errorcount=0;
    double totaltime;
    enum { DEC=0, HEX=1, CHAR=2, HEXFLOAT=3, OCT=4, SCIENTIFIC=5 } mode = DEC;

    // remove , from numbers to allow expressions to be passed from excel
    for (i=0; i<argc; ++i)
    {
        int last = (int) strlen(argv[i])-1;
        for (int j=1; j<last; ++j)
        {
            if (argv[i][j]==','
             && argv[i][j-1]>='0' && argv[i][j-1]<='9'
             && argv[i][j+1]>='0' && argv[i][j+1]<='9')
            {
                 strcpy(&argv[i][j],&argv[i][j+1]);
                 --last;
            }
        }
    }

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);

    EXPR *expr = EXPR_open();

    totaltime = SPIT_gettime();

    do
    {
        gIgnoreUnknownOptions = true;   // avoid spit thinking negatives are options
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);
        gIgnoreUnknownOptions = false;

        if (!SPIThelpgiven)
        {
            for (i=0; i<SPITargnum; ++i)
            {
                isopt = 0;
                int oldverbose = OPTverbose;
                OPTverbose = 0;
                if (SPITsargv[i][0]=='-' || SPITsargv[i][0]=='+' || (SPITsargv[i][0]==OPTdot && SPITsargv[i][1]!='.' && SPITsargv[i][1]!='/' && SPITsargv[i][1]!='\\'))
                {
                    isopt = Zop(SPITsargv[i]+1)?1:0;
                }
                else if (SPITsargv[i][0]=='=')
                {
                    isopt = Zop(SPITsargv[i])?1:0;
                }
                OPTverbose = oldverbose;


                if (!stricmp(SPITsargv[i],"d"))
                {
                    mode = DEC;
                    isopt = 1;
                }
                else if (!stricmp(SPITsargv[i],"x"))
                {
                    mode = HEX;
                    isopt = 1;
                }
                else if (!stricmp(SPITsargv[i],"c"))
                {
                    mode = CHAR;
                    isopt = 1;
                }
                else if (!stricmp(SPITsargv[i],"h"))
                {
                    mode = HEXFLOAT;
                    isopt = 1;
                }
                else if (!stricmp(SPITsargv[i],"o"))
                {
                    mode = OCT;
                    isopt = 1;
                }
                else if (!stricmp(SPITsargv[i],"e"))
                {
                    mode = SCIENTIFIC;
                    isopt = 1;
                }

                if (!isopt)
                {
                    double l;

                    EXPR_setbase(expr,OPTbase);
                    RANGER *r = RANGER_open(expr,SPITsargv[i],0.0,100.0);  // min/max mostly ignored

                    EXPR_set(expr,"TOOL_VERSION", TOOL_VERSION);
                    EXPR_set(expr,"SPIT_VERSION", SPIT_VERSION);
                    EXPR_set(expr,"mode",mode);
                    EXPR_set(expr,"i",i);
                    EXPR_set(expr,"argc",SPITargnum);
                    EXPR_set(expr,"pi",3.14159265358979323846);

                    if (!RANGER_info(r,NULL,NULL,NULL))
                    {
                        if (OPTverbose)
                            printf( TOOL_NAME " - ERROR IN EXPRESSION: %s\n", SPITsargv[i]);
                    }
                    else
                    {
                        do
                        {
                            more = RANGER_next(r,&l);
                            if (more)
                            {
                                EXPR_set(expr,"x",l);
                                switch (mode)
                                {
                                    case DEC:
                                    {
                                        int decimals=-1;
                                        double lint=l;
                                        double diff;

                                        do
                                        {
                                            ++decimals;
                                            diff = lint-ROUNDDOUBLE(lint);
                                            if (diff<0.0)
                                                diff = -diff;
                                            lint *= 10.0f;

                                        } while (diff>MATHEPSILON && decimals<16);

                                        printf("%.*f\n", decimals, l);
                                        break;
                                    }

                                    case HEX:
                                        printf("0x%" FORMAT64 "x\n", (INT64)l);
                                        break;

                                    case CHAR:
                                        for (int j=6; j>=0; j--)    /* 56 bits of data */
                                            printf("%c",   (int) (unsigned char) ((INT64)l>>(j*8)));
                                        printf("\n");
                                        break;

                                    case HEXFLOAT:
                                    {
                                        float f=(float)l;
                                        printf("%04x\n", *(int *)&f);
                                        break;
                                    }

                                    case OCT:
                                        printf("%" FORMAT64 "o\n", (INT64)l);
                                        break;

                                    case SCIENTIFIC:
                                    {
                                        double lint=l;
                                        printf("%e\n", l);
                                        break;
                                    }
                                }
                            }
                        } while (more);
                    }

                    RANGER_close(r);
                }
            }
        }
    } while (more>0);


    int returncode = errorcount;
    if (OPTreturn[0])
    {
        RANGER *r = RANGER_open(expr,OPTreturn,0.0,1000000.0);  // min/max mostly ignored

        EXPR_set(expr,"errorcount",   errorcount);
        EXPR_set(expr,"TOOL_VERSION", TOOL_VERSION);
        EXPR_set(expr,"SPIT_VERSION", SPIT_VERSION);

        returncode = (int) RANGER_closest(r, (double) returncode, RANGER_ROUND_CLOSEST);

        RANGER_close(r);
    }
    EXPR_close(expr);

    if (OPTverbose==1)
        puts("");

    totaltime = SPIT_gettime()-totaltime;
    if (OPTtime)
    {
        if (totaltime>=3600000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds) (%.2f minutes) (%.2f hours)",totaltime,totaltime/1000.0,totaltime/60000.0,totaltime/3600000.0);
        else if (totaltime>=60000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds) (%.2f minutes)",totaltime,totaltime/1000.0,totaltime/60000.0);
        else if (totaltime>=1000.0)
            printf("TIME " TOOL_NAME " %.2f ms (%.2f seconds)",totaltime,totaltime/1000.0);
        else
            printf("TIME " TOOL_NAME " %.2f ms",totaltime);
    }

    SPIT_restore();

    if (galloccount)
    {
        if (OPTverbose>=2)
        {
            printf(TOOL_NAME " - WARNING MEMORY NOT FREED TO SYSTEM %d\n",galloccount);
            returncode = galloccount;
        }
    }

    return returncode;
}

