#ifndef __WLP_IMAGE_CONVERT_H__
#define __WLP_IMAGE_CONVERT_H__

/** @file
 * @brief WLP SDK aux header file
 * @author zhouty@dspchina.com
*/
#include <string.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef UINT64
typedef  unsigned long long UINT64;
#define OUT 
#endif

/**
 * pixel A,B,C,D,E,F,G,H,   I,J..O,P    Q,R...W,X    total 24 pixel
 * (A7,A6,A5,A4,A3,A2,A1,A0), (B7,B6,...B0),...(H7,H6,..H1,H0),    (I7,I6...I0)...(P7,P6..P1,P0)  
 *                                   (Q7,Q6...Q0)...(X7...X0)    total 192bit
 * convert to binary:
 * (A7B7..H7),(I7J7..P7),(Q7R7...X7)    //first binary frame,total 3Bytes, most high bit, 24pixel = 24bit, 
 * (A6B6..H6),(I6J6..P6),(Q6R6...X6)    //second binary frame,total 3Bytes, 
 * .
 * .
 * .									
 * (A0B0..H0), (I0J0..P0),(Q0R0...X0)   //total depth is 8
 *
 * above binary data will transfer to DMD memory.
 * @param [in] pSrc pixel data, such as Y,  or R, G, B
 * @param [in] width Image width, must be multi 8 bytes 
 * @param [in] height Image height.
 * @param [in] bitmask which bit you want to get; for value 0x80, get the most high bit of every pixel, for value 0xff, get total 8 bit
 * @param [out] pOut the buffer for binary data.
 * @return return value is pOut or NULL if fail.
*/

inline char* pixel2bin(char* pSrc, int width,int height, unsigned char bitmask, char* pOut)
{
	if (pSrc == NULL || pOut == NULL || width%8 != 0 )
		return NULL;
	char* ret =  pOut;
	int cnt = (width / 8)*height;  //once operate 8bytes.
	unsigned char bm;
	int i,j,k,m;
	char c;
	UINT64 temp;
	UINT64 mark[8];
	for(i = 0;i<8;i++)
		{
		temp =  0x80 >> i;
		mark[i] = 0;
		for(int j = 0;j<8;j++)
			mark[i] |= temp << j*8;
		}
	
	UINT64* p = (UINT64*)pSrc;
	for (i = 0; i < cnt; i++)
	{
		for (j=0,m=0,bm=bitmask; j < 8 && (bm & 0x80); j++, bm <<= 1)
		{	// get  A7,B7,C7,D7,E7,F7,G7,H7 (most high bit), then A6,B6,C6..H6.
			c = 0;
		 	temp = *p & mark[j]; 
			temp >>= (7-j);
		 	for(k =0;k < 8;k++)
		 		{
		 		c |= ((temp & 0x01) << k);
				temp >>= 8;
		 		}
			pOut[m * cnt] = c;
			m++;
		}
	pOut++;
	p++;
	}
	return ret;
}
/**
 *It's a reverse of pixel2bin, please refer pixel2bin.
 * @param [in] pSrc binary data.
 * @param [in] width Image width, must be multi 8 bytes 
 * @param [in] height Image height.
 * @param [in] bitmask which bit you had got; for value 0x80, pSrc point to one binary frame.
 * @param [out] pOut the buffer for pixel data.
 * @return return value is pOut or NULL if fail.
 * @see ::pixel2bin
*/
inline char* bin2pixel(char* pSrc, int width,int height, unsigned char bitmask, char* pOut)
{
	if ( pOut == NULL || pSrc == NULL ||width%8 != 0  )
		return NULL;
	char* ret =  pOut;
	int cnt = (width / 8)*height;  //once operate 8bytes.
	unsigned char bm;
	int i,j,k,m;
	UINT64 c;
	UINT64 temp;
	
	UINT64* p = (UINT64*)pOut;
	for (i = 0; i < cnt; i++)
	{
	temp = 0;
	for(j=0,m=0,bm=bitmask; j<8 && (bm & 0x80); j++,bm <<= 1)  //j means which bit
		{
		c = (UINT64) *(pSrc + cnt*m);
		m++;
		for(k = 0; k <8 ;k++) //which pixel
			{
			 temp |= (c & 0x01) << (k*8+7-j);
			 c >>= 1;
			}
		}
	pSrc++;
	*p++ = temp;
	}
	return ret;
}

/**
 * It is just a test ,user doesn't need use this.
  * @return void.
*/
inline void test(void){
char pSrc[24];
char pOut1[3][1];
char pOut3[3][3];
char pOut8[3][8];

memset(pSrc,0x80,24);
pixel2bin(pSrc, 24,1, 0x80, (char*)pOut1); //0xff,0xff,0xff
pixel2bin(pSrc, 24,1, 0xe0, (char*)pOut3); //0xff,0xff,0xff,0x00...0x00
pixel2bin(pSrc, 24,1, 0xff, (char*)pOut8); 

bin2pixel((char*)pOut8,24,1,0xff,pSrc);

memset(pSrc,0x81,24);
pixel2bin(pSrc, 24,1, 0xff, (char*)pOut8);

bin2pixel((char*)pOut8,24,1,0xff,pSrc);


memset(pSrc,0x70,24);
pixel2bin(pSrc, 24,1, 0xe0, (char*)pOut3);

memset(pSrc,0x0f,24);
pixel2bin(pSrc, 24,1, 0xff,(char*)pOut8);

}


/**
 * Convert a image file into Gray image(Gray image only have black and white color, using different depth of black to form different sight), 
 * extract the first bit from each BYTE then write in the char you put in,	from the first bit to the goal bit you want (less than 8), 
 * all the info will be written in your input char.
 * @param [in] fileName  the source image file you want to convert.
 * @param [in] width the destination binary image width.
 * @param [in] height the destination binary image height.
 *   and the second bit of every pixel.
 * @param [out] pDestImage the buffer for receive the binary data. one bytes for one pixel.
 * @note  the length of pDestImage must great than (width*height*depth/8) bytes.
 * @return NULL if fail, pDestImage if OK.
*/
	char* GetGrayPixelFormat( const char* fileName, int width, int height, OUT char* pDestImage );

/**
 * Convert a image file into Gray image(Gray image only have black and white color, using different depth of black to form different sight), 
 * extract the first bit from each BYTE then write in the char you put in,	from the first bit to the goal bit you want (less than 8), 
 * all the info will be written in your input char.
 * @param [in] fileName  the source image file you want to convert.
 * @param [in] width the destination binary image width.
 * @param [in] height the destination binary image height.
 * @param [in] bitmask which bit you want to get; for value 0x80, get the most high bit of every pixel, for value 0xff, get total 8 bit
 * @param [out] pBinImage the buffer for receive the binary data.
 * @note  the length of pDestImage must great than (width*height*depth/8) bytes.
 * @return NULL if fail, pBinImage if OK.
*/
	inline char* GetGrayBinFormat( const char* fileName, int width,int height, unsigned char bitmask, OUT char* pBinImage )
	{
		char* y= (char*) malloc(width*height);
		GetGrayPixelFormat(fileName,width,height,y);
	    pixel2bin(y, width, height, bitmask, pBinImage);
		free(y);
		return pBinImage;
	}

/**
 * Extract the first bit from each BYTE then write in the char you put in, from the first bit to the goal bit 
 *	you want (less than 8), red info be written inpDest_ImageRed, blue to pDest_ImageBlue, green to pDest_ImageGreen .
 * @param [in] fileName the path of the image file you want to convert
 * @param [in] width the destination binary image width.
 * @param [in] height the destination binary image height.
 * @param [out] pRed  the buffer for receive the binary data of the RED.
 * @param [out] pGreen the buffer for receive the binary data of the Green.
 * @param [out] pBlue the buffer for receive the binary data of the Blue.
 * @note  the length of pDestImageRed ,pDestImageGreen or pDestImageBlue  must great than (width*height*depth/8) bytes.
 * @return false if fail, true if OK.
*/

bool GetRGBPixelFormat( const char* fileName, int width, int height, OUT char* pRed, OUT char* pGreen, OUT char* pBlue);

/**
 * Extract the first bit from each BYTE then write in the char you put in, from the first bit to the goal bit 
 *	you want (less than 8), red info be written inpDest_ImageRed, blue to pDest_ImageBlue, green to pDest_ImageGreen .
 * @param [in] fileName the path of the image file you want to convert
 * @param [in] width the destination binary image width.
 * @param [in] height the destination binary image height.
 * @param [in] bitmask which bit you want to get; for value 0x80, get the most high bit of every pixel, for value 0xff, get total 8 bit
 * @param [out] pBinRed  the buffer for receive the binary data of the RED.
 * @param [out] pBinGreen the buffer for receive the binary data of the Green.
 * @param [out] pBinBlue the buffer for receive the binary data of the Blue.
 * @note  the length of pDestImageRed ,pDestImageGreen or pDestImageBlue  must great than (width*height*depth/8) bytes.
 * @return false if fail, true if OK.
*/
inline bool GetRGBBinFormat( const char* fileName, int width, int height, unsigned char bitmask, OUT char* pBinRed, OUT char* pBinGreen, OUT char* pBinBlue)
{
		char* r=(char*)malloc(width*height);
		char* g=(char*)malloc(width*height);
		char* b=(char*)malloc(width*height);
		bool ret = GetRGBPixelFormat(fileName,width,height,r,g,b);
		if(ret){
			pixel2bin(r, width, height, bitmask, pBinRed);
			pixel2bin(g, width, height, bitmask, pBinGreen);
			pixel2bin(b, width, height, bitmask, pBinBlue);
		}
		free(r);
		free(g);
		free(b);
		return ret;
	}

/**
 * Add a serial_num into one image file
 * @param [in] pFilePath the path of the Images file
 * @param [in] width destination image width
 * @param [in] height destination image height
 * @param [in] genarateImageCount the path of the Images file
 * @return false if fail, true if OK.
*/

bool AddSerialNumber(char* pFilePath,int width,int height,int genarateImageCount);

/**
 * @brief When a frame ready, will call this function.
 * @param pY The buffer for gray data with width*height length.
 * @return zero if OK, -1 if you do not want get next frame, other value TBD.
 * @note  You can call sleep() in this fucntion.
 * @see  ::GetGrayFromVideoFile
*/
typedef int (*ONFRAMEDDATA)(char* pY) ;

/**
 * Convert a image file into Gray image(Gray image only have black and white color, using different depth of black to form different sight), 
 * extract the first bit from each BYTE then write in the char you put in,	from the first bit to the goal bit you want (less than 8), 
 * all the info will be written in your input char.
 * @param [in] fileName  the source video file.
 * @param [in] width the destination binary image width.
 * @param [in] height the destination binary image height.
 * @param [out] onFrameData the buffer for receive the binary data. one bytes for one pixel.
 * @return NULL if fail, pDestImage if OK.
*/
	char* GetGrayFromVideoFile(const char* fileName, int width, int height, ONFRAMEDDATA onFrameData);
	
#ifdef __cplusplus
}
#endif

#endif