#include "Common.h"
#include <stdarg.h>

#define MODULE_NAME "AVM_Common"
#define AVM_DEBUG_SWITCH    1
#include "Avm_log.h"

#define HDSIZE_MAX    16
static GLuint esAttrHD[HDSIZE_MAX] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

#define TEX_BUF_SIZE    1024*1024*4         //用来临时存放图片数据的buffer大小
static unsigned char *texDataBuf = NULL;    //指向用来临时存放图片数据的buffer


void Init_OES_State()
{
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
}

void esLoadShader(const char **verSrc, const char **frgSrc, GLuint *verShader, GLuint *frgShader)
{
    GLuint tmpVerShader = 0;
    GLuint tmpFrgShader = 0;

    tmpVerShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(tmpVerShader, 1, verSrc, NULL);
    glCompileShader(tmpVerShader);
    *verShader = tmpVerShader;

    tmpFrgShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(tmpFrgShader, 1, frgSrc, NULL);
    glCompileShader(tmpFrgShader);
    *frgShader = tmpFrgShader;
}

GLuint oesLoadShader ( GLenum type, const char *shaderSrc )
{
   GLuint shader;
   GLint compiled;

   // Create the shader object
   shader = glCreateShader ( type );

   if ( shader == 0 )
   	return 0;

   // Load the shader source
   glShaderSource ( shader, 1, &shaderSrc, NULL );

   // Compile the shader
   glCompileShader ( shader );

   // Check the compile status
   glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

   if ( !compiled )
   {
      GLint infoLen = 0;

      glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

      if ( infoLen > 1 )
      {
         char* infoLog = (char *)malloc (sizeof(char) * infoLen );

         glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
         AVM_ERROR ( "Error compiling shader:\n%s\n", infoLog );

         free ( infoLog );
      }

      glDeleteShader ( shader );
      return 0;
   }

   return shader;

}
GLuint esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc )
{
   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;

   // Load the vertex/fragment shaders
   AVM_DEBUG("loadshader: vertex\n");
   vertexShader = oesLoadShader ( GL_VERTEX_SHADER, vertShaderSrc );
   if ( vertexShader == 0 )
      return 0;
   AVM_DEBUG("loadshader: fragment\n");

   fragmentShader = oesLoadShader ( GL_FRAGMENT_SHADER, fragShaderSrc );
   if ( fragmentShader == 0 )
   {
      glDeleteShader( vertexShader );
      return 0;
   }

   // Create the program object
   programObject = glCreateProgram ( );

   if ( programObject == 0 )
      return 0;

   glAttachShader ( programObject, vertexShader );
   glAttachShader ( programObject, fragmentShader );

   // Link the program
   glLinkProgram ( programObject );

   // Check the link status
   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

   if ( !linked )
   {
      GLint infoLen = 0;

      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

      if ( infoLen > 1 )
      {
         char* infoLog = (char *)malloc (sizeof(char) * infoLen );

         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
         AVM_ERROR ( "Error linking program:\n%s\n", infoLog );

         free ( infoLog );
      }

      glDeleteProgram ( programObject );
      return 0;
   }

   // Free up no longer needed shader resources
   glDeleteShader ( vertexShader );
   glDeleteShader ( fragmentShader );

   return programObject;
}

//to get a value between 0-15 for attribHandle
int esApplyAttribHandle(int num, GLuint *attribHandle)
{
    int idx_getHD = 0;
    int idx_libHD = 0;
    int ret = 0;

    do
    {
        if (idx_getHD == num)
        {
            break;
        }
        if (idx_libHD == HDSIZE_MAX)
        {
            AVM_ERROR("there is not enough handles\n");    //剩余的可用数值个数不满足需求
            break;
        }
        if (esAttrHD[idx_libHD] != 100)
        {
            attribHandle[idx_getHD] = esAttrHD[idx_libHD];
            esAttrHD[idx_libHD] = 100;
            idx_getHD++;
        }
        idx_libHD++;

        ret++;
    }while(1);

    return ret;
}


int esDelAttribHandle(int num, GLuint *attribHandle)
{
    int idx_delHD = 0;
    int idx_libHD = 0;
    int ret = 0;

    do
    {
        if (idx_delHD == num)
        {
            break;
        }
        if (idx_libHD == HDSIZE_MAX)
        {
            AVM_ERROR("failed to delete %d handles\n", num);    //句柄数组剩余空间不足以释放所有句柄
            break;
        }
        if (esAttrHD[idx_libHD] == 100)
        {
            esAttrHD[idx_libHD] = attribHandle[idx_delHD];
            attribHandle[idx_delHD] = 100;
            idx_delHD++;
        }
        idx_libHD++;

        ret++;
    } while(1);

    return ret;
}


/*
*bufsize: 如果确定要读取的数据大小，则 > 0
*         无法确定要读取的数据大小，则 = 0
*return: 0: 读取文件失败
*       >0: 读取成功，返回读取到的数据大小
*/
UInt fReadFileData(char *filePath, UChar **fileData, int bufsize)
{
    FILE *pFile = NULL;
    UChar *tmpData = NULL;
    UInt filesize = 0;
    UInt datasize = 0;
    UInt readsize = 0;
    UInt ret = 0;

    do
    {
        pFile = fopen(filePath, "rb");
        if (NULL == pFile)
        {
            AVM_ERROR("fopen %s failed!\n", filePath);
            break;
        }

        /*测量文件大小*/
        fseek(pFile, 0, SEEK_END);
        filesize = ftell(pFile);
        AVM_DEBUG("datasize of %s: %d\n", filePath, filesize);
        fseek(pFile, 0, SEEK_SET);

        /*当dataBuffer不为空时，则不需要另外申请空间*/
        if (NULL != *fileData)
        {
            AVM_DEBUG("%s,  *fileData != NULL, bufsize = %d\n", filePath, bufsize);
            tmpData = *fileData;

            /*当bufsize <= 0时，默认读取文件实际大小；当bufsize > 0时则要检查文件大小是否匹配*/
            if ((0 < bufsize) && (bufsize != filesize))
            {
                AVM_ERROR("filesize dosen't match bufsize %s : bufsize = %d, filesize = %d\n", filePath, bufsize, filesize);
                break;
            }
        }
        else
        /*当dataBuffer为空指针时，则申请与文件实际大小相等的空间，并将dataBuffer指向这段空间的首地址*/
        {
            AVM_DEBUG("%s, fileData == NULL\n", filePath);
            tmpData = (UChar *)malloc(filesize);
            if (NULL == tmpData)
            {
                AVM_ERROR("fileData is NULL, malloc failed!\n");
                break;
            }
            *fileData = tmpData;
        }

        readsize = fread((void*)tmpData, 1, filesize, pFile);
        if (readsize != filesize)
        {
            /*根据上面做的逻辑，只有当bufer大小不足filesize时才会走到这一步*/
            AVM_ERROR("fread %s failed: the buffer may not have size(%d) space. want = %d, get = %d\n", filePath, bufsize, filesize, readsize);
            /*认为当前读取到的数据无效，清零，函数返回0--error*/
            memset(tmpData, 0, sizeof(readsize));
            break;
        }
        ret = readsize;
    } while(0);

    if (NULL != pFile)
    {
        fclose(pFile);
        pFile = NULL;
    }

    return ret;
}


/*
*return: 0: read failed
*       >0: readsize
*/
int LoadFile(char *dirpath, const char *fileName, UChar **DataBuf, int bufsize)
{
    int ret = 0;
    char *tmppath = NULL;

    tmppath = (char*)malloc(100);
    strcpy(tmppath, dirpath);
    strcat(tmppath, fileName);

    ret = fReadFileData(tmppath, DataBuf, bufsize);
    if (ret == 0)
    {
        AVM_ERROR("freadFileData readed 0 words\n");
    }
    free(tmppath);
    tmppath = NULL;

    return ret;
}


/*load texture data to GPU*/
bool loadTexture(GLuint *idTextures, void *picData, unsigned int picWidth, unsigned int picHeight, UChar isAlpha)
{
    bool ret = FALSE;

    do
    {
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        if (*idTextures == 0)
        {
            glGenTextures(1, idTextures);
        }

        glBindTexture(GL_TEXTURE_2D, *idTextures);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,      GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,      GL_CLAMP_TO_EDGE);
#ifdef MIPMAP
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
#else
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
#endif
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        if (1==isAlpha)
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, picData);
        }
        else
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, picWidth, picHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, picData);
        }

#ifdef MIPMAP
        glGenerateMipmap(GL_TEXTURE_2D);
#endif
        glBindTexture(GL_TEXTURE_2D, 0);

		ret = AVM_GL_CHECK("loadTexture");
    } while(0);

    return ret;
}

void tmpTexBufMalloc()
{
    if (NULL == texDataBuf)
    {
        texDataBuf = (unsigned char *)malloc(TEX_BUF_SIZE);
    }
}

void tmpTexBufFree()
{
    free(texDataBuf);
    texDataBuf = NULL;
}

/*
*加载图片到GPU的纹理单元
*return: 成功加载图片的个数
*/
int loadPicFile(const char *picFilePath[], int picCount, GLuint *texPic)
{
    int ret = 0;
    int k = 0;
    FILE *pFile = NULL;
    unsigned char *tmpdata = NULL;
    int datasize = 0;
    int readsize = 0;
    unsigned int picWidth, picHeight, isAlpha;

    glGenTextures(picCount, texPic);
    for (k=0; k<picCount; k++)
    {
        do
        {
            if (0 == fReadFileData((char*)picFilePath[k], &texDataBuf, 0))
            {
                AVM_ERROR("read %s failed\n", picFilePath[k]);
                break;
            }

            picWidth  = *((unsigned int *)texDataBuf);
            picHeight = *((unsigned int *)(texDataBuf+4));
            isAlpha   = *((unsigned int *)(texDataBuf+8));        /*isAlpha: 0---GL_RGB, 1---GL_RGBA*/
            tmpdata   = texDataBuf+12;
            loadTexture((texPic+k), tmpdata, picWidth, picHeight, isAlpha);
            ret ++;
        } while(0);

        if (NULL != pFile)
        {
            fclose(pFile);
            pFile = NULL;
        }
        memset(texDataBuf, 0, TEX_BUF_SIZE);
    }

    return ret;
}


int WriteDatatoFile(const char *filename, void *data, int datasize)
{
    int ret = 0;
    int writesize = 0;
    FILE *genLutFp = NULL;

    do
    {
        genLutFp = fopen(filename, "wb");
        if (genLutFp == NULL)
        {
            AVM_ERROR("fopen file :%s failed!!!\n", filename);
            ret = -1;
            break;
        }

        writesize = fwrite(data, 1U, datasize, genLutFp);
        if (writesize != datasize)
        {
            AVM_ERROR("fwrite file :%s failed, wantwrite: %d, realwrite: %d!!!\n", filename, datasize, writesize);
            ret = -1;
            break;
        }

        ret  = 0;
    } while(0);

    if (NULL != genLutFp)
    {
        fclose(genLutFp);
        genLutFp = NULL;
    }
    system("sync");

    return ret;
}

/*
*numCoord:length of screenCoord array
*/
void ScreenCoordConvert(int screenWidth, int screenHeight, int *screenCoord, float *openglCoord, int numCoord)
{
    int i = 0;

    for (i=0; i<numCoord; i+=2)
    {
        if ((screenCoord[i] >= 0) && (screenCoord[i] < screenWidth))
        {
            openglCoord[i]   = (float)2*screenCoord[i]/(screenWidth-1) - 1.0;
        }
        else
        {
            AVM_ERROR("screenCoord[%d]= %d out of range\n", i, screenCoord[i]);
        }

        if ((screenCoord[i+1] >= 0) && (screenCoord[i+1] < screenHeight))
        {
            openglCoord[i+1] = 1.0 - (float)2*screenCoord[i+1]/(screenHeight-1);
        }
        else
        {
            AVM_ERROR("screenCoord[%d]= %d out of range\n", i+1, screenCoord[i+1]);
        }
    }
}

bool updateLuts()
{
    bool ret = FALSE;

    do
    {
        AVM_DEBUG("update luts to Mem\n");
        AVM_DEBUG("update luts to File\n");
        ret = TRUE;
    } while(0);

    return ret;
}


bool CheckGlError(const char* op, ...)
{
    bool ret = FALSE;
    GLint error;
    char gl_log[1024] = {0};
    int log_size = 0;
    UInt retCount = 0;
    va_list ap;

    do
    {
        for (error = glGetError(); error; error = glGetError()) {
            va_start(ap, op);
            log_size = vsprintf(gl_log, op, ap);
            va_end(ap);
            gl_log[log_size-1] = '\0';

//            printf(RED("%s, glError(0x%x)\n"), gl_log, error);
            AVM_ERROR("%s, glError(0x%x)\n",  gl_log, error);

            retCount++;
        }
        ret = (0 == retCount) ? TRUE : FALSE;
    } while(0);

    return ret;
}

