#ifndef _AX_TYPE_H
#define _AX_TYPE_H
#include "ax_config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>


#define YUV420 YUV420_SP
#define YVU420 YVU420_SP


//#define MALLOC_CHECK

void *AX_MALLOC_RECORD(int size);
void AX_FREE_RECORD(void *ptr);

void *AX_MALLOC_DEBUG(const char *func, int line, int size);
void AX_FREE_DEBUG(void *ptr);

void DUMP_AX_MEM_LIST_INFO(int flag);
void AX_CHECK_MEM_BOUND();

#ifdef MALLOC_CHECK
#define AX_MALLOC(sz) AX_MALLOC_DEBUG(__func__, __LINE__, sz)
#define AX_FREE(ptr)  AX_FREE_DEBUG(ptr)
#else
#define AX_MALLOC(sz) AX_MALLOC_RECORD(sz)
#define AX_FREE(ptr)  AX_FREE_RECORD(ptr)
#endif

#define AX_MALLOC_FAIL_LOG(mem) printf("AX MALLOC "#mem".p fail\n")

template <class T>
void AX_MALLOC_INC_TEMP(T &mem, int required_size)
{
	(mem).size = (required_size);
	int origin_len_1220 = 0;
	if((mem).p)
	{
		origin_len_1220 = *(((int*)(mem).p) - 1);
	} 
	int required_len_1220 = (required_size) * sizeof((mem).p[0]);
	if(((mem).p == NULL && required_len_1220 > 0) || required_len_1220 > origin_len_1220)
	{
		int required_enlarge_len_1220 = required_len_1220 ;
		if((mem).p)
		{
			AX_FREE((mem).p);
			(mem).p = 0;
		}
#ifdef _MSC_VER
		(mem).p=(decltype((mem).p))AX_MALLOC(required_enlarge_len_1220);
#else
		(mem).p = (typeof((mem).p))AX_MALLOC(required_enlarge_len_1220);
#endif
		if ((mem).p == NULL)
		{
			(mem).size = 0;
		}
	}
}


#define AX_MALLOC_INC(mem, type, required_size) AX_MALLOC_INC_TEMP(mem, (required_size))


template <class T>
void AX_MALLOC_INC_2D(T &mem)
{
	int required_size = mem.size.width * mem.size.height;
	int origin_len_1220 = 0;
	if((mem).p)
	{
		origin_len_1220 = *(((int*)(mem).p) - 1);
	} 
	int required_len_1220 = (required_size) * sizeof((mem).p[0]);
	if(((mem).p == NULL && required_len_1220 > 0) || required_len_1220 > origin_len_1220)
	{
		int required_enlarge_len_1220 = required_len_1220 ;
		if((mem).p)
		{
			AX_FREE((mem).p);
			(mem).p = 0;
		}
#ifdef _MSC_VER
		(mem).p=(decltype((mem).p))AX_MALLOC(required_enlarge_len_1220);
#else
		(mem).p = (typeof((mem).p))AX_MALLOC(required_enlarge_len_1220);
#endif
	}
}

#ifndef MAX
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#endif
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif

#ifndef ROUND
#define ROUND(f) (  ((f)>=0)?((int)((f)+0.5)):((int)((f)-0.5)) )
#endif

#ifndef CLIPMINMAX
#define CLIPMINMAX(v,mn,mx)				\
{										\
		if ((v)<(mn))		{(v)=(mn);}\
		else if((v)>(mx))	{(v)=(mx);}\
}
#endif




template <class T1, class T2>
T2* ax_buffer_get(T1 &b, int index)
{
	if(b.size == 0)
	{
		return NULL;
	}
	int temp_index = b.cur - b.size + 1 + index;
	if (temp_index >= 0)
	{
		return &b.buffers[temp_index];
	}
	else
	{
		return &b.buffers[temp_index+b.capacity];
	}
}
template <class T1, class T2>
T2* ax_buffer_get_back(T1 &b)
{
	return ax_buffer_get<T1, T2>(b, b.size-1);
}

template <class T1, class T2>
void ax_buffer_push_back(T1 &b, T2 &element)
{
	if (b.size == 0) 
	{
		b.cur = -1; 
	} 
	b.cur++; 
	if (b.cur >= b.capacity) 
	{ 
		b.cur = 0; \
	} 
	b.buffers[b.cur] = element; 
	b.size++; 
	if (b.size > b.capacity)
	{
		b.size = b.capacity;
	}
}

template <class T1, class T2>
T2 *ax_buffer_pop_back(T1 &b)
{
	 
}


#define AX_BUFFER_GET(b, type, index) (((b).cur-(b).size+1+index >= 0)?((type*)&(b).buffers[(b).cur-(b).size+1+index]):((type*)&(b).buffers[(b).cur-(b).size+1+index+(b).capacity])) 
#define AX_BUFFER_PUSH_BACK(b, element) { \
	if((b).size==0) \
	{\
		(b).cur=-1;\
	} \
	(b).cur++;\
	if((b).cur >= (b).capacity) \
	{ \
		(b).cur = 0; \
	} \
	(b).buffers[(b).cur] = element;\
	(b).size++; \
	if((b).size > (b).capacity) \
		(b).size=(b).capacity; \
	}\

#define AX_BUFFER_RESET(b) { \
	(b).cur=-1; \
	(b).size=0; \
	}


template <class T1>
void AX_ARRAY_ZERO(T1 &mem)
{
	memset(mem.p, 0, sizeof(mem.p[0]) * mem.size);
}


template <class T1, class T2>
void AX_ARRAY_PUSH_BACK(T1 &mem, T2 &elem)
{
	int required_size = mem.size + 1;
	int origin_len = 0;
	if(mem.p)
	{
		origin_len = *(((int*)(mem).p) - 1);
	}
	int required_len = (required_size) * sizeof((mem).p[0]);
	if(((mem).p == NULL && required_len > 0) || required_len > origin_len)
	{
		int required_enlarge_len = required_len * 2;
		void *temp_ptr = AX_MALLOC(required_enlarge_len);

		if((mem).p)
		{
			memcpy(temp_ptr, mem.p, origin_len);
			AX_FREE(mem.p);
		}
#ifdef _MSC_VER
		mem.p = (decltype((mem).p))temp_ptr;
#else
		mem.p=(typeof((mem).p))temp_ptr;
#endif
	}
	mem.p[mem.size] = elem;
	mem.size = required_size;
}

template <class T1>
void AX_ARRAY_CLEAR(T1 &mem)
{
	mem.size = 0;
}

template <class T1>
void AX_ARRAY_RELEASE(T1 &mem)
{
	AX_FREE(mem.p);
	mem.p = NULL;
	mem.size = 0;
}

/*deep copy*/
template <class T1>
void AX_ARRAY_COPY(T1 &src, T1 &dst)
{
	AX_MALLOC_INC_TEMP(dst, src.size);
	memcpy(dst.p, src.p, sizeof(src.p[0]) * src.size);
}



enum {
	AX_INT8 = 0,
	AX_UINT8 = 1,
	AX_INT16 = 2,
	AX_UINT16 = 3,
	AX_INT32 = 4,
	AX_UINT32 = 5,
	AX_FLOAT32 = 6,
	AX_FLOAT64 = 7,
};


#if defined _STDINT || defined __EOS__

#else
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
#endif
typedef float float32_t;
typedef double float64_t;
typedef unsigned short ushort;



#ifndef _AX_RECT_T
#define _AX_RECT_T
typedef struct
{
	int x;
	int y;
	int width;
	int height;
} ax_rect_t;
#endif

#ifndef _AX_BOX_T
#define _AX_BOX_T
typedef struct
{
	int x1;
	int y1;
	int x2;
	int y2;
} ax_box_t;
#endif


#ifndef _AX_SIZE_T
#define _AX_SIZE_T
typedef struct
{
	int width;
	int height;
} ax_size_t;
#endif

#ifndef _AX_SIZE_F_T
#define _AX_SIZE_F_T
typedef struct
{
	float width;
	float height;
} ax_sizef_t;
#endif

#ifndef _AX_POINTU16_T
#define _AX_POINTU16_T
typedef struct {
	unsigned short x;
	unsigned short y;
} ax_pointu16_t;
#endif

#ifndef _AX_POINTS16_T
#define _AX_POINTS16_T
typedef struct {
	short x;
	short y;
} ax_points16_t;
#endif

typedef struct
{
	ax_points16_t *p;
	int size;
} ax_points16_array_t;

#ifndef _AX_POINT_T
#define _AX_POINT_T
typedef struct {
	int x;
	int y;
} ax_point_t;
#endif


#ifndef _AX_POINT_2F_T
#define _AX_POINT_2F_T
typedef struct {
	float x;
	float y;
} ax_pointf_t;
#endif

#ifndef _AX_POINT_2D_T
#define _AX_POINT_2D_T
typedef struct {
	double x;
	double y;
} ax_pointd_t;
#endif

#ifndef _AX_POINT_3D_T
#define _AX_POINT_3D_T
typedef struct {
	double x;
	double y;
	double z;
} ax_point3d_t;
#endif

#ifndef _AX_BBOX_T
#define _AX_BBOX_T
typedef struct {
	int x1;
	int y1;
	int x2;
	int y2;
} ax_bbox_t;
#endif


#ifndef _AX_YUV_T
#define _AX_YUV_T
typedef struct
{
	void *y;
	void *uv;
	void *y_phy;
	void *uv_phy;
	int stride1;
	int stride2;
	ax_size_t size;
	img_fmt format;
} ax_yuv_t;
#endif



#ifndef _AX_INT_ARRAY_T
#define _AX_INT_ARRAY_T
typedef struct
{
	int *p;
	int size;
} ax_int_array_t;

#endif

#ifndef _UINT_ARRAY_T
#define _UINT_ARRAY_T
typedef struct
{
	unsigned int *p;
	int size;
} ax_uint_array_t;
#endif


#ifndef _AX_INT8_ARRAY_T
#define _AX_INT8_ARRAY_T
typedef struct
{
	int8_t *p;
	int size;
} ax_int8_array_t;
#endif

#ifndef _AX_DOUBLE_ARRAY_T
#define _AX_DOUBLE_ARRAY_T
typedef struct
{
	double *p;
	int size;
} ax_double_array_t;
#endif

#ifndef _AX_FLOAT_ARRAY_T
#define _AX_FLOAT_ARRAY_T
typedef struct
{
	float *p;
	int size;
} ax_float_array_t;
#endif

#ifndef _UCHAR_MATRIX_T
#define _UCHAR_MATRIX_T
typedef struct
{
	unsigned char *p;
	ax_size_t size;
} ax_uchar_matrix_t;
#endif

typedef ax_uchar_matrix_t ax_gray_t;

#ifndef _USHORT_ARRAY_T
#define _USHORT_ARRAY_T
typedef struct
{
	unsigned short *p;
	int size;
} ax_ushort_array_t;
#endif


#ifndef __AX_DETECT_BOX_T_
#define __AX_DETECT_BOX_T__
typedef struct
{
	int   x1;
	int   y1;
	int   x2;
	int   y2;
	int   class_idx;
	int   origin_class_idx;
	float score;
} ax_detect_box_t;
#endif


#ifndef __AX_TRACK_BOX_T__
#define __AX_TRACK_BOX_T__
typedef struct
{
	int   id;
	int   x1;
	int   y1;
	int   x2;
	int   y2;
} ax_track_box_t;
#endif

#ifndef __AX_DETECT_BOX_ARRAY_T__
#define __AX_DETECT_BOX_ARRAY_T__
typedef struct
{
	ax_detect_box_t *p;
	int size;
} ax_detect_box_array_t;
#endif


class ax_tensor_mem_t
{
public:
	ax_tensor_mem_t();
	ax_tensor_mem_t(int len);
	void *alloc_mem(int len);
	~ax_tensor_mem_t();

	void *data;
	ax_tensor_mem_t(const ax_tensor_mem_t &m);
	ax_tensor_mem_t& operator = (const ax_tensor_mem_t &m);

	int len;
private:
	int *refcount;
	void release();
};

class ax_tensor_t
{
public:
	ax_tensor_t();
	ax_tensor_t(const ax_tensor_t &m);
	ax_tensor_t(int num, int channel, int row, int col, int dtype);
	ax_tensor_t(int *dims, int dtype);
	~ax_tensor_t();
	int numel();
	int dims[4];
	void *data;
	int  dtype;
	double fix_scale; /*available only when dtype == INT8 */

	ax_tensor_t convert(int dtype);
	ax_tensor_t mul(double scale);
	ax_tensor_t copy();
	void set_to(double value);
	void set_fix_scale(double fix_scale);
	//private:
	ax_tensor_mem_t mem;
	void create(int *dims, int dtype);
	void create(int *dims, int dtype, void *ptr);

	template <typename _Tp> inline _Tp* ptr(int dim0 = 0, int dim1 = 0, int dim2 = 0, int dim3 = 0){
		int step2 = dims[3];
		int step1 = dims[2] * step2;
		int step0 = dims[1] * step1;
		return (_Tp*)data + dim0 * step0 + dim1 * step1 + dim2 * step2 + dim3;
	}

	double norm();
	double dot(ax_tensor_t &t2);
};

#endif