/*
mysqludf_blobfile - a library for load and save blob to file. 
*/ 
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <cstring>
//#include <windows.h>
#include <mysql.h>
#include <stdio.h>
using namespace std;

/************************
Example show 3-Dimensional data.

int,int,int,int;3;3;2;
{
  {  {1,4}{3,5}{3,6}  }
  {  {4,7}{5,8}{6,9}  }
  {  {6,1}{8,1}{5,1}  }
}

The structure will be filled like this: 

dim           = 3
noEle         = 18 
dimVal_size   = 12            (The length of characters used for 
                              defining dimensions datatype
                              in this case it will be "int,int,int,".) 
dimVal        = int,int,int,  (This pointer contains the actual
                               dimension type.)  
eleType_size  = 3             (size of the charactors used to define
                               element type.)
eleType       = int           (Actuall datatype in elements.)
dimA_size     = 1 1 1         (Contains the number of charactors used 
                               to define length of each dimension. 
                               in this case we have "3;3;2"m required 1
                               charactor for each dinmension.)  
dimA          = 3 3 2          (Actuall length of each dimension.) 
intA          =                (pointer contains all the integer elements.)
doubleA       =                (pointer contains all the double elements.)  
stringA_size  =                (In case of strings, this variable contains
                                the length of a string in each dimension.)
stringA       =                (Contains the actual string.)

Example show 2-Dimensional data.

int,int,int;10;10;
{
  {1,3,5,3,6,6,7,8,9,23}
  {4,7,5,8,9,5,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}
  {6,1,8,1,1,9,7,8,9,23}     
}

dim           = 2
noEle         = 100 
dimVal_size   = 8
dimVal        = int,int,
eleType_size  = 3
eleType       = int
dimA_size     = 2 2
dimA          = 10 10
intA          = ------- (All the elements.  )
*************************/
struct Matrix{       
         int  dim;  //
         long int noEle;
         int dimVal_size;
         char* dimVal;
         int eleType_size; 
         char* eleType; // datatype of the values to be stored in intA or
         
         int* dimA_size;       
         char ** dimA; // diamension data  
         
         int * intA;// int value array
         double* doubleA;// double value array
         int* stringA_size;
         char** stringA;// string value array
} ;



#if defined(_WIN32) || defined(_WIN64) 
#define DLLEXP __declspec(dllexport) 
#else 
#define DLLEXP 
#endif 


/****************************************************************************** 
** FUNCTION DECLARATIONS 
******************************************************************************/ 

#ifdef	__cplusplus 
extern "C" { 
#endif 


DLLEXP	my_bool	 file_to_blob_init(UDF_INIT *initid, UDF_ARGS *args, char *message); 
DLLEXP void	 file_to_blob_deinit(UDF_INIT *initid); 
DLLEXP  char*  file_to_blob(UDF_INIT *initid, UDF_ARGS *args, char* result, unsigned long* length, char *is_null, char *error);

#ifdef	__cplusplus 
} 
#endif 


/****************************************************************************** 
** FUNCTION DEFINITIONS 
******************************************************************************/ 
//find the numeric value of queried diamension
// ---
bool writeData(const Matrix * mm, const char* filename){
   
   FILE *fp;
   fp=fopen(filename, "wb");
   if(!fp)
      return false;
   fwrite(&mm->dim, sizeof(int), 1, fp);
   fwrite(&mm->noEle, sizeof(long int), 1, fp);

   fwrite(&mm->dimVal_size, sizeof(int), 1, fp);
   fwrite(mm->dimVal, sizeof(char), mm->dimVal_size+1, fp);

   fwrite(&mm->eleType_size, sizeof(int), 1, fp);
   fwrite(mm->eleType, sizeof(char), mm->eleType_size+1, fp);
   
   fwrite(mm->dimA_size, sizeof(int), mm->dim, fp);
   for(int i=0; i<mm->dim; i++) 
       fwrite(mm->dimA[i], sizeof(char), mm->dimA_size[i]+1, fp);
   if ( strcmp(mm->eleType,"int") == 0)
       fwrite(mm->intA, sizeof(int), mm->noEle, fp);
   else if (strcmp(mm->eleType,"float") == 0)
      fwrite(mm->doubleA, sizeof(double), mm->noEle, fp);
   else if (strcmp(mm->eleType,"string") == 0){
      fwrite(mm->stringA_size, sizeof(int), mm->noEle, fp);
      for(long i = 0; i<mm->noEle;i++)         
          fwrite(mm->stringA[i], sizeof(char), mm->stringA_size[i], fp);

   }
   fclose(fp);
   return(true);


}
bool readData(Matrix * mm, const char* filename){

   
   FILE *f;
   f=fopen(filename, "rb");
   if(!f)
       return false;
   
   fread(&mm->dim, sizeof(int), 1, f);
   fread(&mm->noEle, sizeof(long int), 1, f);
   
   fread(&mm->dimVal_size, sizeof(int), 1, f);
   mm->dimVal = (char*)calloc(mm->dimVal_size+1,sizeof(char));
   fread(mm->dimVal, sizeof(char), mm->dimVal_size+1, f);

   fread(&mm->eleType_size, sizeof(int), 1, f);
   mm->eleType = (char*)calloc(mm->eleType_size+1,sizeof(char));
   fread(mm->eleType, sizeof(char), mm->eleType_size+1, f);

   mm->dimA_size = (int*)calloc(mm->dim, sizeof(int));
   fread(mm->dimA_size, sizeof(int), mm->dim, f);
  
   mm->dimA = (char**)calloc(mm->dim,sizeof(char*));
    for (int i=0; i<mm->dim; i++)
        mm->dimA[i] = (char*)calloc(mm->dimA_size[i]+1,sizeof(char));
   for(int i=0; i<mm->dim; i++)
       fread(mm->dimA[i], sizeof(char), mm->dimA_size[i]+1, f);
   if (strcmp(mm->eleType,"int") == 0)
      {
   mm->intA = (int*)calloc(mm->noEle,sizeof(int));
   if(mm->intA == NULL)
         return false;
   else
     fread(mm->intA, sizeof(int), mm->noEle, f);
      }
   else if ( strcmp(mm->eleType,"float") == 0)
      {
   mm->doubleA = (double*)calloc(mm->noEle,sizeof(double));
   if(mm->doubleA == NULL)
         return false;
   else
     fread(mm->doubleA, sizeof(double), mm->noEle, f);  
      }
   else if (strcmp(mm->eleType,"string") == 0) {
      
      mm->stringA_size = (int*)calloc(mm->noEle,sizeof(int));
      fread(mm->stringA_size, sizeof(int), mm->noEle, f);   

     mm->stringA = (char**)calloc(mm->noEle,sizeof(char*)); 
     for(long int i = 0; i<mm->noEle;i++){
          mm->stringA[i] = (char*)calloc(mm->stringA_size[i]+1,sizeof(char)); 
          fread(mm->stringA[i], sizeof(char), mm->stringA_size[i], f);
      } 
        
   } 



   
   fclose(f);
   return true;
}

bool fillStructure(Matrix* mm, char* obj){

   memcpy(&mm->dim,obj, sizeof(int));
   obj+=sizeof(int);

   memcpy(&mm->noEle,obj, sizeof(long int));
   obj+=sizeof(long int);

   memcpy(&mm->dimVal_size,obj, sizeof(int));
   obj+=sizeof(int);
   
   mm->dimVal = (char*)calloc(mm->dimVal_size+1,sizeof(char));
   memcpy(mm->dimVal, obj, sizeof(char)*mm->dimVal_size+1);
   obj+=(sizeof(char)*mm->dimVal_size)+1;
   
   memcpy(&mm->eleType_size, obj,sizeof(int));
   obj+=sizeof(int);

   mm->eleType = (char*)calloc(mm->eleType_size+1,sizeof(char));   
   memcpy(mm->eleType, obj, sizeof(char)*mm->eleType_size+1);
   obj+=(sizeof(char)*mm->eleType_size)+1;

   mm->dimA_size = (int*)calloc(mm->dim, sizeof(int)); 
   memcpy(mm->dimA_size, obj, sizeof(int)*mm->dim);
   obj+=(sizeof(int)*mm->dim);

   mm->dimA = (char**)calloc(mm->dim,sizeof(char*)); 
   for (int i=0; i<mm->dim; i++)
       {
        mm->dimA[i] = (char*)calloc(mm->dimA_size[i]+1,sizeof(char)); 
        memcpy(mm->dimA[i], obj, (mm->dimA_size[i]*sizeof(char))+1);
        obj+=(sizeof(char)*mm->dimA_size[i])+1;
       }
     
  ////// INTEGERS
  if ( strcmp(mm->eleType,"int") == 0) {
  long int i = 0;
  //memcpy(mm->intA, obj,sizeof(int)*mm->noEle);
  mm->intA = (int*)calloc(mm->noEle,sizeof(int));
  int *tt = mm->intA;
  if (mm->noEle < 1000)
     {
      memcpy(tt, obj, sizeof(int)*mm->noEle);
      i = mm->noEle;
  }
  else{
  for (i=1000; i<mm->noEle; i+=1000){
      memcpy(tt, obj,sizeof(int)*1000);
      tt+=1000;
      obj+=sizeof(int)*1000;

  }
   if ((mm->noEle-i) > 0 )
      memcpy(tt, obj, sizeof(int)*(mm->noEle-i));
   }
  }
  ////// 
  ////// DOUBLES
  else if ( strcmp(mm->eleType,"float") == 0){
  long int i = 0;
  mm->doubleA = (double*)calloc(mm->noEle,sizeof(double)); 
  double *tt = mm->doubleA;
  if (mm->noEle < 1000)
     {
      memcpy(tt, obj, sizeof(double)*mm->noEle);
      i = mm->noEle;
  }
  else{
  for (i=1000; i<mm->noEle; i+=1000){
      memcpy(tt, obj, sizeof(double)*1000);
      tt+=1000;
      obj+=sizeof(double)*1000;

  }
   if ((mm->noEle-i) > 0 )
      memcpy(tt, obj, sizeof(double)*(mm->noEle-i));
   }
  }
  //////
  ////// STRING
  else if ( strcmp(mm->eleType,"string") == 0){
  long int i = 0;
  mm->stringA_size = (int*)calloc(mm->noEle,sizeof(int));
  memcpy(mm->stringA_size, obj, sizeof(int)*mm->noEle);
  obj+=sizeof(int)*mm->noEle;

  mm->stringA = (char**)calloc(mm->noEle,sizeof(char*));
  for (i=0; i<mm->noEle; i++)
       {
        mm->stringA[i] = (char*)calloc(mm->stringA_size[i]+1,sizeof(char));
        memcpy(mm->stringA[i], obj, (mm->stringA_size[i]*sizeof(char))+1);
        obj+=(sizeof(char)*mm->stringA_size[i])+1;
       }
  }
  //////
  return true;

}

bool copyData(Matrix * mm, char*& obj){

   
   memcpy(obj, &mm->dim, sizeof(int));
   obj+=sizeof(int);
    
   memcpy(obj, &mm->noEle, sizeof(long int));
   obj+=sizeof(long int);

   memcpy(obj, &mm->dimVal_size, sizeof(int));
   obj+=sizeof(int);
     
   memcpy(obj, mm->dimVal,sizeof(char)*mm->dimVal_size+1);
   obj+=(sizeof(char)*mm->dimVal_size)+1;
    
   memcpy(obj, &mm->eleType_size,sizeof(int));
   obj+=sizeof(int);
    
   memcpy(obj, mm->eleType,sizeof(char)*mm->eleType_size+1);
   obj+=(sizeof(char)*mm->eleType_size)+1;
    
   memcpy(obj, mm->dimA_size, sizeof(int)*mm->dim);
   obj+=(sizeof(int)*mm->dim);
    
   for (int i=0; i<mm->dim; i++)
       {
        memcpy(obj, mm->dimA[i], (mm->dimA_size[i]*sizeof(char))+1);
        obj+=(sizeof(char)*mm->dimA_size[i])+1;
       }
  ////// INTEGERS
  if ( strcmp(mm->eleType,"int") == 0) {
  int *tt = mm->intA; 
  long int i = 0;  
  //memcpy(obj, mm->intA, sizeof(int)*mm->noEle);
  
  if (mm->noEle < 1000)
     {
      memcpy(obj, tt, sizeof(int)*mm->noEle);
      i = mm->noEle;    
  }
  else{
  for (i=1000; i<mm->noEle; i+=1000){ 
      memcpy(obj, tt, sizeof(int)*1000); 
      tt+=1000;
      obj+=sizeof(int)*1000;
  
  }
   if ((mm->noEle-i) > 0 )
      memcpy(obj, tt, sizeof(int)*(mm->noEle-i));
   }
  }
  ////// 
  ////// DOUBLES
  else if ( strcmp(mm->eleType,"float") == 0){
  double *tt = mm->doubleA;
  long int i = 0;

  if (mm->noEle < 1000)
     {
      memcpy(obj, tt, sizeof(double)*mm->noEle);
      i = mm->noEle;
  }
  else{
  for (i=1000; i<mm->noEle; i+=1000){
      memcpy(obj, tt, sizeof(double)*1000);
      tt+=1000;
      obj+=sizeof(double)*1000;

  }
   if ((mm->noEle-i) > 0 )
      memcpy(obj, tt, sizeof(double)*(mm->noEle-i));
   }
  }
  //////
  ////// STRING
  else if ( strcmp(mm->eleType,"string") == 0){
  long int i = 0;
  memcpy(obj, mm->stringA_size, sizeof(int)*mm->noEle);
  obj+=sizeof(int)*mm->noEle;
  for (i=0; i<mm->noEle; i++)
       {
        memcpy(obj, mm->stringA[i], (mm->stringA_size[i]*sizeof(char))+1);
        obj+=(sizeof(char)*mm->stringA_size[i])+1;
       }
  }
  //////
  return true;
  }





bool freeData(Matrix * mt){

   free(mt->dimVal);

   for(int i=0; i<mt->dim; i++)
       free(mt->dimA[i]);
   free(mt->dimA);
   free(mt->dimA_size);
   
   if ( strcmp(mt->eleType,"int") == 0)  
      free(mt->intA);
   else if ( strcmp(mt->eleType,"float") == 0)
      free(mt->doubleA);
   else if ( strcmp(mt->eleType,"string") == 0)
      {
       free(mt->stringA_size);
       for(long int i=0; i<mt->noEle; i++) 
           free(mt->stringA[i]); 
       free(mt->stringA);

      } 
    
   free(mt->eleType);
 return true;
}

bool inputData (Matrix * mpt, const char* filename, long int* totalSize){

        char x='x';
        int count=0;
        int dim=0;
        int intval=0;
        double dval=0.0;
        bool end=false;
        string eleTypeS("");
        string dimValS("");
        
        ifstream inFile(filename);
        if (!inFile) {
           return false;
        }
        while ((inFile >> x)&& (x!=';')) {
                eleTypeS+=x;
                count +=1;
                if (x==',') {
                        dimValS+=eleTypeS;
                        eleTypeS="";
                        dim += 1;
                }

        }
        *totalSize +=sizeof(int);
        mpt->dim=dim;
        mpt->eleType_size = eleTypeS.length();
        *totalSize += (eleTypeS.length()+1);
        if( ( mpt->eleType = (char*)calloc(eleTypeS.length()+1 ,sizeof(char)) ) == NULL)
            {
             //free(mpt->eleType); 
             return false;
            }
          
        strcpy(mpt->eleType, eleTypeS.c_str());
        mpt->dimVal_size = dimValS.length();
        *totalSize += (dimValS.length()+1);
        if( ( mpt->dimVal = (char*)calloc(dimValS.length()+1,sizeof(char)) ) == NULL)
            {
             //free(mpt->dimVal);
             return false;
            }
        strcpy(mpt->dimVal, dimValS.c_str());
        string a ;
        string* dimA = new string[dim];
        count=0;
        int dimCount=0;

        long int numEle=1;
        string dimS; //=("");
        int dimEleCount=0;
        //acessing the diamension data

        while ((inFile >> x)&& count < dim) {
                if (x==';') {
                        if (dimEleCount>0) {
                                numEle *= (dimEleCount+1);
                                dimEleCount=0;
                        }
                        else {
                                numEle *= atoi(dimS.c_str());
                        }
                        dimA[dimCount++] = dimS.c_str();
                        dimS.clear();
                        ++count;
                }
                else {
                        if (x==','){
                                dimEleCount+=1;
                        }
                        dimS+=x;

                }
        }
       //cout<<"number of elements "<<numEle<<endl;
        mpt->noEle=numEle;
        *totalSize += (dimCount*sizeof(int));
       // I have removed 1 from here. 
        mpt->dimA_size = (int*)calloc(dimCount,sizeof(int));
        for(int i=0; i<dimCount; i++)
           mpt->dimA_size[i] = dimA[i].length();
        *totalSize += (dim*sizeof(char*));
        mpt->dimA = (char**)calloc(dim,sizeof(char*));
        if (mpt->dimA == NULL)
            return false;
        for(int i=0; i<dim; i++){
            *totalSize += (dimA[i].length()+1);
            mpt->dimA[i] = (char*)calloc(dimA[i].length()+1,sizeof(char));
            if(mpt->dimA[i] == NULL)
                  return false;
        }  
        for(int i=0; i<dim; i++)
            strcpy(mpt->dimA[i],dimA[i].c_str());
        delete [] dimA;

        if (eleTypeS=="int"){
                *totalSize += (numEle*sizeof(int)); 
                mpt->intA=(int *)calloc(numEle,sizeof(int));
                    if(mpt->intA == NULL) 
                     return false; 
        }
        else if (eleTypeS=="float"){
                *totalSize += (numEle*sizeof(double));
                mpt->doubleA=(double *)calloc(numEle,sizeof(double));
                   if(mpt->doubleA == NULL)
                     return false;
        }
        else if (eleTypeS=="string"){
               *totalSize += (numEle*sizeof(int));  
               mpt->stringA_size = (int*) calloc(numEle,sizeof(int));  
               *totalSize += (numEle*sizeof(char*));
               mpt->stringA=(char **)calloc(numEle,sizeof(char*));
        }

        count=0;
        long int arrCount=0;
        string valS;
        //datatype conversion
        while ((inFile >> x)&& (x!=';')) {
                if ((x!='}')&&(x!='{')){

                        if (x==',') {
                                //cout<<"vals "<<valS<<endl;
                                //valS[valS.length()+1] = '\0';
                                if (eleTypeS=="int"){
                                        intval=atoi(valS.c_str());
                                        mpt->intA[arrCount++]=intval;
                                }
                                else if (eleTypeS=="float"){
                                        //cout<<"float "<<valS<<endl;
                                        dval=atof(valS.c_str());
                                        mpt->doubleA[arrCount++]=dval;
                                        //mpt->doubleA = &dval;
                                        //mpt->doubleA++;
                                }
                                else if (eleTypeS=="string"){
                                        //cout<<"string "<<val<<endl;
                                        // mpt->stringA[++arrCount]=valS;
                                        //mpt->stringA = &valS;
                                        //mpt->stringA++;
                                        mpt->stringA_size[arrCount] = valS.length();
                                        *totalSize += ((mpt->stringA_size[arrCount]+1)*sizeof(char));
                                        mpt->stringA[arrCount] = (char*)calloc(mpt->stringA_size[arrCount]+1,sizeof(char));
                                        strcpy(mpt->stringA[arrCount], valS.c_str());
                                        arrCount++; 
                                }
                                valS.clear();
                        }
                        else {
                                valS+=x;
                        }
                }
                else {
                        if ((x=='}') && (!end)) {
                                //cout<<"vals>> "<<valS<<endl;
                                end=true;
                                count=0;
                                //valS[valS.length()+1] = '\0';  
                                if (eleTypeS=="int"){
                                        intval=atoi(valS.c_str());
                                        mpt->intA[arrCount++]=intval;
                                        //mpt->intA = &intval;
                                        //mpt->intA++;  
                                        //cout<<"int "<<intval<<endl;
                                }
                                else if (eleTypeS=="float"){
                                        dval=atof(valS.c_str());
                                        //cout<<"float1 "<<valS<<endl;
                                        mpt->doubleA[arrCount++]=dval;
                                        //mpt->doubleA = &dval;
                                        //mpt->doubleA++;
                                }
                                else {
                                        //cout<<"string "<<val<<endl;
                                        // -- mpt->stringA[++arrCount]=valS;
                                        // NOW mpt->stringA = &valS;
                                        //mpt->stringA++;
                                        mpt->stringA_size[arrCount] = valS.length();
                                        *totalSize += (mpt->stringA_size[arrCount]+1);
                                        mpt->stringA[arrCount] = (char*)calloc(mpt->stringA_size[arrCount]+1,sizeof(char));
                                        strcpy(mpt->stringA[arrCount], valS.c_str());
                                        arrCount++; 
                                }
                        }
                        else if (x=='{') {
                                end=false;
                        }
                        valS.clear();
                }

        }
       inFile.close();
 return true;
}
 
// ***** file_to_blob ***** ///// 

my_bool file_to_blob_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { 

    
    if (args->arg_count != 2)
        {
            strcpy(message,"file_to_blob() requires input and output data file.");
            return 1;
        }
    return 0; 
} 
void file_to_blob_deinit(UDF_INIT *initid) {
   
   free(initid->ptr);
   
} 

char* file_to_blob(UDF_INIT *initid, UDF_ARGS *args, char* result, unsigned long* length, char *is_null, char *error)

{
       //char* comData = (char*)initid->ptr; 
       char* comData; 
       Matrix* mpt = (Matrix*)malloc(sizeof(Matrix));  
       string status = "Done";
       if (mpt == NULL)
          {
           status = "mpt is NULL";    
       }
       const char* inputFile = args->args[0];
       const char* outputFile = args->args[1];
       long int totalSize = 0;  
       if (inputData(mpt, inputFile, &totalSize))
          {
           status = "Yes";  
        }
       else{
           status = "No";
       }
      
       comData = (char*)malloc(totalSize*sizeof(char)); 
       initid->ptr = comData;      
         
       char* dd = comData;
       copyData(mpt, comData); 
       comData = dd;      
       
 
       freeData(mpt);
       free(mpt);
       
       result = comData;
       *length = totalSize; 
       return result; 
        
} 
