//
//  subpixel_edge.h
//  subpixel_edge
//
//  Created by Mehdi Stapleton on 2015-05-18.
//
//

#ifndef __SUBPIXEL_EDGE__
#define __SUBPIXEL_EDGE__

#include "OpenCL/opencl.h"

//#define XCODE_COMPILATION
#ifndef XCODE_COMPILATION
#include "mex.h"
#endif

#include <sstream>
#include <string>
#include <cstdio>
#include <iostream>
#include <fstream>

#include <vector>
#include <cmath>

//---------------------------------------------------------------
//             GENERAL PREPROCESSOR DEFINITIONS
//---------------------------------------------------------------

//#define SHOW_DEBUG_INFO
#define BITS_PER_BYTE 8
#define BUILD_LOG_BUFFER_SIZE 10000
#define NHOOD_SIZE 3
#define BUILD_PROGRAM_DEFINE "-D NHOOD_SIZE=3"

//---------------------------------------------------------------
//                      TYPE DEFINITIONS
//---------------------------------------------------------------

typedef double   matrix_t;
typedef float    grad_t;
typedef float    subpixel_t;
typedef uint32_t index_t;

//---------------------------------------------------------------
//                      GLOBAL CONSTANTS
//---------------------------------------------------------------

#ifdef XCODE_COMPILATION
static const std::string PROJECT_DIR_PATH = "../subpixel_edge/";
#else
static const std::string PROJECT_DIR_PATH = "/Users/mehdistapleton/Documents/MATLAB/MASC/OpenCL Tools/subpixel_edge/";
#endif

static const std::size_t LOCAL_WORK_SIZE = 16;

template<typename T = char>
struct Image
{
	std::vector<T> pixels;
	size_t width, height;
    
    Image& operator=(const Image& img)
    {
        width  = img.width;
        height = img.height;
        
        for(index_t i=0; i<img.pixels.size(); ++i)
        {
            pixels.push_back(img.pixels[i]);
        }
        
        return *this;
    }
    
    void zero_fill(void)
    {
        size_t nPxls = width*height;
        pixels.resize(nPxls);
        std::fill(pixels.begin(), pixels.end(), 0);
    }
};

//---------------------------------------------------------------
//                      LOGGING FUNCTIONS
//---------------------------------------------------------------

#define LOG_DEBUG(txt) {std::ostringstream debug_oss; \
debug_oss <<"Debug:"<< txt << ";\n"; \
mexPrintf(debug_oss.str().c_str());}

#define LOG_PRINT(txt) {std::ostringstream debug_oss; \
debug_oss << txt << ";\n"; \
mexPrintf(debug_oss.str().c_str());}

#define LOG_WARNING(txt) {std::ostringstream debug_oss; \
debug_oss <<"Warning:"<< txt << ";\n"; \
mexPrintf(debug_oss.str().c_str());}

#define LOG_ERROR(txt) {std::ostringstream debug_oss; \
debug_oss <<"Error:"<< txt << ";\n"; \
mexPrintf(debug_oss.str().c_str());}

//---------------------------------------------------------------
//                      MACROS
//---------------------------------------------------------------

#define PROJECT_RELPATH(relpath) PROJECT_DIR_PATH + relpath

#define IS_REAL_3D_FULL_DOUBLE(P) (!mxIsComplex(P) && \
        mxGetNumberOfDimensions(P) == 3 && !mxIsSparse(P) && mxIsDouble(P))

#define IS_REAL_2D_FULL_DOUBLE(P) (!mxIsComplex(P) && \
        mxGetNumberOfDimensions(P) == 2 && !mxIsSparse(P) && mxIsDouble(P))

#define IS_REAL_SCALAR(P) (IS_REAL_2D_FULL_DOUBLE(P) && mxGetNumberOfElements(P) == 1)

//---------------------------------------------------------------
//                      HELPER FUNCTIONS
//---------------------------------------------------------------


//---------------------------------------------------------------
//                 MAIN FUNCTION DECLARATIONS
//---------------------------------------------------------------

/*\brief perform GPU-accelerated Devernay-NMS-method
 */
#ifndef XCODE_COMPILATION
void subpixel_edge_gpu(Image<grad_t> &xGradImgOcl, Image<grad_t> &yGradImgOcl,
                       Image<grad_t> &edgeImgOcl, Image<subpixel_t> &xDeltaImgOcl, Image<subpixel_t> &yDeltaImgOcl);
#endif

//Courtesy of Anteru's Blog: https://anteru.net/tag/opencl/

/*\brief return name of platform for host computer (ex. Apple)
 */
std::string GetPlatformName(cl_platform_id id);

/*\brief return name of devices available (ex. Radeon 6750M)
 */
std::string GetDeviceName(cl_device_id id);

/*\brief checks for successful error code (CL_SUCCESS)
 */
void CheckError(cl_int error);

/*\brief reads file (name provided) and returns string of entire
         text
 */
std::string LoadKernel(const char* name);

/*\brief uses string of text from .cl and builds program for devices
         associated with context
 */
cl_program CreateProgram(const std::string& source, cl_context context);

#endif /* defined(__SUBPIXEL_EDGE__) */
