//
// Copyright (c) 2017 The Khronos Group Inc.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "../../test_common/harness/compat.h"

#include "test_printf.h"


#if defined (_WIN32)
#define strtoull _strtoi64
#endif

const char* strType[] = {"int","float","octal","unsigned","hexadecimal","char","string","vector","address space"};



//==================================

// int

//==================================

//------------------------------------------------------

// [string] format  | [string] int-data representation |

//------------------------------------------------------

struct printDataGenParameters printIntGenParameters[] = {

    //(Minimum)Five-wide,default(right)-justified

    {"%5d","10"},

        //(Minimum)Five-wide,left-justified

    {"%-5d","10"},

        //(Minimum)Five-wide,default(right)-justified,zero-filled

    {"%05d","10"},

        //(Minimum)Five-wide,default(right)-justified,with sign

    {"%+5d","10"},

         //(Minimum)Five-wide ,left-justified,with sign

    {"%-+5d","10"},

        //(Minimum)Five-digit(zero-filled in absent digits),default(right)-justified

    {"%.5i","100"},

        //(Minimum)Six-wide,Five-digit(zero-filled in absent digits),default(right)-justified

    {"%6.5i","100"},

        //0 and - flag both apper ==>0 is ignored,left-justified,capital I

    {"%-06i","100"},

        //(Minimum)Six-wide,Five-digit(zero-filled in absent digits),default(right)-justified

    {"%06.5i","100"}

};

//------------------------------------------------

// Lookup table - [string]int-correct buffer     |

//------------------------------------------------

const char *correctBufferInt[] = {

    "   10",

    "10   ",

    "00010",

    "  +10",

    "+10  ",

    "00100",

    " 00100",

    "100   ",

    " 00100"

};





//-----------------------------------------------

//test case for int                             |

//-----------------------------------------------

testCase testCaseInt = {

    sizeof(correctBufferInt)/sizeof(char*),

    TYPE_INT,

    correctBufferInt,

    printIntGenParameters

};





//==============================================

// float

//==============================================



//--------------------------------------------------------

// [string] format |  [string] float-data representation |

//--------------------------------------------------------

struct printDataGenParameters printFloatGenParameters[] = {

    //Default(right)-justified

    {"%f","10.3456"},

    //One position after the decimal,default(right)-justified

    {"%.1f","10.3456"},

    //Two positions after the decimal,default(right)-justified

    {"%.2f","10.3456"},

    //(Minimum)Eight-wide,three positions after the decimal,default(right)-justified

    {"%8.3f","10.3456"},

    //(Minimum)Eight-wide,two positions after the decimal,zero-filled,default(right)-justified

    {"%08.2f","10.3456"},

    //(Minimum)Eight-wide,two positions after the decimal,left-justified

    {"%-8.2f","10.3456"},

    //(Minimum)Eight-wide,two positions after the decimal,with sign,default(right)-justified

    {"%+8.2f","-10.3456"},

    //Zero positions after the decimal([floor]rounding),default(right)-justified

    {"%.0f","0.1"},

    //Zero positions after the decimal([ceil]rounding),default(right)-justified

    {"%.0f","0.6"},

    //Zero-filled,default positions number after the decimal,default(right)-justified

    {"%0f","0.6"},

    //Double argument representing floating-point,used by f style,default(right)-justified

    {"%4g","12345.6789"},

    //Double argument representing floating-point,used by e style,default(right)-justified

    {"%4.2g","12345.6789"},

    //Double argument representing floating-point,used by f style,default(right)-justified

    {"%4G","0.0000023"},

    //Double argument representing floating-point,used by e style,default(right)-justified

    {"%4G","0.023"},

    //Double argument representing floating-point,with exponent,left-justified,default(right)-justified

    {"%-#20.15e","789456123.0"},

    //Double argument representing floating-point,with exponent,left-justified,with sign,capital E,default(right)-justified

    {"%+#21.15E","789456123.0"},

    //Double argument representing floating-point,in [-]xh.hhhhpAd style

    {"%.6a","0.1"},

    //(Minimum)Ten-wide,Double argument representing floating-point,in xh.hhhhpAd style,default(right)-justified

    {"%10.2a","9990.235"},

    //Infinity (1.0/0.0)

    {"%f","1.0f/0.0f"},

    //NaN

    {"%f","sqrt(-1.0f)"},

    //NaN
    {"%f","acospi(2.0f)"}
    };
//--------------------------------------------------------

//  Lookup table - [string]float-correct buffer             |

//--------------------------------------------------------

const char* correctBufferFloat[] = {

    "10.345600",

    "10.3",

    "10.35",

    "  10.346",

    "00010.35",

    "10.35   ",

    "  -10.35",

    "0",

    "1",

    "0.600000",

    "12345.7",

    "1.2e+4",

    "2.3E-6",

    "0.023",

    "7.894561230000000e+8",

    "+7.894561230000000E+8",

    "0x1.99999ap-4",

    "0x1.38p+13",

    "inf",

    "-nan",

    "nan"
};

//---------------------------------------------------------

//Test case for float                                     |

//---------------------------------------------------------

testCase testCaseFloat = {

    sizeof(correctBufferFloat)/sizeof(char*),

    TYPE_FLOAT,

    correctBufferFloat,

    printFloatGenParameters

};



//=========================================================

// octal

//=========================================================



//---------------------------------------------------------

// [string] format  | [string] octal-data representation  |

//---------------------------------------------------------

struct printDataGenParameters printOctalGenParameters[] = {

    //Default(right)-justified

    {"%o","10"},

    //Five-digit,default(right)-justified

    {"%.5o","10"},

    //Default(right)-justified,increase precision

    {"%#o","100000000"},

    //(Minimum)Four-wide,Five-digit,0-flag ignored(because of precision),default(right)-justified

    {"%04.5o","10"}

};

//-------------------------------------------------------

// Lookup table - [string] octal-correct buffer            |

//-------------------------------------------------------



const char* correctBufferOctal[] = {

    "12",

    "00012",

    "0575360400",

    "00012"

};

//-------------------------------------------------------

//Test case for octal                                   |

//-------------------------------------------------------

testCase testCaseOctal = {

    sizeof(correctBufferOctal)/sizeof(char*),

    TYPE_OCTAL,

    correctBufferOctal,

    printOctalGenParameters

};



//=========================================================

// unsigned

//=========================================================



//---------------------------------------------------------

// [string] format  | [string] unsined-data representation  |

//---------------------------------------------------------

struct printDataGenParameters printUnsignedGenParameters[] = {

    //Default(right)-justified

    {"%u","10"},

    //Zero precision for zero,default(right)-justified

    {"%.0u","0"},

};

//-------------------------------------------------------

// Lookup table - [string] octal-correct buffer            |

//-------------------------------------------------------



const char* correctBufferUnsigned[] = {

    "10",

    ""

};

//-------------------------------------------------------

//Test case for octal                                   |

//-------------------------------------------------------

testCase testCaseUnsigned = {

    sizeof(correctBufferUnsigned)/sizeof(char*),

    TYPE_UNSIGNED,

    correctBufferUnsigned,

    printUnsignedGenParameters

};



//=======================================================

// hexadecimal

//=======================================================



//--------------------------------------------------------------

// [string] format  | [string] hexadecimal-data representation |

//--------------------------------------------------------------

struct printDataGenParameters printHexadecimalGenParameters[] = {

    //Add 0x,low x,default(right)-justified

    {"%#x","0xABCDEF"},

    //Add 0x,capital X,default(right)-justified

    {"%#X","0xABCDEF"},

    //Not add 0x,if zero,default(right)-justified

    {"%#X","0"},

    //(Minimum)Eight-wide,default(right)-justified

    {"%8x","399"},

    //(Minimum)Four-wide,zero-filled,default(right)-justified

    {"%04x","399"}

};

//--------------------------------------------------------------

// Lookup table - [string]hexadecimal-correct buffer           |

//--------------------------------------------------------------



const char* correctBufferHexadecimal[] = {

    "0xabcdef",

    "0XABCDEF",

    "0",

    "     18f",

    "018f"

};

//--------------------------------------------------------------

//Test case for hexadecimal                                    |

//--------------------------------------------------------------

testCase testCaseHexadecimal = {

    sizeof(correctBufferHexadecimal)/sizeof(char*),

    TYPE_HEXADEC,

    correctBufferHexadecimal,

    printHexadecimalGenParameters

};



//=============================================================

// char

//=============================================================



//-----------------------------------------------------------

// [string] format  | [string] string-data representation   |

//-----------------------------------------------------------

struct printDataGenParameters printCharGenParameters[] = {

    //Four-wide,zero-filled,default(right)-justified

    {"%4c","\'1\'"},

        //Four-wide,left-justified

    {"%-4c","\'1\'"},

        //(unsigned) int argument,default(right)-justified

    {"%c","66"}

};

//---------------------------------------------------------

// Lookup table -[string] char-correct buffer             |

//---------------------------------------------------------

const char * correctBufferChar[] = {

    "   1",

    "1   ",

    "B",

};



//----------------------------------------------------------

//Test case for char                                       |

//----------------------------------------------------------

testCase testCaseChar = {

    sizeof(correctBufferChar)/sizeof(char*),

    TYPE_CHAR,

    correctBufferChar,

    printCharGenParameters

};



//==========================================================

// string

//==========================================================



//--------------------------------------------------------

// [string]format | [string] string-data representation  |

//--------------------------------------------------------

struct printDataGenParameters printStringGenParameters[] = {

    //(Minimum)Four-wide,zero-filled,default(right)-justified

    {"%4s","\"foo\""},

    //One-digit(precision ignored),left-justified

    {"%.1s","\"foo\""},

    //%% specification

    {"%s","\"%%\""},
};

//---------------------------------------------------------

// Lookup table -[string] string-correct buffer           |

//---------------------------------------------------------

const char * correctBufferString[] = {

    " foo",

    "f",

    "%%",
};

//---------------------------------------------------------

//Test case for string                                    |

//---------------------------------------------------------

testCase testCaseString = {

    sizeof(correctBufferString)/sizeof(char*),

    TYPE_STRING,

    correctBufferString,

    printStringGenParameters

};



//=========================================================

// vector

//=========================================================



//-------------------------------------------------------------------------------------------------------------------

//[string] flag | [string] specifier | [string] type | [string] vector-data representation | [string] vector size   |

//-------------------------------------------------------------------------------------------------------------------

struct printDataGenParameters printVectorGenParameters[]={

    //(Minimum)Two-wide,two positions after decimal

    {NULL,"(1.0f,2.0f,3.0f,4.0f)","%2.2","hlf","float","4"},

    //Alternative form,uchar argument

    {NULL,"(0xFA,0xFB)","%#","hhx","uchar","2"},

    //Alternative form,ushort argument

    {NULL,"(0x1234,0x8765)","%#","hx","ushort","2"},

  //Alternative form,uint argument

    {NULL,"(0x12345678,0x87654321)","%#","hlx","uint","2"},

    //Alternative form,long argument

    {NULL,"(12345678,98765432)","%","ld","long","2"}

};

//------------------------------------------------------------

// Lookup table -[string] vector-correct buffer              |

//------------------------------------------------------------

const char * correctBufferVector[] = {

    "1.00,2.00,3.00,4.00",

    "0xfa,0xfb",

    "0x1234,0x8765",

  "0x12345678,0x87654321",

    "12345678,98765432"

};

//-----------------------------------------------------------

//Test case for vector                                      |

//-----------------------------------------------------------

testCase testCaseVector = {

    sizeof(correctBufferVector)/(sizeof(char *)),

    TYPE_VECTOR,

    correctBufferVector,

    printVectorGenParameters

};



//==================================================================

// address space

//==================================================================



//-------------------------------------------------------------------------------------------------------------------------------------------------------------------

// [string] argument type qualifier |[string] variable type qualifier + initialization | [string] format | [string] parameter |[string]%p indicator/additional code |

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------



struct printDataGenParameters printAddrSpaceGenParameters[]={

    //Global memory region

    {"\"%d\\n\"",NULL,NULL,NULL,NULL,NULL,"__global int* x","","*x",""},

    //Global,constant, memory region

    {"\"%d\\n\"",NULL,NULL,NULL,NULL,NULL,"constant int* x","","*x",""},

    //Local memory region

    {"\"%+d\\n\"",NULL,NULL,NULL,NULL,NULL,"","local int x;\n x= (int)3;\n","x",""},

    //Private memory region

    {"\"%i\\n\"",NULL,NULL,NULL,NULL,NULL,"","private int x;\n x = (int)-1;\n","x",""},

    //Address of void * from global memory region

    {"\"%p\\n\"",NULL,NULL,NULL,NULL,NULL,"__global void* x,__global intptr_t*  xAddr","","x","*xAddr = (intptr_t)x;\n"}

};

//-------------------------------------------------------------------------------

//  Lookup table -[string] address space -correct buffer                        |

//-------------------------------------------------------------------------------

const char * correctAddrSpace[] = {

    "2","2","+3","-1",""

};

//-------------------------------------------------------------------------------

//Test case for address space                                                   |

//-------------------------------------------------------------------------------

testCase testCaseAddrSpace = {

    sizeof(correctAddrSpace)/(sizeof(char *)),

    TYPE_ADDRESS_SPACE,

    correctAddrSpace,

    printAddrSpaceGenParameters

};



//-------------------------------------------------------------------------------

//All Test cases                                                                |

//-------------------------------------------------------------------------------

testCase* allTestCase[] = {&testCaseInt,&testCaseFloat,&testCaseOctal,&testCaseUnsigned,&testCaseHexadecimal,&testCaseChar,&testCaseString,&testCaseVector,&testCaseAddrSpace};


//-----------------------------------------

// Check functions

//-----------------------------------------

size_t verifyOutputBuffer(char *analysisBuffer,testCase* pTestCase,size_t testId,cl_ulong pAddr)
{
    int terminatePos = strlen(analysisBuffer);
        if(terminatePos > 0)
    {
        analysisBuffer[terminatePos - 1] = '\0';
    }

    //Convert analysis buffer to long for address space
    if(pTestCase->_type == TYPE_ADDRESS_SPACE && strcmp(pTestCase->_genParameters[testId].addrSpacePAdd,""))

    {
        char analysisBufferTmp[ANALYSIS_BUFFER_SIZE];

        if(strstr(analysisBuffer,"0x") == NULL)
        // Need to prepend 0x to ASCII number before calling strtol.
        strcpy(analysisBufferTmp,"0x");

        else analysisBufferTmp[0]='\0';
        strcat(analysisBufferTmp,analysisBuffer);
        if (sizeof(long) == 8) {
            if(strtoul(analysisBufferTmp,NULL,0) == pAddr) return 0;
        }
        else {
            if(strtoull(analysisBufferTmp,NULL,0) == pAddr) return 0;
        }
        return 1;

    }

    char* exp;
    //Exponenent representation
    if((exp = strstr(analysisBuffer,"E+")) != NULL || (exp = strstr(analysisBuffer,"e+")) != NULL || (exp = strstr(analysisBuffer,"E-")) != NULL || (exp = strstr(analysisBuffer,"e-")) != NULL)
    {
        char correctExp[3]={0};
        strncpy(correctExp,exp,2);


        char* eCorrectBuffer = strstr((char*)pTestCase->_correctBuffer[testId],correctExp);
        if(eCorrectBuffer == NULL)
            return 1;

        eCorrectBuffer+=2;
        exp += 2;

        //Exponent always contains at least two digits
        if(strlen(exp) < 2)
            return 1;
        //Scip leading zeros in the exponent
        while(*exp == '0')
            ++exp;
        return strcmp(eCorrectBuffer,exp);
    }
    if(!strcmp(pTestCase->_correctBuffer[testId],"inf"))
    return strcmp(analysisBuffer,"inf")&&strcmp(analysisBuffer,"infinity")&&strcmp(analysisBuffer,"1.#INF00")&&strcmp(analysisBuffer,"Inf");
    if(!strcmp(pTestCase->_correctBuffer[testId],"nan") || !strcmp(pTestCase->_correctBuffer[testId],"-nan")) {
       return strcmp(analysisBuffer,"nan")&&strcmp(analysisBuffer,"-nan")&&strcmp(analysisBuffer,"1.#IND00")&&strcmp(analysisBuffer,"-1.#IND00")&&strcmp(analysisBuffer,"NaN")&&strcmp(analysisBuffer,"nan(ind)")&&strcmp(analysisBuffer,"nan(snan)")&&strcmp(analysisBuffer,"-nan(ind)");
    }
    return strcmp(analysisBuffer,pTestCase->_correctBuffer[testId]);
}

