/*
 * oclh_h_srr.c
 *      Author: havock
 */
#include <stdio.h>
#include <oclh_h_settings.h>
#include <oclh_h_externals.h>
#include <oclh_h_srr.h>

#define _OCLH_OCL_HOST_ALGORITHMS_ 1
#include <oclh_hd_srr.clh>
#undef _OCLH_OCL_HOST_ALGORITHMS_

_GHM_BASETYPE_HD_IMPLEMENTATION(i8)
_GHM_BASETYPE_HD_IMPLEMENTATION(u8)
_GHM_BASETYPE_HD_IMPLEMENTATION(i16)
_GHM_BASETYPE_HD_IMPLEMENTATION(u16)
_GHM_BASETYPE_HD_IMPLEMENTATION(i32)
_GHM_BASETYPE_HD_IMPLEMENTATION(u32)
_GHM_BASETYPE_HD_IMPLEMENTATION(i64)
_GHM_BASETYPE_HD_IMPLEMENTATION(u64)
_GHM_BASETYPE_HD_IMPLEMENTATION(f32)
_GHM_BASETYPE_HD_IMPLEMENTATION(f64)

/*
 * host-device array of unsigned 8-bit values
 * can be used as any data, considering byte alignment
 */
_GHT_HDvoid _ghf_declHD_void(void) {
    const _GHT_HDvoid hdv={ .hPtr=NULL, .dPtr=NULL, .u64NofBytes=0ul };
    return(hdv);
}

int32_t _ghf_wdcAllocHD_void(_GHT_WRKSET wSet,_GHT_HDvoid* const pHdv,
                             const uint64_t u64NofBytes) {
    if(!pHdv) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    _ghf_wipeHD_void(wSet,pHdv);
    pHdv->hPtr=_ghf_wdcAllocHostBuf(wSet, u64NofBytes);
    if(!pHdv->hPtr) {
        _ghf_wipeHD_void(wSet, pHdv); return(_GHM_HOST_MEMALLOC_ERROR);
    }
    pHdv->dPtr=_ghf_wdcAllocDevBuf(wSet,CL_MEM_READ_WRITE,u64NofBytes,NULL);
    if(!pHdv->dPtr) {
        _ghf_wipeHD_void(wSet,pHdv); return(_GHM_DEVICE_MEMALLOC_ERROR);
    }
    pHdv->u64NofBytes=u64NofBytes;
    return(_GHM_OK);
}

int32_t _ghf_wdcReAllocHD_void(_GHT_WRKSET wSet, const _GHE_SYNC_TYPE SyncType,
                               _GHT_HDvoid* const pHdv,
                               const uint64_t u64NofBytes) {
    uint8_t* hTmpPtr=NULL;
    if(!pHdv) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    hTmpPtr=realloc(pHdv->hPtr, u64NofBytes);
    if(!hTmpPtr) {
        if(wSet.pvDat) wSet.pfnDatCleaner(wSet);
        _ghf_wipeHD_void(wSet,pHdv); return(_GHM_HOST_MEMALLOC_ERROR);
    }
    pHdv->hPtr=hTmpPtr;
    _ghf_freeDevZ(wSet, &pHdv->dPtr);
    pHdv->dPtr=_ghf_wdcAllocDevBuf(wSet, CL_MEM_READ_WRITE, u64NofBytes, NULL);
    if(!pHdv->dPtr) {
        _ghf_wipeHD_void(wSet,pHdv); return(_GHM_DEVICE_MEMALLOC_ERROR);
    }
    pHdv->u64NofBytes=u64NofBytes;
    { int32_t err=0; 
      if((err=_ghf_wdcSyncHD_void(wSet,SyncType,pHdv))) return(err); }
    return(_GHM_OK);
}

int32_t _ghf_wdcSyncHD_void(_GHT_WRKSET wSet, const _GHE_SYNC_TYPE SyncType,
                            _GHT_HDvoid* const pHdv) {
    int32_t err=0;
    if(!pHdv) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    switch(SyncType) {
        case _GHE_SYNC_HOST_TO_DEV:
            {
                char pcLogMsg[_GHM_MAXLEN_OF_LOGMSG];
                snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG,
                         "%s/clEnqueueWriteBuffer", __func__);
                wSet.APIErr=clEnqueueWriteBuffer(wSet.Queue, pHdv->dPtr,
                                                 CL_TRUE, 0ul,
                                                 pHdv->u64NofBytes,
                                                 pHdv->hPtr, 0u, NULL, NULL);
                if((err=_ghf_wdcChkWS_APIErr(wSet,pcLogMsg,_GHM_FL))) {
                    _ghf_wipeHD_void(wSet, pHdv); return(err);
                }
            }
            break;
        case _GHE_SYNC_DEV_TO_HOST:
            {
                char pcLogMsg[_GHM_MAXLEN_OF_LOGMSG];
                snprintf(pcLogMsg, _GHM_MAXLEN_OF_LOGMSG,
                         "%s/clEnqueueReadBuffer", __func__);
                wSet.APIErr=clEnqueueReadBuffer(wSet.Queue, pHdv->dPtr, CL_TRUE,
                                                0ul, pHdv->u64NofBytes,
                                                pHdv->hPtr, 0u, NULL, NULL);
                if((err=_ghf_wdcChkWS_APIErr(wSet,pcLogMsg,_GHM_FL))) {
                    _ghf_wipeHD_void(wSet, pHdv); return(err);
                }
            }
            break;
        default:
            return(_GHM_UNKNOWN_SYNC_TYPE_ERROR);
            break;
    }
    return(_GHM_OK);
}

int32_t _ghf_wdcFillHD_void(_GHT_WRKSET wSet, _GHT_HDvoid* const pHdv,
                            const uint8_t u8Val) {
    int32_t err=0;
    uint64_t i=0ul;
    if(!pHdv) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    for(i=0ul; i<pHdv->u64NofBytes; i++) pHdv->hPtr[i]=u8Val;
    if((err=_ghf_wdcSyncHD_void(wSet,_GHE_SYNC_HOST_TO_DEV,pHdv))) return(err);
    return(_GHM_OK);
}

int32_t _ghf_wipeHD_void(_GHT_WRKSET wSet,_GHT_HDvoid* const pHdv) {
    if(pHdv) {
        _ghf_freeDevZ(wSet, &pHdv->dPtr);
        _ghf_freeHostZ(&pHdv->hPtr);
        pHdv->u64NofBytes=0ul;
    }
    return(_GHM_OK);
}


/*
 * host-device array of float 32-bit vectors
 */
_GHT_HDVECS_F32 _ghf_declHDV_f32(void) {
    const _GHT_HDVECS_F32 pVhdf32={
            .hdf32V=_ghf_declHD_f32(),
            .u64Dim=0ul, .u64SemDim=0ul
    };
    return(pVhdf32);
}

int32_t _ghf_wdcAllocHDV_f32(_GHT_WRKSET wSet, _GHT_HDVECS_F32* const pVhdf32,
                             const uint64_t u64Dim, const uint64_t u64SemDim,
                             const uint64_t u64NofVecs) {
    int32_t err=0;
    if(!pVhdf32) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    _ghf_wipeHDV_f32(wSet,pVhdf32);
    if((u64Dim<u64SemDim) || (u64Dim%u64SemDim)) return(4); /* TODO: make error codes */
    if((err=_ghf_wdcAllocHD_f32(wSet, &pVhdf32->hdf32V, u64Dim*u64NofVecs))) {
        _ghf_wipeHDV_f32(wSet,pVhdf32); return(err);
    }
    pVhdf32->u64Dim=u64Dim;
    pVhdf32->u64SemDim=u64SemDim;
    return(_GHM_OK);
}

int32_t _ghf_wdcSyncHDV_f32(_GHT_WRKSET wSet, const _GHE_SYNC_TYPE SyncType,
                            _GHT_HDVECS_F32* const pVhdf32) {
    int32_t err=0;
    if(!pVhdf32) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    if((err=_ghf_wdcSyncHD_f32(wSet,SyncType,&pVhdf32->hdf32V))) return(err); /* TODO: make error codes */
    return(_GHM_OK);
}

int32_t _ghf_wdcFillHDV_f32(_GHT_WRKSET wSet, _GHT_HDVECS_F32* const pVhdf32,
                            const flt32_t f32Val) {
    int32_t err=0;
    uint64_t i=0ul;
    if(!pVhdf32) return(_GHM_NULL_POINTER_RECEIVED_ERROR);
    for(i=0ul; i<pVhdf32->hdf32V.u64NofVals; i++)
        pVhdf32->hdf32V.hPtr[i]=f32Val;
    if((err=_ghf_wdcSyncHDV_f32(wSet,_GHE_SYNC_HOST_TO_DEV,pVhdf32)))
        return(err);
    return(_GHM_OK);
}

int32_t _ghf_isHDV_f32_Valid(const _GHT_HDVECS_F32 Vhdf32) {
    if(Vhdf32.u64Dim &&
       Vhdf32.u64SemDim && 
       _ghf_isHD_f32_Valid(Vhdf32.hdf32V)) {
        if((Vhdf32.u64Dim < Vhdf32.u64SemDim ) ||
           (Vhdf32.u64Dim % Vhdf32.u64SemDim ) ||
           (Vhdf32.hdf32V.u64NofVals % Vhdf32.u64Dim )) return(0);
        return(1);
    }
    return(0); /* Внимание! 0 -- значит набор векторов невалиден */
}

uint64_t _ghf_getNofVecsHDV_f32(const _GHT_HDVECS_F32 Vhdf32) {
    if(!_ghf_isHDV_f32_Valid(Vhdf32))
        return((uint64_t) -1);/* max unsigned long int */
    return(Vhdf32.hdf32V.u64NofVals/Vhdf32.u64Dim);
}

int32_t _ghf_saveHDV_f32_ToTxt(const _GHT_HDVECS_F32 vfVecs,
                               const char* const pcFileName,
                               const int32_t i32UseSemDimFlag) {
    if(vfVecs.u64Dim &&            vfVecs.u64SemDim &&
       vfVecs.hdf32V.u64NofVals && vfVecs.hdf32V.hPtr) {
        FILE* pFile=NULL;
        uint64_t i=0ul;
        const uint64_t u64Dim=(i32UseSemDimFlag)?vfVecs.u64SemDim:vfVecs.u64Dim,
                       u64NofV=vfVecs.hdf32V.u64NofVals/u64Dim;
        pFile=fopen(pcFileName,"w");
        if(!pFile) return(_GHM_OPEN_FILE_ERROR);
        fprintf(pFile,"seqn\t");
        for(i=0ul; i<u64Dim; i++) {
            if(i==(u64Dim-1)) fprintf(pFile,"c%lu\n",i);
            else fprintf(pFile,"c%lu\t",i);
        }
        for(i=0ul; i<u64NofV; i++) {
            uint64_t j=0ul;
            flt32_t* pf32CurVec=&vfVecs.hdf32V.hPtr[i*u64Dim];
            fprintf(pFile,"%f\t",(flt32_t) i);
            for(j=0ul; j<u64Dim; j++) {
                if(j==(u64Dim-1)) fprintf(pFile,"%f\n",pf32CurVec[j]);
                else fprintf(pFile,"%f\t",pf32CurVec[j]);
            }
        }
        fclose(pFile);
        return(_GHM_OK);
    }
    return(1); /* TODO: make error codes */
}
int32_t _ghf_saveHDV_f32_ToTxtByHD_u64_Idxs(const _GHT_HDVECS_F32 vfVecs,
                                            const _GHT_HDu64 hdu64IV,
                                            const char* const pcFileName,
                                            const int32_t i32UseSemDimFlag) {
    if(vfVecs.u64Dim &&            vfVecs.u64SemDim &&
       vfVecs.hdf32V.u64NofVals && vfVecs.hdf32V.hPtr &&
       hdu64IV.u64NofVals &&       hdu64IV.hPtr) {
        const uint64_t u64Dim=(i32UseSemDimFlag)?vfVecs.u64SemDim:vfVecs.u64Dim;
        FILE* pFile=NULL;
        uint64_t i=0ul;
        pFile=fopen(pcFileName, "w");
        if(!pFile) return(_GHM_OPEN_FILE_ERROR);
        fprintf(pFile, "seqn\t");
        for(i=0ul; i<u64Dim; i++) {
            if(i==(u64Dim-1)) fprintf(pFile,"c%lu\n",i);
            else fprintf(pFile,"c%lu\t",i);
        }
        for(i=0ul; i<hdu64IV.u64NofVals; i++) {
            uint64_t j=0ul;
            flt32_t* pf32CurVec=&vfVecs.hdf32V.hPtr[hdu64IV.hPtr[i]*u64Dim];
            fprintf(pFile,"%f\t",(flt32_t) i);
            for(j=0ul; j<u64Dim; j++) {
                if(j==(u64Dim-1)) fprintf(pFile,"%f\n",pf32CurVec[j]);
                else fprintf(pFile,"%f\t",pf32CurVec[j]);
            }
        }
        fclose(pFile);
        return(_GHM_OK);
    }
    return(1); /* TODO: make error codes */
}
int32_t _ghf_wipeHDV_f32(_GHT_WRKSET wSet, _GHT_HDVECS_F32* const pVhdf32) {
    if(pVhdf32) {
        _ghf_wipeHD_f32(wSet,&pVhdf32->hdf32V);
        pVhdf32->u64Dim=0ul;
        pVhdf32->u64SemDim=0ul;
    }
    return(_GHM_OK);
}
