extern "C" {
#include "api.h"
#include "core.h"

#include "tilesheet.h"

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
}

#include "nu.h"

const unsigned short int TNPORT = 27945, TMPORT0 = 41000;

void tic_api_cmxmul4(tic_mem *mem, float a[4][4], float b[4][4], float c[4][4]){
	c[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0] + a[0][3] * b[3][0];
	c[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1] + a[0][3] * b[3][1];
	c[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2] + a[0][3] * b[3][2];
	c[0][3] = a[0][0] * b[0][3] + a[0][1] * b[1][3] + a[0][2] * b[2][3] + a[0][3] * b[3][3];
	c[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0] + a[1][3] * b[3][0];
	c[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1] + a[1][3] * b[3][1];
	c[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2] + a[1][3] * b[3][2];
	c[1][3] = a[1][0] * b[0][3] + a[1][1] * b[1][3] + a[1][2] * b[2][3] + a[1][3] * b[3][3];
	c[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0] + a[2][3] * b[3][0];
	c[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1] + a[2][3] * b[3][1];
	c[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2] + a[2][3] * b[3][2];
	c[2][3] = a[2][0] * b[0][3] + a[2][1] * b[1][3] + a[2][2] * b[2][3] + a[2][3] * b[3][3];
	c[3][0] = a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + a[3][3] * b[3][0];
	c[3][1] = a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + a[3][3] * b[3][1];
	c[3][2] = a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + a[3][3] * b[3][2];
	c[3][3] = a[3][0] * b[0][3] + a[3][1] * b[1][3] + a[3][2] * b[2][3] + a[3][3] * b[3][3];
}
void tic_api_cfmxmul4(tic_mem *mem, float *A, float *B, float *C){

}

void tic_api_cvmxmul4(tic_mem *mem, float *a, float *b, float *c){
	c[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
	c[1] = a[4] * b[0] + a[5] * b[1] + a[6] * b[2] + a[7] * b[3];
	c[2] = a[8] * b[0] + a[9] * b[1] + a[10] * b[2] + a[11] * b[3];
	c[3] = a[12] * b[0] + a[13] * b[1] + a[14] * b[2] + a[15] * b[3];
}
void tic_api_cfvmxmul4(tic_mem *mem, float *a, float *b, float *c){
	tic_api_cvmxmul4(mem, a, b, c);
}

void tic_api_cmxmul(tic_mem *mem, float *a, float *b, float *c, u16 c1, u16 r1, u16 c2, u16 r2){
	if(r2!=c1){
		return;
	}
	for(int i = 0; i < r1; i++){
		for(int j = 0; j < c2; j++){
			c[i*c2 + j] = 0;
			for(int k = 0; k < c1; k++){
				c[i*c2+j] += a[i*c1+k] * b[k*c2+j];
			}

		}
	}
}

static enum TexTriDrawMode{
	SimpleMode = 0,

	PerspectiveCorrectMode = 1<<0,

	DepthBufferMode = 1<<1,

	MatrixMode = 1<<2,

	ScriptShaderMode = 1<<3,

	NormalizeMode = 1<<4,

	TicShadingUnitMode = 1<<5,
};
typedef s8(*ScriptShaderFunc)(void*data, int mode, s8 color, s32 x, s32 y, float u, float v, float z);

static struct{
	float mat[16];
} tmatrix;

static tic_tilesheet getTileSheetFromSegment(tic_mem* memory, u8 segment){
	u8* src;
	switch(segment){
		case 0:
		case 1:
			src = (u8*)&memory->ram.font.data; break;
		default:
			src = (u8*)&memory->ram.tiles.data; break;
	}

	return tic_tilesheet_get(segment, src);
}

static u8* getPalette(tic_mem* tic, u8* colors, u8 count){
	static u8 mapping[TIC_PALETTE_SIZE];
	for(s32 i = 0; i < TIC_PALETTE_SIZE; i++) mapping[i] = tic_tool_peek4(tic->ram.vram.mapping, i);
	for(s32 i = 0; i < count; i++) mapping[colors[i]] = 255;
	return mapping;
}

static inline u8 mapColor(tic_mem* tic, u8 color){
	return tic_tool_peek4(tic->ram.vram.mapping, color & 0xf);
}

static void setPixel(tic_core* core, s32 x, s32 y, u8 color){
	if(x < core->state.clip.l || y < core->state.clip.t || x >= core->state.clip.r || y >= core->state.clip.b) return;

	core->state.setpix(&core->memory, x, y, color);
}

static u8 getPixel(tic_core* core, s32 x, s32 y){
	if(x < 0 || y < 0 || x >= TIC80_WIDTH || y >= TIC80_HEIGHT) return 0;

	return core->state.getpix(&core->memory, x, y);
}

#define EARLY_CLIP(x, y, width, height) \
    ( \
        (((y)+(height)-1) < core->state.clip.t) \
        || (((x)+(width)-1) < core->state.clip.l) \
        || ((y) >= core->state.clip.b) \
        || ((x) >= core->state.clip.r) \
    )

static void drawHLine(tic_core* core, s32 x, s32 y, s32 width, u8 color){
	if(y < core->state.clip.t || core->state.clip.b <= y) return;

	s32 xl = MAX(x, core->state.clip.l);
	s32 xr = MIN(x + width, core->state.clip.r);

	core->state.drawhline(&core->memory, xl, xr, y, color);
}

static void drawVLine(tic_core* core, s32 x, s32 y, s32 height, u8 color){
	if(x < core->state.clip.l || core->state.clip.r <= x) return;

	s32 yl = y < 0 ? 0 : y;
	s32 yr = y + height >= TIC80_HEIGHT ? TIC80_HEIGHT : y + height;

	for(s32 i = yl; i < yr; ++i)
		setPixel(core, x, i, color);
}

static void drawRect(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color){
	for(s32 i = y; i < y + height; ++i)
		drawHLine(core, x, i, width, color);
}

static void drawRectBorder(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color){
	drawHLine(core, x, y, width, color);
	drawHLine(core, x, y + height - 1, width, color);

	drawVLine(core, x, y, height, color);
	drawVLine(core, x + width - 1, y, height, color);
}

#define DRAW_TILE_BODY(X, Y) do {\
    for(s32 py=sy; py < ey; py++, y++) \
    { \
        s32 xx = x; \
        for(s32 px=sx; px < ex; px++, xx++) \
        { \
            u8 color = mapping[tic_tilesheet_gettilepix(tile, (X), (Y))];\
            if(color != TRANSPARENT_COLOR) core->state.setpix(&core->memory, xx, y, color); \
        } \
    } \
    } while(0)
static struct{
	s16 Left[TIC80_HEIGHT];
	s16 Right[TIC80_HEIGHT];
	s32 ULeft[TIC80_HEIGHT];
	s32 VLeft[TIC80_HEIGHT];
	s32 RZ[TIC80_HEIGHT];
} SidesBuffer;
static float depthBuffer[TIC80_HEIGHT * TIC80_WIDTH];
static void initSidesBuffer(){
	for(s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++)
		SidesBuffer.Left[i] = TIC80_WIDTH, SidesBuffer.Right[i] = -1;
}
static void setSidePixel(s32 x, s32 y){
	if(y >= 0 && y < TIC80_HEIGHT){
		if(x < SidesBuffer.Left[y]) SidesBuffer.Left[y] = x;
		if(x > SidesBuffer.Right[y]) SidesBuffer.Right[y] = x;
	}
}

static void setSideTexPixel(s32 x, s32 y, float u, float v, float z){
	s32 yy = y;
	if(yy >= 0 && yy < TIC80_HEIGHT){
		if(x < SidesBuffer.Left[yy]){
			SidesBuffer.Left[yy] = x;
			SidesBuffer.ULeft[yy] = (s32)(u * 65536.0f);
			SidesBuffer.VLeft[yy] = (s32)(v * 65536.0f);
			SidesBuffer.RZ[yy] = (s32)(z * 65536.0f);
		}
		if(x > SidesBuffer.Right[yy]){
			SidesBuffer.Right[yy] = x;
		}
	}
}
typedef void(*linePixelFunc)(tic_mem* memory, s32 x, s32 y, u8 color);
static void ticLine(tic_mem* memory, s32 x0, s32 y0, s32 x1, s32 y1, u8 color, linePixelFunc func){
	if(y0 > y1){
		SWAP(x0, x1, s32);
		SWAP(y0, y1, s32);
	}

	s32 dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
	s32 dy = y1 - y0;
	s32 err = (dx > dy ? dx : -dy) / 2, e2;

	for(;;){
		func(memory, x0, y0, color);
		if(x0 == x1 && y0 == y1) break;
		e2 = err;
		if(e2 > -dx){ err -= dy; x0 += sx; }
		if(e2 < dy){ err += dx; y0++; }
	}
}

static void triPixelFunc(tic_mem* memory, s32 x, s32 y, u8 color){
	setSidePixel(x, y);
}
typedef struct{
	float x, y, u, v, z;
} TexVert;
inline static float lerp(float v0, float v1, float t){
	return (1 - t) * v0 + t * v1;
}
static float interpolate(float t, float i){

}
inline static float remap(float v, float a1, float b1, float a2, float b2){
	return (a2 + (v - a1) * (b2 - a2) / (b1 - a1));
};
static void ticTexLine(tic_mem* memory, TexVert* v0, TexVert* v1){
	TexVert* top = v0;
	TexVert* bot = v1;

	if(bot->y < top->y){
		top = v1;
		bot = v0;
	}

	float dy = bot->y - top->y;
	float step_x = (bot->x - top->x);
	float step_rz = (1/bot->z - 1/top->z);
	float step_u = (bot->u - top->u);
	float step_v = (bot->v - top->v);
	float step_uz = (bot->u / (bot->z) - top->u / (top->z));
	float step_vz = (bot->v / (bot->z) - top->v / (top->z));

	if((s32)dy != 0){
		step_x /= dy;
		step_u /= dy;
		step_v /= dy;
		step_uz /= dy;
		step_vz /= dy;
		step_rz /= dy;
	}

	float x = top->x;
	float y = top->y;
	float u = top->u;
	float v = top->v;
	float uz = u/top->z;
	float vz = v/top->z;
	float rz = 1/top->z;

	if(y < .0f){
		y = .0f - y;

		x += step_x * y;
		u += step_u * y;
		v += step_v * y;
		uz += step_uz * y;
		vz += step_vz * y;
		rz += step_rz * y;

		y = .0f;
	}

	s32 botY = (s32)bot->y;
	if(botY > TIC80_HEIGHT)
		botY = TIC80_HEIGHT;
	u = lerp(top->u, bot->u, 0); v = lerp(top->v, bot->v, 0);

	for(; y < botY; ++y){
		setSideTexPixel((s32)x, (s32)y, uz, vz, rz);
		x += step_x;
		uz += step_uz;
		vz += step_vz;
		rz += step_rz;

	}
}
static void drawTexturedTriangle(tic_core* core, int mode, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, float dnear = 0, float dfar = 100, ScriptShaderFunc ssf = nullptr, void *ssfData = nullptr, bool norm = false, float *matrix = nullptr){
	tic_mem* memory = &core->memory;
	u8* mapping = getPalette(memory, colors, count);
	TexVert V0, V1, V2;

	const u8* map = memory->ram.map.data;
	tic_tilesheet sheet = getTileSheetFromSegment(memory, memory->ram.vram.blit.segment);

	if(mode & MatrixMode && matrix){
		float vecs[3][4] = {
			{x1, y1, d1, 1.0},
			{x2, y2, d2, 1.0},
			{x3, y3, d3, 1.0},
		};
		float trans[3][4];
		tic_api_cvmxmul4(memory, matrix, vecs[0], trans[0]);
		tic_api_cvmxmul4(memory, matrix, vecs[1], trans[1]);
		tic_api_cvmxmul4(memory, matrix, vecs[2], trans[2]);
		float w;
		w = trans[0][3];
		x1 = trans[0][0] / w; y1 = trans[0][1] / w; d1 = trans[0][2] / w;
		x2 = trans[1][0] / w; y2 = trans[1][1] / w; d2 = trans[1][2] / w;
		x3 = trans[2][0] / w; y3 = trans[2][1] / w; d3 = trans[2][2] / w;
	}
	if(mode & NormalizeMode){
		V0.x = remap(x1, -1.0, +1.0, 0, 239);  V0.y = remap(y1, -1.0, +1.0, 0, 135);  V0.u = u1;  V0.v = v1; V0.z = d1 ? d1 : 1;
		V1.x = remap(x2, -1.0, +1.0, 0, 239);  V1.y = remap(y2, -1.0, +1.0, 0, 135);  V1.u = u2;  V1.v = v2; V1.z = d2 ? d2 : 1;
		V2.x = remap(x3, -1.0, +1.0, 0, 239);  V2.y = remap(y3, -1.0, +1.0, 0, 135);  V2.u = u3;  V2.v = v3; V2.z = d3 ? d3 : 1;
	}
	else{
	V0.x = x1;  V0.y = y1;  V0.u = u1;  V0.v = v1; V0.z = d1 ? d1 : 1;
	V1.x = x2;  V1.y = y2;  V1.u = u2;  V1.v = v2; V1.z = d2 ? d2 : 1;
	V2.x = x3;  V2.y = y3;  V2.u = u3;  V2.v = v3; V2.z = d3 ? d3 : 1;
	}

	float denom = (V0.x - V2.x) * (V1.y - V2.y) - (V1.x - V2.x) * (V0.y - V2.y);
	if(denom == 0.0){
		return;
	}
	float id = 1.0f / denom;
	float dudx, dvdx, dzdx;

	dudx = ((V0.u/V0.z - V2.u/V2.z) * (V1.y - V2.y) - (V1.u/V1.z - V2.u/V2.z) * (V0.y - V2.y)) * id;
	dvdx = ((V0.v/V0.z - V2.v/V2.z) * (V1.y - V2.y) - (V1.v/V1.z - V2.v/V2.z) * (V0.y - V2.y)) * id;
	dzdx = ((1./V0.z - 1./V2.z) * (V1.y - V2.y) - (1./V1.z - 1./V2.z) * (V0.y - V2.y)) * id;

	s32 dudxs = (s32)(dudx * 65536.0f);
	s32 dvdxs = (s32)(dvdx * 65536.0f);
	s32 dzdxs = (s32)(dzdx * 65536.0f);

	initSidesBuffer();

	ticTexLine(memory, &V0, &V1);
	ticTexLine(memory, &V1, &V2);
	ticTexLine(memory, &V2, &V0);

	for(s32 y = 0; y < TIC80_HEIGHT; y++){

		s32 width = SidesBuffer.Right[y] - SidesBuffer.Left[y];

		if((y < core->state.clip.t) || (y > core->state.clip.b))
			width = 0;
		if(width > 0){
			s32 u = SidesBuffer.ULeft[y];
			s32 v = SidesBuffer.VLeft[y];
			s32 rz = SidesBuffer.RZ[y];
			s32 left = SidesBuffer.Left[y];
			s32 right = SidesBuffer.Right[y];

			if(right > core->state.clip.r)
				right = core->state.clip.r;

			if(left < core->state.clip.l){
				s32 dist = core->state.clip.l - SidesBuffer.Left[y];
				u += dudxs * dist;
				v += dvdxs * dist;
				rz += dzdxs * dist;
				left = core->state.clip.l;
			}

			if(use_map == true){
				for(s32 x = left; x < right; ++x){
					enum{ MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE };
					s32 iu = (u >> 16) % MapWidth;
					s32 iv = (v >> 16) % MapHeight;

					while(iu < 0) iu += MapWidth;
					while(iv < 0) iv += MapHeight;

					u8 tileindex = map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)];
					tic_tileptr tile = tic_tilesheet_gettile(&sheet, tileindex, true);

					u8 color = mapping[tic_tilesheet_gettilepix(&tile, iu & 7, iv & 7)];
					if(color != 255)
						setPixel(core, x, y, color);
					u += dudxs;
					v += dvdxs;
				}
			}
			else{

				for(s32 x = left; x < right; ++x){
					if(((rz < dnear/65535 || 65536./rz < dnear) && (rz > dfar / 65535 || 65536. / rz > dfar)) || rz < 0 || (mode&DepthBufferMode ? depthBuffer[y*TIC80_WIDTH+x] < 65535.0/rz : false)){
						u += dudxs;
						v += dvdxs;
						rz += dzdxs;
						continue;
					}
					enum{ SheetWidth = TIC_SPRITESHEET_SIZE, SheetHeight = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS };
					s32 iu = ((s32)(u/(rz?rz/65536.:1)) >> 16) & (SheetWidth - 1);
					s32 iv = ((s32)(v/((rz?rz/65536.:1))) >> 16) & (SheetHeight - 1);

					u8 color = mapping[tic_tilesheet_getpix(&sheet, iu, iv)];
					if(mode & ScriptShaderMode && ssf && ssfData){
						color = ssf(ssfData, mode, color, x, y, (u/(rz?rz/65536.:1)/(1<<16)), (v/(rz?rz/65536.:1)/(1<<16)), 65536. / rz);
					}
					if(color != 255){
						setPixel(core, x, y, color);
						depthBuffer[y*TIC80_WIDTH + x] = 65535.0/rz;
					}
					u += dudxs;
					v += dvdxs;
					rz += dzdxs;

				}
			}
		}
	}
}

void tic_api_ptextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, float dclip){
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip, 10000);
}

tic_shader tic_api_shader(tic_mem *mem, const int *opcodes, const char *src, int isCode){
	tic_core *tic = (tic_core*)mem;
	tic_shader shader;
	return shader;
}

void tic_api_stextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8* colors, s32 count, tic_shader *shader){
	tic_core *tic = (tic_core*)mem;

}

void tic_api_sptextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, tic_shader *shader){
	tic_core *tic = (tic_core*)mem;

}

void tic_api_pdtextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8 *colors, s32 count, float dclip){
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode | DepthBufferMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip);
}

void tic_api_dclear(tic_mem *mem){
	tic_core *tic = (tic_core*)mem;
	for(size_t i = 0; i < TIC80_HEIGHT*TIC80_WIDTH; i++){
		depthBuffer[i] = +90000;
	}
}

void tic_api_pdftextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8 *colors, s32 count, float dclip, float dfar, ScriptShaderFunc func, void *data){
	if(func == NULL || data == NULL){
		tic_api_pdtextri(mem, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip);
		return;
	}
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode | DepthBufferMode | ScriptShaderMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip, dfar, func, data);
}

void tic_api_utextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, float dnear, float dfar, bool use_map, u8 *colors, s32 count, bool norm, float *matx, ScriptShaderFunc ssf, void *ssfData){
	drawTexturedTriangle((tic_core*)mem, 0
						 | (d1!=d2||d2!=d3?PerspectiveCorrectMode:0) | (dnear>=0&&dfar>0?DepthBufferMode:0)
						 | (ssf!=nullptr&&ssfData!=nullptr?ScriptShaderMode:0)
						 | (norm?NormalizeMode:0) | (matx!=nullptr ? MatrixMode : 0),
						 x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3,
						 use_map, colors, count, dnear, dfar, ssf, ssfData, norm, matx);
}

float tic_api_rttri(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float *res){

	float v1v0x = x2 - x1, v1v0y = y2-y1, v1v0z = z2-z1;

	float v2v0x = x3 - x1, v2v0y = y3 - y1, v2v0z = z3 - z1;

	float rov0x = rx - x1, rov0y = ry- y1, rov0z = rz - z1;

	float nx = v1v0y * v2v0z - v1v0z * v2v0y, ny = v1v0z * v2v0x - v1v0x * v2v0z, nz = v1v0x * v2v0y - v1v0y * v2v0x;

	float qx = rov0y*dz-rov0z*dy, qy = rov0z*dx-rov0x*dz, qz = rov0x*dy-rov0y*dx;

	float d = 1.0 / (dx * nx + dy * ny + dz * nz);

	float u = d * (-qx * v2v0x + -qy * v2v0y + -qz * v2v0z);

	float v = d * (qx * v1v0x + qy * v1v0y + qz * v1v0z);

	float t = d * (-nx * rov0x + -ny * rov0y + -nz * rov0z);
	if(u<0.0 || u>1.0 || v<0.0 || (u + v)>1.0) t = -1.0;

	res[0] = t; res[1] = u; res[2] = v;
	return t;
}

float tic_api_rtsphr(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x, float y, float z, float rad, float *res){

	float oc1, oc2, oc3;
	oc1 = rx - x; oc2 = ry - y; oc3 = rz - z;

	float b = oc1 * dx + oc2 * dy + oc3 * dz;

	float c = (oc1*oc1+oc2*oc2+oc3*oc3) - (rad * rad);

	float h = b * b - c;

	if(h < 0.0){
		res[0] = res[1] = -1.0;
		return -1.0; 
	}

	h = sqrt(h);

	res[0] = -b - h; res[1] = -b + h;
	return (-b - h);
}

float tic_api_rtcube(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x, float y, float z, float sx, float sy, float sz, float *res, float *norm){

	float mx = 1.0 / dx, my = 1.0 / dy, mz = 1.0 / dz;

	float nx = mx * (rx-x), ny = my * (ry-y), nz = mz * (rz-z);

	float kx = fabs(mx) * sx, ky = fabs(my) * sy, kz = fabs(mz) * sz;

	float t1x = -nx - kx, t1y = -ny - ky, t1z = -nz - kz;

	float t2x = -nx + kx, t2y = -ny + ky, t2z = -nz + kz;

	float tN = fmax(fmax(t1x, t1y), t1z);
	float tF = fmin(fmin(t2x, t2y), t2z);

	if(tN > tF || tF < 0.0){ res[0]=res[1]=-1.0; return -1.0; };

	res[2] = -signbit(dx)*((t1y > t1x) ? 1 : 0)*((t1z > t1x) ? 1 : 0);
	res[3] = -signbit(dy)*((t1z > t1y) ? 1 : 0)*((t1x > t1y) ? 1 : 0);
	res[4] = -signbit(dz)*((t1x > t1z) ? 1 : 0)*((t1y > t1z) ? 1 : 0);

	res[0] = tN; res[1] = tF;
	return tN;
}

enum MessageType{
	MesNull,
	MesPing, 
	MesConnect, 
	MesDisconnect, 
	MesExit, 
	MesTicPacket, 
	MesUpdate, 
	MesPong, 
	MesTicComplex, 
};

enum DataType{
	DataNull,
	DataInteger,
	DataFloat,
	DataString,
	DataBoolean,
	DataArrayInt,
	DataArrayFloat,
};

#pragma pack(push)
struct PacketHeader{
	u8 type;
	u8 version = 1;
};
struct PacketPing{
	PacketHeader head;
};
struct PacketConnect{
	PacketHeader head;
	unsigned short wantId = 0; 

};
struct PacketDisconnect{
	PacketHeader head;
};
struct PacketExit{
	PacketHeader head;
};
struct PacketTicPacket{
	PacketHeader head;
	u16 src, dst;
	u8 type;

	u16 appsize, keysize;
	unsigned short size;
	u8 data[1];
};
struct PacketUpdate{
	PacketHeader head;
	unsigned short newId = 0; 
};
struct PacketPong{
	PacketHeader head;
};
#pragma pack(pop)
typedef PacketHeader PacketHeader;
typedef PacketTicPacket PacketTicPacket;

const uint_least32_t Crc32Table[256] = {
	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
static inline uint_least32_t Crc32(const unsigned char *buf, size_t len){
	uint_least32_t crc = 0xFFFFFFFF;
	while(len--)
		crc = (crc >> 8) ^ Crc32Table[(crc ^ *buf++) & 0xFF];
	return crc ^ 0xFFFFFFFF;
}

static u32 ticnetMasterIp = 0;
static u32 ticnetState = 0;
static const int netbufferSize = 32 * 1024 * 4;
static struct{
	int read = 0, write = 0;
	bool full = false;
	u8 buffer[netbufferSize] = {0};
} netbuffer;
bool ring_empty(){
	return !netbuffer.full && netbuffer.read == netbuffer.write;
}
int ring_free(){
	if(netbuffer.write >= netbuffer.read)
		return ((netbufferSize-1) - (netbuffer.write - netbuffer.read));
	else
		return (netbuffer.read - netbuffer.write - 1);
}
bool ring_write(void *data, unsigned int size){
	if(ring_free() < size){
		return false;
	}
	for(size_t i = 0; i < size; i++){
		netbuffer.buffer[(netbuffer.write++)] = ((u8*)data)[i];
		netbuffer.write %= netbufferSize;
	}
	return true;
}
bool ring_read(void *data, unsigned int size){
	if(netbufferSize < size || (netbufferSize - ring_free()) < size){
		return false;
	}
	for(size_t i = 0; i < size; i++){
		((u8*)data)[i] = netbuffer.buffer[netbuffer.read++];
		netbuffer.read %= netbufferSize;
	}
	return true;
}

short tic_core_ticnet_htons(short val){
	return nu_htons(val);
}
short tic_core_ticnet_ntohs(short val){
	return nu_ntohs(val);
}
long tic_core_ticnet_htonl(long val){
	return nu_htonl(val);
}
long tic_core_ticnet_ntohl(long val){
	return nu_ntohl(val);
}

void tic_core_ticnet_init(tic_mem *mem, u32 ip){
	tic_core *core = (tic_core*)mem;
	nu_init();
	if(ip && isNetutilitySupport()){
		volatile int err;
		core->ticnet_socket = nu_socket();
		nu_bind(core->ticnet_socket, rand()%64+(TMPORT0+1));
		err = nu_geterror();
		ticnetMasterIp = (ip);
		PacketConnect packet;
		packet.head.type = MesConnect;
		packet.head.version = 1;
		nu_sendto(core->ticnet_socket, ip, TMPORT0, (void*)&packet, sizeof(PacketConnect), 0);
		err = nu_geterror();
	}
}
void tic_core_ticnet_deinit(tic_mem *mem){
	tic_core *core = (tic_core*)mem;
	if(ticnetMasterIp || ticnetState){
		PacketDisconnect packet;
		packet.head.type = MesDisconnect;
		packet.head.version = 1;
		nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)&packet, sizeof(PacketDisconnect), 0);
		nu_close(core->ticnet_socket);
	}
	nu_deinit();
}

u32 ticnet_inetaddr(const char *str){
	return nu_getip(str);
}

static void ticnetHandle(tic_core *core, PacketHeader *head, u32 ip, u16 port, u32 len);
static int ticnetLastPing = 0;

void tic_core_ticnet_update(tic_mem *mem){
	tic_core *core = (tic_core*)mem;
	if(!ticnetState && !ticnetMasterIp){
		return;
	}

	int currenttime = clock();
	if(ticnetState){
		if(ticnetLastPing + 10 * CLOCKS_PER_SEC < currenttime){
			ticnetLastPing = currenttime;
			PacketPing packet;
			packet.head.type = MesPing;
			packet.head.version = 1;

			nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)&packet, sizeof(PacketPing), 0);
		}
	}
	int err = 0;
	do{
		u32 addr;
		s32 len;
		u16 port;
		u8 buffer[2 * 1024];
		len = nu_recvfrom(core->ticnet_socket, &addr, &port, buffer, 2*1024, 0);
		err = ((len <= 0) ? -1 : 0);
		volatile int er = nu_geterror();
		if(err != -1){
			ticnetHandle(core, (PacketHeader*)buffer, addr, port, len);
		}
	} while(err != -1);
}

static void ticnetHandle(tic_core *core, PacketHeader *head, u32 ip, u16 port, u32 len){
	if(head->version != 1){
		return;
	}
	switch(head->type){

		case MesNull:
		case MesPing:
		case MesConnect:
		case MesDisconnect:
			break;
		case MesExit:
			ticnetMasterIp = 0;
			core->tmid = 0;
			ticnetState = 0;
			break;
		case MesTicPacket:
			{
				PacketTicPacket *packet = ((PacketTicPacket*)(head));
				u16 applen = nu_ntohs(packet->appsize), keylen = nu_ntohs(packet->keysize), datalen = nu_ntohs(packet->size);
				ring_write(head, sizeof(PacketTicPacket) + applen + keylen + datalen);
			}
			break;
		case MesUpdate:
			{
				PacketUpdate *upd = (PacketUpdate*)head;
				if(upd->newId == -1 || upd->newId == 2 || upd->newId == 3){
					ticnetState = 0;
					core->tmid = 0;
				}
				else if(upd->newId != 0){
					core->tmid = nu_ntohs(upd->newId);
					ticnetState = 1;
				}
			}
			break;
		case MesPong:

			break;
		case MesTicComplex:

			break;
		default:
			break;
	}
}

void tic_api_net(tic_mem *mem, u16 *tmid, u16 *arg2){
	tic_core *core = (tic_core*)mem;
	if(tmid){
		*tmid = core->tmid;
	}
}

void tic_api_nsend(tic_mem *mem, u16 id, const char *app, const char *key, u8 type, void *data, u16 size, u8 port){

	tic_core *core = (tic_core*)mem;
	if((!ticnetMasterIp || !core->tmid || !core->ticnet_socket || !ticnetState) && id != 2){
		return;
	}
	u8 buffer[2*1024];
	u16 keylen = strlen(key) + 1, applen = strlen(app) + 1;
	u16 sumSize = applen + keylen + size;
	if((applen + keylen + size) > (2*1024-128) && sumSize <= 3){
		return;
	}

	if((id < 4 && id != 2 && id != 1) || type > 8){
		return;
	}
	PacketTicPacket *packet = (PacketTicPacket*)buffer;
	packet->head.type = MesTicPacket;
	packet->head.version = 1;
	packet->src = nu_htons(core->tmid);
	packet->dst = nu_htons(id);

	packet->appsize = nu_htons(applen);
	packet->keysize = nu_htons(keylen);
	packet->type = type;
	packet->size = nu_htons(size);

	memcpy(buffer + sizeof(PacketTicPacket) + 0, app, applen);
	memcpy(buffer + sizeof(PacketTicPacket) + applen, key, keylen);
	memcpy(buffer + sizeof(PacketTicPacket) + applen + keylen, data, size);
	if(id == 2){
		ring_write(buffer, sizeof(PacketTicPacket) + applen + keylen + size);
		return;
	}
	nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)buffer, sizeof(PacketTicPacket) + applen + keylen + size, 0);
	return;
}

bool tic_api_nrecv(tic_mem *mem, u16 *id, char *app, char *key, u8 *type, void *data, u16 *size, u8 *port){
	tic_core *core = (tic_core*)mem;
	if(!ticnetMasterIp || !core->tmid || !core->ticnet_socket || !ticnetState){
		return 0;
	}
	bool ok = true;
	PacketTicPacket packet;
	u8 buffer[2 * 1024 - 128];
	u16 applen, keylen, datalen;
	ok = ok && ring_read(&packet, sizeof(PacketTicPacket));
	if(ok){ applen = nu_ntohs(packet.appsize), keylen = nu_ntohs(packet.keysize), datalen = nu_ntohs(packet.size); }
	ok = ok && ring_read(buffer, applen + keylen + datalen);
	if(!ok){
		netbuffer.read = netbuffer.write = 0;
		return 0;
	}
	*id = nu_ntohs(packet.src);
	*type = packet.type;

	strncpy(app, (const char*)(buffer + 0), applen);
	strncpy(key, (const char*)(buffer + applen), keylen);
	memcpy(data, (void*)(buffer + applen + keylen), datalen);
	*size = datalen;
	return 1;
}

void tic_api_nrpc(tic_mem *mem, u16 id, const char *app, const char *func, void *argv, u32 argc){

}
extern "C" {
#include "api.h"
#include "core.h"

#include "tilesheet.h"

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
}

#include "nu.h"


/*
#include <DirectXMath.h>
using namespace DirectX;
__forceinline void XM_CALLCONV XMMatrixMultiply_V0_InlExp2_Impl(CXMMATRIX M1, XMMATRIX& mResult, XMVECTOR M2R0, XMVECTOR M2R1, XMVECTOR M2R2, XMVECTOR M2R3){
	// Splat the component X,Y,Z then W
	XMVECTOR vX = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[0]) + 0);
	XMVECTOR vY = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[0]) + 1);
	XMVECTOR vZ = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[0]) + 2);
	XMVECTOR vW = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[0]) + 3);
	// Perform the operation on the first row
	vX = _mm_mul_ps(vX, M2R0);
	vY = _mm_mul_ps(vY, M2R1);
	vZ = _mm_mul_ps(vZ, M2R2);
	vW = _mm_mul_ps(vW, M2R3);
	// Perform a binary add to reduce cumulative errors
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[0] = vX;
	// Repeat for the other 3 rows
	vX = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[1]) + 0);
	vY = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[1]) + 1);
	vZ = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[1]) + 2);
	vW = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[1]) + 3);
	vX = _mm_mul_ps(vX, M2R0);
	vY = _mm_mul_ps(vY, M2R1);
	vZ = _mm_mul_ps(vZ, M2R2);
	vW = _mm_mul_ps(vW, M2R3);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[1] = vX;
	vX = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[2]) + 0);
	vY = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[2]) + 1);
	vZ = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[2]) + 2);
	vW = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[2]) + 3);
	vX = _mm_mul_ps(vX, M2R0);
	vY = _mm_mul_ps(vY, M2R1);
	vZ = _mm_mul_ps(vZ, M2R2);
	vW = _mm_mul_ps(vW, M2R3);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[2] = vX;
	vX = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[3]) + 0);
	vY = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[3]) + 1);
	vZ = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[3]) + 2);
	vW = _mm_broadcast_ss(reinterpret_cast<const float*>(&M1.r[3]) + 3);
	vX = _mm_mul_ps(vX, M2R0);
	vY = _mm_mul_ps(vY, M2R1);
	vZ = _mm_mul_ps(vZ, M2R2);
	vW = _mm_mul_ps(vW, M2R3);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[3] = vX;
}
inline XMMATRIX XM_CALLCONV XMMatrixMultiply_Ref_Reg(FXMMATRIX M1, CXMMATRIX M2){
	XMMATRIX mResult;
	// Use vW to hold the original row
	XMVECTOR vW = M1.r[0];
	// Splat the component X,Y,Z then W
	XMVECTOR vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0));
	XMVECTOR vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1));
	XMVECTOR vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2));
	vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3));
	// Perform the operation on the first row
	vX = _mm_mul_ps(vX, M2.r[0]);
	vY = _mm_mul_ps(vY, M2.r[1]);
	vZ = _mm_mul_ps(vZ, M2.r[2]);
	vW = _mm_mul_ps(vW, M2.r[3]);
	// Perform a binary add to reduce cumulative errors
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[0] = vX;
	// Repeat for the other 3 rows
	vW = M1.r[1];
	vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0));
	vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1));
	vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2));
	vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3));
	vX = _mm_mul_ps(vX, M2.r[0]);
	vY = _mm_mul_ps(vY, M2.r[1]);
	vZ = _mm_mul_ps(vZ, M2.r[2]);
	vW = _mm_mul_ps(vW, M2.r[3]);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[1] = vX;
	vW = M1.r[2];
	vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0));
	vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1));
	vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2));
	vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3));
	vX = _mm_mul_ps(vX, M2.r[0]);
	vY = _mm_mul_ps(vY, M2.r[1]);
	vZ = _mm_mul_ps(vZ, M2.r[2]);
	vW = _mm_mul_ps(vW, M2.r[3]);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[2] = vX;
	vW = M1.r[3];
	vX = XM_PERMUTE_PS(vW, _MM_SHUFFLE(0, 0, 0, 0));
	vY = XM_PERMUTE_PS(vW, _MM_SHUFFLE(1, 1, 1, 1));
	vZ = XM_PERMUTE_PS(vW, _MM_SHUFFLE(2, 2, 2, 2));
	vW = XM_PERMUTE_PS(vW, _MM_SHUFFLE(3, 3, 3, 3));
	vX = _mm_mul_ps(vX, M2.r[0]);
	vY = _mm_mul_ps(vY, M2.r[1]);
	vZ = _mm_mul_ps(vZ, M2.r[2]);
	vW = _mm_mul_ps(vW, M2.r[3]);
	vX = _mm_add_ps(vX, vZ);
	vY = _mm_add_ps(vY, vW);
	vX = _mm_add_ps(vX, vY);
	mResult.r[3] = vX;
	return mResult;
}
void tic_api_cfmxmul4(tic_mem *mem, float *A, float *B, float *C){
	XMMATRIX mat = XMMatrixMultiply_Ref_Reg(FXMMATRIX(A), XMMATRIX(B));
	C[0] = mat.r[0].m128_f32[0];
	C[1] = mat.r[0].m128_f32[1];
	C[2] = mat.r[0].m128_f32[2];
	C[3] = mat.r[0].m128_f32[3];
	C[0] = mat.r[1].m128_f32[0];
	C[1] = mat.r[1].m128_f32[1];
	C[2] = mat.r[1].m128_f32[2];
	C[3] = mat.r[1].m128_f32[3];
	C[0] = mat.r[2].m128_f32[0];
	C[1] = mat.r[2].m128_f32[1];
	C[2] = mat.r[2].m128_f32[2];
	C[3] = mat.r[2].m128_f32[3];
	C[0] = mat.r[3].m128_f32[0];
	C[1] = mat.r[3].m128_f32[1];
	C[2] = mat.r[3].m128_f32[2];
	C[3] = mat.r[3].m128_f32[3];
	return;
}
*/

//#include <xmmintrin.h>

// Net utility

//const int TNPORT = 8800;
const unsigned short int TNPORT = 27945, TMPORT0 = 41000;





void tic_api_cmxmul4(tic_mem *mem, float a[4][4], float b[4][4], float c[4][4]){
	c[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0] + a[0][3] * b[3][0];
	c[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1] + a[0][3] * b[3][1];
	c[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2] + a[0][3] * b[3][2];
	c[0][3] = a[0][0] * b[0][3] + a[0][1] * b[1][3] + a[0][2] * b[2][3] + a[0][3] * b[3][3];
	c[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0] + a[1][3] * b[3][0];
	c[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1] + a[1][3] * b[3][1];
	c[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2] + a[1][3] * b[3][2];
	c[1][3] = a[1][0] * b[0][3] + a[1][1] * b[1][3] + a[1][2] * b[2][3] + a[1][3] * b[3][3];
	c[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0] + a[2][3] * b[3][0];
	c[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1] + a[2][3] * b[3][1];
	c[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2] + a[2][3] * b[3][2];
	c[2][3] = a[2][0] * b[0][3] + a[2][1] * b[1][3] + a[2][2] * b[2][3] + a[2][3] * b[3][3];
	c[3][0] = a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + a[3][3] * b[3][0];
	c[3][1] = a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + a[3][3] * b[3][1];
	c[3][2] = a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + a[3][3] * b[3][2];
	c[3][3] = a[3][0] * b[0][3] + a[3][1] * b[1][3] + a[3][2] * b[2][3] + a[3][3] * b[3][3];
}
void tic_api_cfmxmul4(tic_mem *mem, float *A, float *B, float *C){
	//__m128 row1 = _mm_load_ps(&B[0]);
	//__m128 row2 = _mm_load_ps(&B[4]);
	//__m128 row3 = _mm_load_ps(&B[8]);
	//__m128 row4 = _mm_load_ps(&B[12]);
	//for(int i = 0; i < 4; i++){
	//	__m128 brod1 = _mm_set1_ps(A[4 * i + 0]);
	//	__m128 brod2 = _mm_set1_ps(A[4 * i + 1]);
	//	__m128 brod3 = _mm_set1_ps(A[4 * i + 2]);
	//	__m128 brod4 = _mm_set1_ps(A[4 * i + 3]);
	//	__m128 row = _mm_add_ps(
	//		_mm_add_ps(
	//			_mm_mul_ps(brod1, row1),
	//			_mm_mul_ps(brod2, row2)),
	//		_mm_add_ps(
	//			_mm_mul_ps(brod3, row3),
	//			_mm_mul_ps(brod4, row4)));
	//	_mm_store_ps(&C[4 * i], row);
	//}
	//tic_api_cmxmul4(mem, A, B, C);
}
// a-mat, b-vec, !!NOT REVERSE
void tic_api_cvmxmul4(tic_mem *mem, float *a, float *b, float *c){
	c[0] = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
	c[1] = a[4] * b[0] + a[5] * b[1] + a[6] * b[2] + a[7] * b[3];
	c[2] = a[8] * b[0] + a[9] * b[1] + a[10] * b[2] + a[11] * b[3];
	c[3] = a[12] * b[0] + a[13] * b[1] + a[14] * b[2] + a[15] * b[3];
}
void tic_api_cfvmxmul4(tic_mem *mem, float *a, float *b, float *c){
	tic_api_cvmxmul4(mem, a, b, c);
}

void tic_api_cmxmul(tic_mem *mem, float *a, float *b, float *c, u16 c1, u16 r1, u16 c2, u16 r2){
	if(r2!=c1){
		return;
	}
	for(int i = 0; i < r1; i++){
		for(int j = 0; j < c2; j++){
			c[i*c2 + j] = 0;
			for(int k = 0; k < c1/*r2*/; k++){
				c[i*c2+j] += a[i*c1+k] * b[k*c2+j];
			}
			/*printf("%d\n", matC[i][j]);*/
		}
	}
}

static enum TexTriDrawMode{
	SimpleMode = 0,
	// correct uv values
	PerspectiveCorrectMode = 1<<0,
	// depth test
	DepthBufferMode = 1<<1,
	// matrix transformation mode
	MatrixMode = 1<<2,
	// call lua func on every pixel draw for getting new color
	ScriptShaderMode = 1<<3,
	// normalize from [-1,+1] to [0,DISPLAY_SIZE)
	NormalizeMode = 1<<4,
	// tsl, tsu, not implement
	TicShadingUnitMode = 1<<5,
};
typedef s8(*ScriptShaderFunc)(void*data, int mode, s8 color, s32 x, s32 y, float u, float v, float z);

static struct{
	float mat[16];
} tmatrix;

static tic_tilesheet getTileSheetFromSegment(tic_mem* memory, u8 segment){
	u8* src;
	switch(segment){
		case 0:
		case 1:
			src = (u8*)&memory->ram.font.data; break;
		default:
			src = (u8*)&memory->ram.tiles.data; break;
	}

	return tic_tilesheet_get(segment, src);
}

static u8* getPalette(tic_mem* tic, u8* colors, u8 count){
	static u8 mapping[TIC_PALETTE_SIZE];
	for(s32 i = 0; i < TIC_PALETTE_SIZE; i++) mapping[i] = tic_tool_peek4(tic->ram.vram.mapping, i);
	for(s32 i = 0; i < count; i++) mapping[colors[i]] = 255;
	return mapping;
}

static inline u8 mapColor(tic_mem* tic, u8 color){
	return tic_tool_peek4(tic->ram.vram.mapping, color & 0xf);
}

static void setPixel(tic_core* core, s32 x, s32 y, u8 color){
	if(x < core->state.clip.l || y < core->state.clip.t || x >= core->state.clip.r || y >= core->state.clip.b) return;

	core->state.setpix(&core->memory, x, y, color);
}

static u8 getPixel(tic_core* core, s32 x, s32 y){
	if(x < 0 || y < 0 || x >= TIC80_WIDTH || y >= TIC80_HEIGHT) return 0;

	return core->state.getpix(&core->memory, x, y);
}

#define EARLY_CLIP(x, y, width, height) \
    ( \
        (((y)+(height)-1) < core->state.clip.t) \
        || (((x)+(width)-1) < core->state.clip.l) \
        || ((y) >= core->state.clip.b) \
        || ((x) >= core->state.clip.r) \
    )

static void drawHLine(tic_core* core, s32 x, s32 y, s32 width, u8 color){
	if(y < core->state.clip.t || core->state.clip.b <= y) return;

	s32 xl = MAX(x, core->state.clip.l);
	s32 xr = MIN(x + width, core->state.clip.r);

	core->state.drawhline(&core->memory, xl, xr, y, color);
}

static void drawVLine(tic_core* core, s32 x, s32 y, s32 height, u8 color){
	if(x < core->state.clip.l || core->state.clip.r <= x) return;

	s32 yl = y < 0 ? 0 : y;
	s32 yr = y + height >= TIC80_HEIGHT ? TIC80_HEIGHT : y + height;

	for(s32 i = yl; i < yr; ++i)
		setPixel(core, x, i, color);
}

static void drawRect(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color){
	for(s32 i = y; i < y + height; ++i)
		drawHLine(core, x, i, width, color);
}

static void drawRectBorder(tic_core* core, s32 x, s32 y, s32 width, s32 height, u8 color){
	drawHLine(core, x, y, width, color);
	drawHLine(core, x, y + height - 1, width, color);

	drawVLine(core, x, y, height, color);
	drawVLine(core, x + width - 1, y, height, color);
}

#define DRAW_TILE_BODY(X, Y) do {\
    for(s32 py=sy; py < ey; py++, y++) \
    { \
        s32 xx = x; \
        for(s32 px=sx; px < ex; px++, xx++) \
        { \
            u8 color = mapping[tic_tilesheet_gettilepix(tile, (X), (Y))];\
            if(color != TRANSPARENT_COLOR) core->state.setpix(&core->memory, xx, y, color); \
        } \
    } \
    } while(0)
static struct{
	s16 Left[TIC80_HEIGHT];
	s16 Right[TIC80_HEIGHT];
	s32 ULeft[TIC80_HEIGHT];
	s32 VLeft[TIC80_HEIGHT];
	s32 RZ[TIC80_HEIGHT];
} SidesBuffer;
static float depthBuffer[TIC80_HEIGHT * TIC80_WIDTH];
static void initSidesBuffer(){
	for(s32 i = 0; i < COUNT_OF(SidesBuffer.Left); i++)
		SidesBuffer.Left[i] = TIC80_WIDTH, SidesBuffer.Right[i] = -1;
}
static void setSidePixel(s32 x, s32 y){
	if(y >= 0 && y < TIC80_HEIGHT){
		if(x < SidesBuffer.Left[y]) SidesBuffer.Left[y] = x;
		if(x > SidesBuffer.Right[y]) SidesBuffer.Right[y] = x;
	}
}

static void setSideTexPixel(s32 x, s32 y, float u, float v, float z){
	s32 yy = y;
	if(yy >= 0 && yy < TIC80_HEIGHT){
		if(x < SidesBuffer.Left[yy]){
			SidesBuffer.Left[yy] = x;
			SidesBuffer.ULeft[yy] = (s32)(u * 65536.0f);
			SidesBuffer.VLeft[yy] = (s32)(v * 65536.0f);
			SidesBuffer.RZ[yy] = (s32)(z * 65536.0f);
		}
		if(x > SidesBuffer.Right[yy]){
			SidesBuffer.Right[yy] = x;
		}
	}
}
typedef void(*linePixelFunc)(tic_mem* memory, s32 x, s32 y, u8 color);
static void ticLine(tic_mem* memory, s32 x0, s32 y0, s32 x1, s32 y1, u8 color, linePixelFunc func){
	if(y0 > y1){
		SWAP(x0, x1, s32);
		SWAP(y0, y1, s32);
	}

	s32 dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
	s32 dy = y1 - y0;
	s32 err = (dx > dy ? dx : -dy) / 2, e2;

	for(;;){
		func(memory, x0, y0, color);
		if(x0 == x1 && y0 == y1) break;
		e2 = err;
		if(e2 > -dx){ err -= dy; x0 += sx; }
		if(e2 < dy){ err += dx; y0++; }
	}
}

static void triPixelFunc(tic_mem* memory, s32 x, s32 y, u8 color){
	setSidePixel(x, y);
}
typedef struct{
	float x, y, u, v, z;
} TexVert;
inline static float lerp(float v0, float v1, float t){
	return (1 - t) * v0 + t * v1;
}
static float interpolate(float t, float i){

}
inline static float remap(float v, float a1, float b1, float a2, float b2){
	return (a2 + (v - a1) * (b2 - a2) / (b1 - a1));
};
static void ticTexLine(tic_mem* memory, TexVert* v0, TexVert* v1){
	TexVert* top = v0;
	TexVert* bot = v1;

	if(bot->y < top->y){
		top = v1;
		bot = v0;
	}

	float dy = bot->y - top->y;
	float step_x = (bot->x - top->x);
	float step_rz = (1/bot->z - 1/top->z);
	float step_u = (bot->u - top->u);
	float step_v = (bot->v - top->v);
	float step_uz = (bot->u / (bot->z) - top->u / (top->z));
	float step_vz = (bot->v / (bot->z) - top->v / (top->z));

	if((s32)dy != 0){
		step_x /= dy;
		step_u /= dy;
		step_v /= dy;
		step_uz /= dy;
		step_vz /= dy;
		step_rz /= dy;
	}

	float x = top->x;
	float y = top->y;
	float u = top->u;
	float v = top->v;
	float uz = u/top->z;
	float vz = v/top->z;
	float rz = 1/top->z;

	if(y < .0f){
		y = .0f - y;

		x += step_x * y;
		u += step_u * y;
		v += step_v * y;
		uz += step_uz * y;
		vz += step_vz * y;
		rz += step_rz * y;

		y = .0f;
	}

	s32 botY = (s32)bot->y;
	if(botY > TIC80_HEIGHT)
		botY = TIC80_HEIGHT;
	u = lerp(top->u, bot->u, 0); v = lerp(top->v, bot->v, 0);
	//rz = lerp(1/top->z, 1/bot->z, 0);
	//uz = lerp(top->u / top->z, bot->u / bot->z, 0);
	//vz = lerp(top->v / top->z, bot->v / bot->z, 0);
	for(; y < botY; ++y){
		setSideTexPixel((s32)x, (s32)y, uz, vz, rz);
		x += step_x;
		uz += step_uz;
		vz += step_vz;
		rz += step_rz;
		//u = lerp(top->u, bot->u, (y-top->y)/dy); v = lerp(top->v, bot->v, (y-top->y)/dy);
		//rz = lerp(1 / top->z, 1 / bot->z, (y - top->y) / dy);
		//uz = lerp(top->u / top->z, bot->u / bot->z, (y - top->y) / dy);
		//vz = lerp(top->v / top->z, bot->v / bot->z, (y - top->y) / dy);
		//uz = lerp(top->u * rz, bot->u * rz, (y - top->y) / dy);
		//vz = lerp(top->v * rz, bot->v * rz, (y - top->y) / dy);
		//uz = u * rz; vz = v * rz;
	}
}
static void drawTexturedTriangle(tic_core* core, int mode, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, float dnear = 0, float dfar = 100, ScriptShaderFunc ssf = nullptr, void *ssfData = nullptr, bool norm = false, float *matrix = nullptr){
	tic_mem* memory = &core->memory;
	u8* mapping = getPalette(memory, colors, count);
	TexVert V0, V1, V2;

	const u8* map = memory->ram.map.data;
	tic_tilesheet sheet = getTileSheetFromSegment(memory, memory->ram.vram.blit.segment);

	if(mode & MatrixMode && matrix){
		float vecs[3][4] = {
			{x1, y1, d1, 1.0},
			{x2, y2, d2, 1.0},
			{x3, y3, d3, 1.0},
		};
		float trans[3][4];
		tic_api_cvmxmul4(memory, matrix, vecs[0], trans[0]);
		tic_api_cvmxmul4(memory, matrix, vecs[1], trans[1]);
		tic_api_cvmxmul4(memory, matrix, vecs[2], trans[2]);
		float w;
		w = trans[0][3];
		x1 = trans[0][0] / w; y1 = trans[0][1] / w; d1 = trans[0][2] / w;
		x2 = trans[1][0] / w; y2 = trans[1][1] / w; d2 = trans[1][2] / w;
		x3 = trans[2][0] / w; y3 = trans[2][1] / w; d3 = trans[2][2] / w;
	}
	if(mode & NormalizeMode){
		V0.x = remap(x1, -1.0, +1.0, 0, 239);  V0.y = remap(y1, -1.0, +1.0, 0, 135);  V0.u = u1;  V0.v = v1; V0.z = d1 ? d1 : 1;
		V1.x = remap(x2, -1.0, +1.0, 0, 239);  V1.y = remap(y2, -1.0, +1.0, 0, 135);  V1.u = u2;  V1.v = v2; V1.z = d2 ? d2 : 1;
		V2.x = remap(x3, -1.0, +1.0, 0, 239);  V2.y = remap(y3, -1.0, +1.0, 0, 135);  V2.u = u3;  V2.v = v3; V2.z = d3 ? d3 : 1;
	}
	else{
	V0.x = x1;  V0.y = y1;  V0.u = u1;  V0.v = v1; V0.z = d1 ? d1 : 1;
	V1.x = x2;  V1.y = y2;  V1.u = u2;  V1.v = v2; V1.z = d2 ? d2 : 1;
	V2.x = x3;  V2.y = y3;  V2.u = u3;  V2.v = v3; V2.z = d3 ? d3 : 1;
	}
	

	//  calculate the slope of the surface 
	//  use floats here 
	float denom = (V0.x - V2.x) * (V1.y - V2.y) - (V1.x - V2.x) * (V0.y - V2.y);
	if(denom == 0.0){
		return;
	}
	float id = 1.0f / denom;
	float dudx, dvdx, dzdx;
	//  this is the UV slope across the surface
	dudx = ((V0.u/V0.z - V2.u/V2.z) * (V1.y - V2.y) - (V1.u/V1.z - V2.u/V2.z) * (V0.y - V2.y)) * id;
	dvdx = ((V0.v/V0.z - V2.v/V2.z) * (V1.y - V2.y) - (V1.v/V1.z - V2.v/V2.z) * (V0.y - V2.y)) * id;
	dzdx = ((1./V0.z - 1./V2.z) * (V1.y - V2.y) - (1./V1.z - 1./V2.z) * (V0.y - V2.y)) * id;
	//  convert to fixed
	s32 dudxs = (s32)(dudx * 65536.0f);
	s32 dvdxs = (s32)(dvdx * 65536.0f);
	s32 dzdxs = (s32)(dzdx * 65536.0f);
	//  fill the buffer 
	initSidesBuffer();
	//  parse each line and decide where in the buffer to store them ( left or right ) 
	ticTexLine(memory, &V0, &V1);
	ticTexLine(memory, &V1, &V2);
	ticTexLine(memory, &V2, &V0);

	for(s32 y = 0; y < TIC80_HEIGHT; y++){
		//  if it's backwards skip it
		s32 width = SidesBuffer.Right[y] - SidesBuffer.Left[y];
		//  if it's off top or bottom , skip this line
		if((y < core->state.clip.t) || (y > core->state.clip.b))
			width = 0;
		if(width > 0){
			s32 u = SidesBuffer.ULeft[y];
			s32 v = SidesBuffer.VLeft[y];
			s32 rz = SidesBuffer.RZ[y];
			s32 left = SidesBuffer.Left[y];
			s32 right = SidesBuffer.Right[y];
			//  check right edge, and CLAMP it
			if(right > core->state.clip.r)
				right = core->state.clip.r;
			//  check left edge and offset UV's if we are off the left 
			if(left < core->state.clip.l){
				s32 dist = core->state.clip.l - SidesBuffer.Left[y];
				u += dudxs * dist;
				v += dvdxs * dist;
				rz += dzdxs * dist;
				left = core->state.clip.l;
			}
			//  are we drawing from the map . ok then at least check before the inner loop
			if(use_map == true){
				for(s32 x = left; x < right; ++x){
					enum{ MapWidth = TIC_MAP_WIDTH * TIC_SPRITESIZE, MapHeight = TIC_MAP_HEIGHT * TIC_SPRITESIZE };
					s32 iu = (u >> 16) % MapWidth;
					s32 iv = (v >> 16) % MapHeight;

					while(iu < 0) iu += MapWidth;
					while(iv < 0) iv += MapHeight;

					u8 tileindex = map[(iv >> 3) * TIC_MAP_WIDTH + (iu >> 3)];
					tic_tileptr tile = tic_tilesheet_gettile(&sheet, tileindex, true);

					u8 color = mapping[tic_tilesheet_gettilepix(&tile, iu & 7, iv & 7)];
					if(color != 255)
						setPixel(core, x, y, color);
					u += dudxs;
					v += dvdxs;
				}
			}
			else{
				//  direct from tile ram 
				for(s32 x = left; x < right; ++x){
					if(((rz < dnear/65535 || 65536./rz < dnear) && (rz > dfar / 65535 || 65536. / rz > dfar)) || rz < 0 || (mode&DepthBufferMode ? depthBuffer[y*TIC80_WIDTH+x] < 65535.0/rz : false)){
						u += dudxs;
						v += dvdxs;
						rz += dzdxs;
						continue;
					}
					enum{ SheetWidth = TIC_SPRITESHEET_SIZE, SheetHeight = TIC_SPRITESHEET_SIZE * TIC_SPRITE_BANKS };
					s32 iu = ((s32)(u/(rz?rz/65536.:1)) >> 16) & (SheetWidth - 1);
					s32 iv = ((s32)(v/((rz?rz/65536.:1))) >> 16) & (SheetHeight - 1);

					u8 color = mapping[tic_tilesheet_getpix(&sheet, iu, iv)];
					if(mode & ScriptShaderMode && ssf && ssfData){
						color = ssf(ssfData, mode, color, x, y, (u/(rz?rz/65536.:1)/(1<<16)), (v/(rz?rz/65536.:1)/(1<<16)), 65536. / rz);
					}
					if(color != 255){
						setPixel(core, x, y, color);
						depthBuffer[y*TIC80_WIDTH + x] = 65535.0/rz;
					}
					u += dudxs;
					v += dvdxs;
					rz += dzdxs;
					//rz = lerp();
				}
			}
		}
	}
}

void tic_api_ptextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, float dclip){
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip, 10000);
}

tic_shader tic_api_shader(tic_mem *mem, const int *opcodes, const char *src, int isCode){
	tic_core *tic = (tic_core*)mem;
	tic_shader shader;
	return shader;
}

void tic_api_stextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, bool use_map, u8* colors, s32 count, tic_shader *shader){
	tic_core *tic = (tic_core*)mem;

}

void tic_api_sptextri(tic_mem *mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8* colors, s32 count, tic_shader *shader){
	tic_core *tic = (tic_core*)mem;

}

void tic_api_pdtextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8 *colors, s32 count, float dclip){
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode | DepthBufferMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip);
}

void tic_api_dclear(tic_mem *mem){
	tic_core *tic = (tic_core*)mem;
	for(size_t i = 0; i < TIC80_HEIGHT*TIC80_WIDTH; i++){
		depthBuffer[i] = +90000;
	}
}

void tic_api_pdftextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, bool use_map, u8 *colors, s32 count, float dclip, float dfar, ScriptShaderFunc func, void *data){//why bool(*scriptshade)(s32 x, s32 y)
	if(func == NULL || data == NULL){
		tic_api_pdtextri(mem, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip);
		return;
	}
	drawTexturedTriangle((tic_core*)mem, PerspectiveCorrectMode | DepthBufferMode | ScriptShaderMode, x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3, use_map, colors, count, dclip, dfar, func, data);
}

void tic_api_utextri(tic_mem* mem, float x1, float y1, float x2, float y2, float x3, float y3, float u1, float v1, float u2, float v2, float u3, float v3, float d1, float d2, float d3, float dnear, float dfar, bool use_map, u8 *colors, s32 count, bool norm, float *matx, ScriptShaderFunc ssf, void *ssfData){
	drawTexturedTriangle((tic_core*)mem, 0
						 | (d1!=d2||d2!=d3?PerspectiveCorrectMode:0) | (dnear>=0&&dfar>0?DepthBufferMode:0)
						 | (ssf!=nullptr&&ssfData!=nullptr?ScriptShaderMode:0)
						 | (norm?NormalizeMode:0) | (matx!=nullptr ? MatrixMode : 0),
						 x1, y1, x2, y2, x3, y3, u1, v1, u2, v2, u3, v3, d1, d2, d3,
						 use_map, colors, count, dnear, dfar, ssf, ssfData, norm, matx);
}

float tic_api_rttri(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float *res){
	//vec3 v1v0 = v1 - v0;
	float v1v0x = x2 - x1, v1v0y = y2-y1, v1v0z = z2-z1;
	//vec3 v2v0 = v2 - v0;
	float v2v0x = x3 - x1, v2v0y = y3 - y1, v2v0z = z3 - z1;
	//vec3 rov0 = ro - v0;
	float rov0x = rx - x1, rov0y = ry- y1, rov0z = rz - z1;
	//vec3  n = cross(v1v0, v2v0);
	float nx = v1v0y * v2v0z - v1v0z * v2v0y, ny = v1v0z * v2v0x - v1v0x * v2v0z, nz = v1v0x * v2v0y - v1v0y * v2v0x;
	//vec3  q = cross(rov0, rd);
	float qx = rov0y*dz-rov0z*dy, qy = rov0z*dx-rov0x*dz, qz = rov0x*dy-rov0y*dx;
	//float d = 1.0 / dot(rd, n);
	float d = 1.0 / (dx * nx + dy * ny + dz * nz);
	//float u = d * dot(-q, v2v0);
	float u = d * (-qx * v2v0x + -qy * v2v0y + -qz * v2v0z);
	//float v = d * dot(q, v1v0);
	float v = d * (qx * v1v0x + qy * v1v0y + qz * v1v0z);
	//float t = d * dot(-n, rov0);
	float t = d * (-nx * rov0x + -ny * rov0y + -nz * rov0z);
	if(u<0.0 || u>1.0 || v<0.0 || (u + v)>1.0) t = -1.0;
	//return vec3(t, u, v);;
	res[0] = t; res[1] = u; res[2] = v;
	return t;
}
// res[0,1] = intersect dist
float tic_api_rtsphr(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x, float y, float z, float rad, float *res){
	//vec3 oc = ro - ce;
	float oc1, oc2, oc3;
	oc1 = rx - x; oc2 = ry - y; oc3 = rz - z;
	//float b = dot(oc, rd);
	float b = oc1 * dx + oc2 * dy + oc3 * dz;
	//float c = dot(oc, oc) - ra * ra;
	float c = (oc1*oc1+oc2*oc2+oc3*oc3) - (rad * rad);
	//float h = b * b - c;
	float h = b * b - c;
	//if(h < 0.0) return vec2(-1.0); // no intersection
	if(h < 0.0){
		res[0] = res[1] = -1.0;
		return -1.0; // no intersection
	}
	//h = sqrt(h);
	h = sqrt(h);
	//return vec2(-b - h, -b + h);
	res[0] = -b - h; res[1] = -b + h;
	return (-b - h);
}
// res[0,1] - intersect dist, res[2,3,4] - norm xyz;
float tic_api_rtcube(tic_mem*, float rx, float ry, float rz, float dx, float dy, float dz, float x, float y, float z, float sx, float sy, float sz, float *res, float *norm){
	//vec3 m = 1.0 / rd; // can precompute if traversing a set of aligned boxes
	float mx = 1.0 / dx, my = 1.0 / dy, mz = 1.0 / dz;
	//vec3 n = m * ro;   // can precompute if traversing a set of aligned boxes
	float nx = mx * (rx-x), ny = my * (ry-y), nz = mz * (rz-z);
	//vec3 k = abs(m)*boxSize;
	float kx = fabs(mx) * sx, ky = fabs(my) * sy, kz = fabs(mz) * sz;
	//vec3 t1 = -n - k;
	float t1x = -nx - kx, t1y = -ny - ky, t1z = -nz - kz;
	//vec3 t2 = -n + k;
	float t2x = -nx + kx, t2y = -ny + ky, t2z = -nz + kz;
	//float tN = max(max(t1.x, t1.y), t1.z);
	//float tF = min(min(t2.x, t2.y), t2.z);
	float tN = fmax(fmax(t1x, t1y), t1z);
	float tF = fmin(fmin(t2x, t2y), t2z);
	//if(tN > tF || tF < 0.0) return vec2(-1.0); // no intersection
	if(tN > tF || tF < 0.0){ res[0]=res[1]=-1.0; return -1.0; };
	//outNormal = -sign(rd)*step(t1.yzx, t1.xyz)*step(t1.zxy, t1.xyz);
	res[2] = -signbit(dx)*((t1y > t1x) ? 1 : 0)*((t1z > t1x) ? 1 : 0);
	res[3] = -signbit(dy)*((t1z > t1y) ? 1 : 0)*((t1x > t1y) ? 1 : 0);
	res[4] = -signbit(dz)*((t1x > t1z) ? 1 : 0)*((t1y > t1z) ? 1 : 0);
	//return vec2(tN, tF);
	res[0] = tN; res[1] = tF;
	return tN;
}


enum MessageType{
	MesNull,
	MesPing, // C->S
	MesConnect, // C->S
	MesDisconnect, // C->S
	MesExit, // S->C
	MesTicPacket, // C<=>S<=>C
	MesUpdate, // S->C
	MesPong, // S->C, after ping
	MesTicComplex, // C->S->C
};

enum DataType{
	DataNull,
	DataInteger,
	DataFloat,
	DataString,
	DataBoolean,
	DataArrayInt,
	DataArrayFloat,
};

#pragma pack(push)
struct PacketHeader{
	u8 type;
	u8 version = 1;
};
struct PacketPing{
	PacketHeader head;
};
struct PacketConnect{
	PacketHeader head;
	unsigned short wantId = 0; // if user want special id, server can give if allow and free
	//float timeout = TIMEOUT; // user can make own timeout less than on server
};
struct PacketDisconnect{
	PacketHeader head;
};
struct PacketExit{
	PacketHeader head;
};
struct PacketTicPacket{
	PacketHeader head;
	u16 src, dst;
	u8 type;
	//u64 app, key;
	u16 appsize, keysize;
	unsigned short size;
	u8 data[1];
};
struct PacketUpdate{
	PacketHeader head;
	unsigned short newId = 0; // server send new tmid or 0 if not changed
};
struct PacketPong{
	PacketHeader head;
};
#pragma pack(pop)
typedef PacketHeader PacketHeader;
typedef PacketTicPacket PacketTicPacket;


const uint_least32_t Crc32Table[256] = {
	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
static inline uint_least32_t Crc32(const unsigned char *buf, size_t len){
	uint_least32_t crc = 0xFFFFFFFF;
	while(len--)
		crc = (crc >> 8) ^ Crc32Table[(crc ^ *buf++) & 0xFF];
	return crc ^ 0xFFFFFFFF;
}

//PacketTicPacket netbuffer[32];
static u32 ticnetMasterIp = 0;
static u32 ticnetState = 0;
static const int netbufferSize = 32 * 1024 * 4;
static struct{
	int read = 0, write = 0;
	bool full = false;
	u8 buffer[netbufferSize] = {0};
} netbuffer;
bool ring_empty(){
	return !netbuffer.full && netbuffer.read == netbuffer.write;
}
int ring_free(){
	if(netbuffer.write >= netbuffer.read)
		return ((netbufferSize-1) - (netbuffer.write - netbuffer.read));
	else
		return (netbuffer.read - netbuffer.write - 1);
}
bool ring_write(void *data, unsigned int size){
	if(ring_free() < size){
		return false;
	}
	for(size_t i = 0; i < size; i++){
		netbuffer.buffer[(netbuffer.write++)] = ((u8*)data)[i];
		netbuffer.write %= netbufferSize;
	}
	return true;
}
bool ring_read(void *data, unsigned int size){
	if(netbufferSize < size || (netbufferSize - ring_free()) < size){
		return false;
	}
	for(size_t i = 0; i < size; i++){
		((u8*)data)[i] = netbuffer.buffer[netbuffer.read++];
		netbuffer.read %= netbufferSize;
	}
	return true;
}

short tic_core_ticnet_htons(short val){
	return nu_htons(val);
}
short tic_core_ticnet_ntohs(short val){
	return nu_ntohs(val);
}
long tic_core_ticnet_htonl(long val){
	return nu_htonl(val);
}
long tic_core_ticnet_ntohl(long val){
	return nu_ntohl(val);
}

void tic_core_ticnet_init(tic_mem *mem, u32 ip){
	tic_core *core = (tic_core*)mem;
	nu_init();
	if(ip && isNetutilitySupport()){
		volatile int err;
		core->ticnet_socket = nu_socket();
		nu_bind(core->ticnet_socket, rand()%64+(TMPORT0+1));
		err = nu_geterror();
		ticnetMasterIp = (ip);
		PacketConnect packet;
		packet.head.type = MesConnect;
		packet.head.version = 1;
		nu_sendto(core->ticnet_socket, ip, TMPORT0, (void*)&packet, sizeof(PacketConnect), 0);
		err = nu_geterror();
	}
}
void tic_core_ticnet_deinit(tic_mem *mem){
	tic_core *core = (tic_core*)mem;
	if(ticnetMasterIp || ticnetState){
		PacketDisconnect packet;
		packet.head.type = MesDisconnect;
		packet.head.version = 1;
		nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)&packet, sizeof(PacketDisconnect), 0);
		nu_close(core->ticnet_socket);
	}
	nu_deinit();
}

u32 ticnet_inetaddr(const char *str){
	return nu_getip(str);
}

static void ticnetHandle(tic_core *core, PacketHeader *head, u32 ip, u16 port, u32 len);
static int ticnetLastPing = 0;

void tic_core_ticnet_update(tic_mem *mem){
	tic_core *core = (tic_core*)mem;
	if(!ticnetState && !ticnetMasterIp){
		return;
	}
	//static int lasttime = clock();
	int currenttime = clock();
	if(ticnetState){
		if(ticnetLastPing + 10 * CLOCKS_PER_SEC < currenttime){
			ticnetLastPing = currenttime;
			PacketPing packet;
			packet.head.type = MesPing;
			packet.head.version = 1;
			// TODO: Get address from config
			//nu_sendto(core->ticnet_socket, nu_getip("127.0.0.1"), TMPORT0, (void*)&packet, sizeof(PacketPing), 0);
			nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)&packet, sizeof(PacketPing), 0);
		}
	}
	int err = 0;
	do{
		u32 addr;
		s32 len;
		u16 port;
		u8 buffer[2 * 1024];
		len = nu_recvfrom(core->ticnet_socket, &addr, &port, buffer, 2*1024, 0);
		err = ((len <= 0) ? -1 : 0);
		volatile int er = nu_geterror();
		if(err != -1){
			ticnetHandle(core, (PacketHeader*)buffer, addr, port, len);
		}
	} while(err != -1);
}

static void ticnetHandle(tic_core *core, PacketHeader *head, u32 ip, u16 port, u32 len){
	if(head->version != 1){
		return;
	}
	switch(head->type){
		// it all unreal
		case MesNull:
		case MesPing:
		case MesConnect:
		case MesDisconnect:
			break;
		case MesExit:
			ticnetMasterIp = 0;
			core->tmid = 0;
			ticnetState = 0;
			break;
		case MesTicPacket:
			{
				PacketTicPacket *packet = ((PacketTicPacket*)(head));
				u16 applen = nu_ntohs(packet->appsize), keylen = nu_ntohs(packet->keysize), datalen = nu_ntohs(packet->size);
				ring_write(head, sizeof(PacketTicPacket) + applen + keylen + datalen);
			}
			break;
		case MesUpdate:
			{
				PacketUpdate *upd = (PacketUpdate*)head;
				if(upd->newId == -1 || upd->newId == 2 || upd->newId == 3){
					ticnetState = 0;
					core->tmid = 0;
				}
				else if(upd->newId != 0){
					core->tmid = nu_ntohs(upd->newId);
					ticnetState = 1;
				}
			}
			break;
		case MesPong:
			// TODO: Master timelife
			break;
		case MesTicComplex:
			// TODO: Implement
			break;
		default:
			break;
	}
}

void tic_api_net(tic_mem *mem, u16 *tmid, u16 *arg2){
	tic_core *core = (tic_core*)mem;
	if(tmid){
		*tmid = core->tmid;
	}
}

void tic_api_nsend(tic_mem *mem, u16 id, const char *app, const char *key, u8 type, void *data, u16 size, u8 port){
	// TODO: Remake for later sending with PacketTicComplex
	tic_core *core = (tic_core*)mem;
	if((!ticnetMasterIp || !core->tmid || !core->ticnet_socket || !ticnetState) && id != 2){
		return;
	}
	u8 buffer[2*1024];
	u16 keylen = strlen(key) + 1, applen = strlen(app) + 1;
	u16 sumSize = applen + keylen + size;
	if((applen + keylen + size) > (2*1024-128) && sumSize <= 3){
		return;
	}
	// Temporary is not-implement
	if((id < 4 && id != 2 && id != 1) || type > 8){
		return;
	}
	PacketTicPacket *packet = (PacketTicPacket*)buffer;
	packet->head.type = MesTicPacket;
	packet->head.version = 1;
	packet->src = nu_htons(core->tmid);
	packet->dst = nu_htons(id);
	//packet->app = Crc32((const unsigned char*)app, strlen(app));
	//packet->key = Crc32((const unsigned char*)key, strlen(key));
	packet->appsize = nu_htons(applen);
	packet->keysize = nu_htons(keylen);
	packet->type = type;
	packet->size = nu_htons(size);
	//memcpy(packet->data, data, size);
	memcpy(/*packet->data*/buffer + sizeof(PacketTicPacket) + 0, app, applen);
	memcpy(/*packet->data*/buffer + sizeof(PacketTicPacket) + applen, key, keylen);
	memcpy(/*packet->data*/buffer + sizeof(PacketTicPacket) + applen + keylen, data, size);
	if(id == 2){
		ring_write(buffer, sizeof(PacketTicPacket) + applen + keylen + size);
		return;
	}
	nu_sendto(core->ticnet_socket, ticnetMasterIp, TMPORT0, (void*)buffer, sizeof(PacketTicPacket) + applen + keylen + size, 0);
	return;
}

bool tic_api_nrecv(tic_mem *mem, u16 *id, char *app, char *key, u8 *type, void *data, u16 *size, u8 *port){
	tic_core *core = (tic_core*)mem;
	if(!ticnetMasterIp || !core->tmid || !core->ticnet_socket || !ticnetState){
		return 0;
	}
	bool ok = true;
	PacketTicPacket packet;
	u8 buffer[2 * 1024 - 128];
	u16 applen, keylen, datalen;
	ok = ok && ring_read(&packet, sizeof(PacketTicPacket));
	if(ok){ applen = nu_ntohs(packet.appsize), keylen = nu_ntohs(packet.keysize), datalen = nu_ntohs(packet.size); }
	ok = ok && ring_read(buffer, applen + keylen + datalen);
	if(!ok){
		netbuffer.read = netbuffer.write = 0;
		return 0;
	}
	*id = nu_ntohs(packet.src);
	*type = packet.type;
	//strncpy(app, (const char*)(&packet + sizeof(PacketTicPacket) + 0), packet.appsize);
	//strncpy(key, (const char*)(&packet + sizeof(PacketTicPacket) + packet.appsize), packet.keysize);
	//memcpy(data, (void*)(&packet + sizeof(PacketTicPacket) + packet.appsize + packet.keysize), packet.size);
	strncpy(app, (const char*)(buffer + 0), applen);
	strncpy(key, (const char*)(buffer + applen), keylen);
	memcpy(data, (void*)(buffer + applen + keylen), datalen);
	*size = datalen;
	return 1;
}

void tic_api_nrpc(tic_mem *mem, u16 id, const char *app, const char *func, void *argv, u32 argc){
	// TODO: Implement
}

