#define CACHE_SEGMENT_SIZE 512
#define CACHE_VOLUME_SIZE 64

// ivec2 GetCacheStoragePos(ivec3 voxelIndex, int face) { // in pixels/texels
	// return (voxelIndex.xz + (CACHE_SEGMENT_SIZE / 32)) * 16 + ivec2(voxelIndex.y % 16, voxelIndex.y / 16)
	// 		+ ivec2(CACHE_SEGMENT_SIZE*face, 0);
//     // return (voxelIndex.xz + (CACHE_SEGMENT_HEIGHT / 32)) * ivec2(8, 16) + ivec2(voxelIndex.y / 16, voxelIndex.y % 16);
//     //         + ivec2(CACHE_SEGMENT_WIDTH*face, 0);

// }

ivec3 VoxelSpaceToCacheSpace(ivec3 voxelIndex) {
	return voxelIndex - ivec3(0,int(floor(cameraPosition.y)),0) + CACHE_VOLUME_SIZE/2;
}

ivec3 CacheSpaceToVoxelSpace(ivec3 cachePos) {
	return cachePos + ivec3(0,int(floor(cameraPosition.y)),0) - CACHE_VOLUME_SIZE/2;
}

bool IsInCache(ivec3 voxelIndex) {
	ivec3 cachePos = VoxelSpaceToCacheSpace(voxelIndex);

	return clamp(cachePos, ivec3(0), ivec3(CACHE_VOLUME_SIZE)) == cachePos;
}

ivec2 GetCacheStoragePos(ivec3 voxelIndex, int face) { 
	ivec3 cachePos = VoxelSpaceToCacheSpace(voxelIndex);
	int i = cachePos.x + cachePos.y*CACHE_VOLUME_SIZE + cachePos.z*CACHE_VOLUME_SIZE*CACHE_VOLUME_SIZE;
	return ivec2(i % CACHE_SEGMENT_SIZE, i / CACHE_SEGMENT_SIZE) + ivec2(CACHE_SEGMENT_SIZE*face, 0);
}

ivec3 CacheStoragePosToVoxelPosition(ivec2 storagePos) {
	storagePos.x = storagePos.x % CACHE_SEGMENT_SIZE;
	int i = storagePos.y*CACHE_SEGMENT_SIZE + storagePos.x;
	int m0 = i % (CACHE_VOLUME_SIZE*CACHE_VOLUME_SIZE);
	int m1 = i % (CACHE_VOLUME_SIZE);
	ivec3 cachePos = ivec3(m1, (m0-m1)/CACHE_VOLUME_SIZE, (i-m0)/(CACHE_VOLUME_SIZE*CACHE_VOLUME_SIZE));
	return CacheSpaceToVoxelSpace(cachePos);
}


// ivec3 CacheStoragePosToVoxelPosition(ivec2 storagePos) { 
// 	storagePos.x = storagePos.x % CACHE_SEGMENT_SIZE;
// 	ivec3 voxelIndex = ivec3(0);
// 	voxelIndex.y = (storagePos.y % 16) * 16 + storagePos.x % 16;
// 	ivec2 leftSide = storagePos - ivec2(voxelIndex.y % 16, voxelIndex.y / 16);
// 	voxelIndex.xz = leftSide / 16 - (CACHE_SEGMENT_SIZE / 32);
// 	return voxelIndex;
//     // storagePos.x = storagePos.x % CACHE_SEGMENT_WIDTH;
//     // ivec3 voxelIndex = ivec3(0);
// 	// voxelIndex.y = (storagePos.x % 8) * 16 + storagePos.y % 16;
// 	// ivec2 leftSide = storagePos - ivec2(voxelIndex.y / 16, voxelIndex.y % 16);
// 	// voxelIndex.xz = leftSide / ivec2(8, 16) - (CACHE_SEGMENT_HEIGHT / 32);
// 	// return voxelIndex;
// }

const vec3[6] faceNormals = vec3[6] (vec3(1,0,0), vec3(0,1,0), vec3(0,0,1),
									  vec3(-1,0,0), vec3(0,-1,0), vec3(0,0,-1));


const ivec3[6] faceTangents = ivec3[6] (ivec3(0,0,1), ivec3(1,0,0), ivec3(1,0,0),
									  ivec3(0,0,1), ivec3(1,0,0), ivec3(1,0,0));

const ivec3[6] faceBitangents = ivec3[6] (ivec3(0,1,0), ivec3(0,0,1), ivec3(0,1,0),
									  ivec3(0,1,0), ivec3(0,0,1), ivec3(0,1,0));

const int[6] faceHorizontal = int[6] (2, 0, 0, 2, 0, 0);
const int[6] faceVertical = int[6] (1, 2, 1, 1, 2, 1);

// int getFace(vec3 n) {
//     const float one = 0.9;
//     if(n.x > one) {
//         return 0;
//     } else if(n.y > one) {
//         return 1;
//     } else if(n.z > one) {
//         return 2;
//     } else if(n.x < -one) {
//         return 3;
//     } else if(n.y < -one) {
//         return 4;
//     } else {
//         return 5;
//     }
// }

int getFace(vec3 n) {//Thanks Capt Tatsu for this function
  ivec3 i = ivec3(n + sign(n)*0.1);
  int three = int(clamp(-(i.x+i.y+i.z), 0, 1))*3;
  return 2 * abs(i.z) + (abs(i.y) + three);
}

vec3 cacheRead(ivec2 storagePos) {
    return texelFetch(colortex13, storagePos, 0).rgb;
}

vec3 sampleCacheHQ(vec3 voxelPos, vec3 geoNormal, int face) {
	vec3 a = texelFetch(colortex13, GetCacheStoragePos(ivec3(floor(voxelPos - geoNormal*0.25)), face), 0).rgb;
	float luma_a = LuminanceAP1(a);
	vec3 b = texelFetch(colortex13, GetCacheStoragePos(ivec3(floor(voxelPos - geoNormal*0.25)) + faceTangents[face], face), 0).rgb;
	float luma_b = LuminanceAP1(b);
	vec3 c = texelFetch(colortex13, GetCacheStoragePos(ivec3(floor(voxelPos - geoNormal*0.25)) + faceBitangents[face], face), 0).rgb;
	float luma_c = LuminanceAP1(c);
	vec3 d = texelFetch(colortex13, GetCacheStoragePos(ivec3(floor(voxelPos - geoNormal*0.25)) + faceTangents[face] + faceBitangents[face], face), 0).rgb;
	float luma_d = LuminanceAP1(d);

	float bValid = float(luma_b > 1e-5);
	float cValid = float(luma_c > 1e-5);
	float dValid = float(luma_d > 1e-5);

	vec3 fPos = fract(voxelPos);
	return clamp(mix(mix(a, b, fPos[faceHorizontal[face]]*bValid), mix(c, d, fPos[faceHorizontal[face]]*dValid), fPos[faceVertical[face]]*clamp01(cValid + dValid)), 0.0, 9999.0);
}