/*
 * oclh_h_externals.c
 *      Author: havock
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <oclh_hd_std_types.clh>
#include <oclh_h_settings.h>
#include <oclh_h_externals.h>
#include <oclh_h_errors.h>

void* _ghf_wdcAllocHostBuf(_GHT_WRKSET wSet, const size_t sz) {
    void* res=malloc(sz);
    if(!res) {
        if(wSet.pvDat) wSet.pfnDatCleaner(wSet);
        return(NULL);
    }
    return(res);
}

void _ghf_freeHostZ(void* const ppPtr) {
/* It looks like insanity but it is necessary. The problem is if you copy mem
 * from GPU to host with clEnqueueReadBuffer() you get unobvious mapping between
 * hostPointer and gpuPointer as a result freeing hostPointer without explicit
 * zeroing it and following clReleaseMemObject() on GpuPointer causes errors
 * like a corrupted linked-list or segmentation fault */
    void* pRealPtr=(*((void**) ppPtr));
    if(pRealPtr) { free(pRealPtr); *((void**)ppPtr)=NULL; }
    return;
}

cl_mem _ghf_wdcAllocDevBuf(_GHT_WRKSET  wSet,
                     const cl_mem_flags cmFlags,
                     const size_t       sz,
                           void*  const pvHostPtr) {
    size_t szAct=0ul;
    cl_mem res=clCreateBuffer(_ghf_getWS_Ctx(wSet), cmFlags, sz,
                              pvHostPtr, &wSet.APIErr);
    char pcLogMsg[_GHM_MAXLEN_OF_LOGMSG];
    snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG, "%s/clCreateBuffer", __func__);
    if(_ghf_wdcChkWS_APIErr(wSet, pcLogMsg, _GHM_FL)) return(NULL);
    wSet.APIErr=clGetMemObjectInfo(res, CL_MEM_SIZE,
                                   sizeof(size_t), &szAct, NULL);
    snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG,
             "%s/clGetMemObjectInfo/CL_MEM_SIZE", __func__);
    _ghf_wdcChkWS_APIErr(wSet, pcLogMsg, _GHM_FL);
    if(wSet.APIErr) res=NULL;
    else (*wSet.pszMemBytes)+=szAct;
    return(res);
}

void _ghf_freeDevZ(_GHT_WRKSET wSet, cl_mem* const pCLMem) {
/* the current wrapper have made because of clReleaseMemObject() in contrast to
 * free() did not check the NULL pointer and what was more important it was
 * comme il faut to set a freed pointer to NULL */
    wSet.pvDat=NULL;
    if(*pCLMem) {
        size_t szAct=0ul;
        char pcLogMsg[_GHM_MAXLEN_OF_LOGMSG];
        snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG,
                 "%s/clGetMemObjectInfo/CL_MEM_SIZE", __func__);
        wSet.APIErr=clGetMemObjectInfo(*pCLMem, CL_MEM_SIZE,
                                       sizeof(size_t), &szAct,NULL);
        _ghf_wdcChkWS_APIErr(wSet,pcLogMsg, _GHM_FL);
        wSet.APIErr=clReleaseMemObject(*pCLMem);
        snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG,
                 "%s/clReleaseMemObject", __func__);
        if(!_ghf_wdcChkWS_APIErr(wSet, pcLogMsg, _GHM_FL)) {
            *pCLMem=NULL; (*wSet.pszMemBytes)-=szAct;
        }
    }
    return;
}

int32_t _ghf_addCharPtrToCharPtrList(char*** const pppcLst,
                                     const char* const pcStr) {
    if(pppcLst && pcStr) {
        char** ppcTmp=NULL;
        uint64_t i=0, u64StrLen=strlen(pcStr);
        if(*pppcLst) while((*pppcLst)[i]) i++;
        else {
            *pppcLst=malloc(sizeof(char*));
            if(!*pppcLst) return(_GHM_HOST_MEMALLOC_ERROR);
            (*pppcLst)[0]=NULL;
        }
        ppcTmp=realloc(*pppcLst,sizeof(char*)*(i+2ul));
        if(!ppcTmp) return(_GHM_HOST_MEMALLOC_ERROR);
        *pppcLst=ppcTmp;
        (*pppcLst)[i]=NULL;
        (*pppcLst)[i]=malloc(sizeof(char)*(u64StrLen+1));
        if(!(*pppcLst)[i]) return(_GHM_HOST_MEMALLOC_ERROR);
        strncpy((*pppcLst)[i],pcStr,u64StrLen);
        (*pppcLst)[i+1]=NULL;
        return(_GHM_OK);
    } else return(_GHM_NULL_POINTER_RECEIVED_ERROR);
}

int32_t _ghf_addFileToCharPtrListWithSizes(      char*** const pppcLst,
                                           const char*   const pcFileName,
                                           const uint64_t      u64Align) {
/**/
#define __ALIGNED_SIZE \
    (((szFileLen+1)%u64Align)? \
        szFileLen+(u64Align-szFileLen%u64Align):szFileLen+1)
/**/
    if(pppcLst && pcFileName) {
        FILE* pFile=NULL;
        size_t szFileLen=0ul;
        char* pcSource=NULL;
        pFile=fopen(pcFileName, "rb");
        if(!pFile) {
            fprintf(stderr, "Unable to open the source file %s\n", pcFileName);
            perror("");
            return(_GHM_OPEN_FILE_ERROR);
        }
        if(fseek(pFile,0L,SEEK_END)) {
            fprintf(stderr,"Unable to seek the source file %s\n",pcFileName);
            perror("");
            fclose(pFile);
            return(_GHM_SEEK_FILE_ERROR);
        }
        szFileLen=(size_t)ftell(pFile);
        if(fseek(pFile,0L,SEEK_SET)) {
            fprintf(stderr,"Unable to seek the source file %s\n",pcFileName);
            perror("");
            fclose(pFile);
            return(_GHM_SEEK_FILE_ERROR);
        }
        pcSource=malloc(__ALIGNED_SIZE);
        if(!pcSource) {
            fprintf(stderr,"Unable to allocate host memory\n");
            perror("");
            fclose(pFile);
            return(_GHM_HOST_MEMALLOC_ERROR);
        }
        { /* тонкий момент: выделено памяти на один байт больше, чем размер
           * файла, чтобы установить в него нуль-терминатор, если программа
           * представлена в виде исходного кода. Если программа представлена в
           * бинарном виде, то её чтение ограничивается длиной и нуль-терминатор
           * игнорируется. */
            uint64_t i=0ul;
            for(i=__ALIGNED_SIZE-1; i>0 && i>__ALIGNED_SIZE-17ul; i--)
                pcSource[i]='\0';
            pcSource[0]='\0';
        }
        if(fread(pcSource,szFileLen,1,pFile)!=1) {
            fprintf(stderr,"Unable to read the source file %s\n",pcFileName);
            perror("");
            _ghf_freeHostZ(&pcSource);
            fclose(pFile);
            return(_GHM_READ_FILE_ERROR);
        }
        fclose(pFile);
        {
            char** ppcTmp=NULL;
            uint64_t i=0ul;
            if(*pppcLst) while((*pppcLst)[i]) i++;
            if(!i) {
                *pppcLst=malloc(sizeof(char*)*2ul);
                if(!*pppcLst) {
                    fprintf(stderr,"Unable to allocate host memory\n");
                    perror("");
                    _ghf_freeHostZ(pcSource);
                    return(_GHM_HOST_MEMALLOC_ERROR);
                }
                (*pppcLst)[0]=NULL;
                (*pppcLst)[1]=NULL;
            }
            ppcTmp=realloc(*pppcLst,sizeof(char*)*(i+3ul));
            if(!ppcTmp) {
                fprintf(stderr,"Unable to allocate host memory\n");
                perror("");
                _ghf_freeHostZ(pcSource);
                return(_GHM_HOST_MEMALLOC_ERROR);
            }
            *pppcLst=ppcTmp;
            (*pppcLst)[i]=pcSource;
            (*pppcLst)[i+2]=(*pppcLst)[i+1];
            (*pppcLst)[i+1]=NULL;
            {
                size_t* pszTmp=NULL;
                pszTmp=realloc((*pppcLst)[i+2],sizeof(uint64_t)*(i+1));
                if(!pszTmp) {
                    fprintf(stderr,"Unable to allocate host memory\n");
                    perror("");
                    return(_GHM_HOST_MEMALLOC_ERROR);
                }
                pszTmp[i]=szFileLen;
                (*pppcLst)[i+2]=(char*)pszTmp;
            }
        }
        return(_GHM_OK);
    } else return(_GHM_NULL_POINTER_RECEIVED_ERROR);
/**/
#undef __ALIGNED_SIZE
/**/
}

void _ghf_wipeCharPtrList(char*** const pppcLst) {
    if(pppcLst && *pppcLst) {
        uint64_t i=0ul;
        for(i=0ul; (*pppcLst)[i]; i++) _ghf_freeHostZ(&(*pppcLst)[i]);
    }
    _ghf_freeHostZ(pppcLst);
    return;
}

void _ghf_wipeCharPtrListWithSizes(char*** const pppcLst) {
    uint64_t i=0ul;
    if(pppcLst && *pppcLst) {
        for(i=0ul; (*pppcLst)[i]; i++)
            _ghf_freeHostZ(&(*pppcLst)[i]);
        _ghf_freeHostZ(&(*pppcLst)[i+1]);
    }
    _ghf_freeHostZ(pppcLst);
    return;
}
