const Afunction = require('../util/function.js');
module.exports = {
	inject: function(bot) {
		
		const Ypos = Afunction.math.random(-32, -32); // random!
		bot.task.data.needRefillCore = true;
		bot.task.data.nbtQueries = {};
		bot.task.timeout.commandBlockNeverRepeating = new Map();
		bot.task.timeout.nbtQueries = {};
		
		if (bot.task.data.serverName === 'Chipmunk25570') bot.task.data.coreConfig.area.end.y = 0;
		
		bot.core = {
			area: { ...bot.task.data.coreConfig.area },
			position: null,
			currentBlockRelative: { x: 0, y: 0, z: 0 },
			
			move(pos = bot.task.data.position) {
				if (!pos) return false;
				bot.core.position = {
				  x: Math.floor(pos.x / 16) * 16,
				  y: Math.floor(Ypos / 16) * 16,
				  z: Math.floor(pos.z / 16) * 16
				};
				return true
			},
			
			// Sometime, it wont work.
			autoRefill() {
				const { start, end } = bot.core.area;
				const isCorePosWithinBounds = (pos, corepos) => {
				  return pos.x >= corepos.x + start.x && pos.x <= corepos.x + end.x &&
						 pos.y >= corepos.y + start.y && pos.y <= corepos.y + end.y &&
						 pos.z >= corepos.z + start.z && pos.z <= corepos.z + end.z;
				};
				
				const isValidBlockType = (type) => {
				  return Afunction.math.between(type, 8680, 8691) || Afunction.math.between(type, 13540, 13551) || Afunction.math.between(type, 13528, 13539);
				};

				const coreSize = (Math.abs(start.x) + Math.abs(end.x) + 1) * (Math.abs(start.y) + Math.abs(end.y) + 1) * (Math.abs(start.z) + Math.abs(end.z) + 1);

				bot.on('block_change', (packet) => {
				  if (packet.type === 0 || !isValidBlockType(packet.type)) {
				    const corePos = bot.core.position;
				    if (!corePos) return bot.task.data.needRefillCore = true;
				    if (isCorePosWithinBounds(packet.location, corePos)) {
				      bot.task.data.needRefillCore = true;
				    }
				  }
				});

				bot.on('multi_block_change', (data) => {
				  const corePos = bot.core.position;
				  if (!corePos) return bot.task.data.needRefillCore = true;
				  const packetChunkX = data.chunkCoordinates.x * 16;
				  const packetChunkY = data.chunkCoordinates.y * 16;
				  const packetChunkZ = data.chunkCoordinates.z * 16;
				  if (packetChunkX !== corePos.x || packetChunkY !== corePos.y || packetChunkZ !== corePos.z) return;
				  let count = coreSize;
				  for (const record of data.records) {
					const pos = {
					  x: packetChunkX + ((record >> 8) & 15),
					  y: packetChunkY + (record & 15),
					  z: packetChunkZ + ((record >> 4) & 15),
					};
					if (isCorePosWithinBounds(pos, corePos) && !isValidBlockType(record >> 12)) {
					  count--;
					  break;
					}
				  }
				  
				  bot.task.data.needRefillCore = count < coreSize;
				});
				
				bot.on('map_chunk', (data) => {
				  const chunkX = data.x * 16;
				  const chunkZ = data.z * 16;
				  const corepos = bot.core.position;
				  if (!corepos) return bot.task.data.needRefillCore = true;
				  if (chunkX === corepos.x && chunkZ === corepos.z) {
					  
					const coreCount = data.blockEntities.filter(BlockEntity => BlockEntity.type === 23).reduce((count, BlockEntity) => {
					  const blockX = chunkX + BlockEntity.x;
					  const blockY = BlockEntity.y;
					  const blockZ = chunkZ + BlockEntity.z;

					  return count + (isCorePosWithinBounds({ x: blockX, y: blockY, z: blockZ }, corepos) ? 1 : 0);
					}, 0);

					bot.task.data.needRefillCore = coreCount < coreSize;
				  }
				});
				
				bot.on('move', (oldPos, newPos) => {
					bot.core.move(newPos);
					bot.task.data.needRefillCore = true;
				});
				
				bot.task.interval.autoRefill = setInterval(() => {
				  if (!bot.core.position) return bot.core.move();
				  if (
					bot.task.data.needRefillCore && bot.task.data.isOperator && bot.task.data.isCreative &&
					(bot.task.data.position.y <= 320 || bot.task.data.position.y > -50) &&
					(bot.task.data.position.world === 'minecraft:overworld' || bot.task.data.position.world === 'minecraft:world_flatlands') &&
					bot.task.data.worldborder.size >= 40 &&
					(
					  bot.task.data.position.x + 17 < bot.task.data.worldborder.center.x + bot.task.data.worldborder.size &&
					  bot.task.data.position.x > 17 + bot.task.data.worldborder.center.x - bot.task.data.worldborder.size &&
					  bot.task.data.position.z + 17 < bot.task.data.worldborder.center.z + bot.task.data.worldborder.size &&
					  bot.task.data.position.z > 17 + bot.task.data.worldborder.center.z - bot.task.data.worldborder.size
					)
				  ) {
					bot.core.refillCore();
				  }
				}, 500);
				/*
				bot.task.interval.fiveMinsAutoRefill = setInterval(() => {
					bot.task.data.needRefillCore = true;
				}, 300000)
				*/
			},
			
			refillCore() { // hard code !
				const pos = bot.core.position;
				if (!pos) return bot.task.data.needRefillCore = true;
				bot.write('look', { yaw: 0, pitch: -90, flags: { yaw: false, pitch: false }});
				const { start, end } = bot.core.area;
				const refillCmd = `/fill ${pos.x + start.x} ${pos.y + start.y} ${pos.z + start.z} ${pos.x + end.x} ${pos.y + end.y} ${pos.z + end.z} chain_command_block[facing=down]{CustomName:'${bot.task.data.coreConfig.name}'} replace`;
				const locationOfmaxCommandChainLength = { x: Math.floor(bot.task.data.position.x), y: Math.floor(bot.task.data.position.y) - 1, z: Math.floor(bot.task.data.position.z) };
				const locationOfcommandModificationBlockLimit = { x: Math.floor(bot.task.data.position.x), y: Math.floor(bot.task.data.position.y) - 2, z: Math.floor(bot.task.data.position.z) };
				const locationOfRefill = { x: Math.floor(bot.task.data.position.x), y: Math.floor(bot.task.data.position.y) - 3, z: Math.floor(bot.task.data.position.z) };
				bot.write('block_dig', { status: 0, location: locationOfmaxCommandChainLength, face: 1 });
				bot.write('block_dig', { status: 0, location: locationOfcommandModificationBlockLimit, face: 1 });
				bot.write('block_dig', { status: 0, location: locationOfRefill, face: 1 });
							
				bot.write('set_creative_slot', {
				  "stateId": 0,
				  "slot": 36,
				  "item": {
					"itemCount": 1,
					"itemId": 418,
					"addedComponentCount": 1,
					"removedComponentCount": 0,
					"components": [
					  {
						"type": "block_entity_data",
						"data": {
						  "type": "compound",
						  "value": {
							"id": {
							  "type": "string",
							  "value": "command_block"
							},
							"Command": {
							  "type": "string",
							  "value": "gamerule maxCommandChainLength 2084"
							},
							"auto": {
							  "type": "byte",
							  "value": 1
							}
						  }
						}
					  }
					],
					"removeComponents": []
				  }
				})
				
				bot.write('block_place', {
					location: locationOfmaxCommandChainLength,
					direction: 1, // Top face
					hand: 0,
					cursorX: 0.5, // Center of the block
					cursorY: 0.5,
					cursorZ: 0.5,
					insideBlock: false, // Not inside the block
				});
				bot.write('set_creative_slot', {
				  "stateId": 0,
				  "slot": 36,
				  "item": {
					"itemCount": 1,
					"itemId": 538,
					"addedComponentCount": 1,
					"removedComponentCount": 0,
					"components": [
					  {
						"type": "block_entity_data",
						"data": {
						  "type": "compound",
						  "value": {
							"id": {
							  "type": "string",
							  "value": "command_block"
							},
							"Command": {
							  "type": "string",
							  "value": "gamerule commandModificationBlockLimit 2048" // - amyavi
							},

							"auto": {
							  "type": "byte",
							  "value": 1
							}
						  }
						}
					  }
					],
					"removeComponents": []
				  }
				});
				
				
				bot.write('block_place', {
					location: locationOfcommandModificationBlockLimit,
					direction: 1, // Top face
					hand: 0,
					cursorX: 0.5, // Center of the block
					cursorY: 0.5,
					cursorZ: 0.5,
					insideBlock: false, // Not inside the block
				});

					
				bot.write('set_creative_slot', {
				  "stateId": 0,
				  "slot": 36,
				  "item": {
					"itemCount": 1,
					"itemId": 538,
					"addedComponentCount": 1,
					"removedComponentCount": 0,
					"components": [
					  {
						"type": "block_entity_data",
						"data": {
						  "type": "compound",
						  "value": {
							"id": {
							  "type": "string",
							  "value": "command_block"
							},
							"Command": {
							  "type": "string",
							  "value": refillCmd
							},
							"auto": {
							  "type": "byte",
							  "value": 1
							}
						  }
						}
					  }
					],
					"removeComponents": []
				  }
				});
					
				
				
				bot.write('block_place', {
					location: locationOfRefill,
					direction: 1,
					hand: 0,
					cursorX: 0.5,
					cursorY: 0.5,
					cursorZ: 0.5,
					insideBlock: false,
				});
				
				bot.task.timeout.BreakPlaceBlock = setTimeout(() => {
				  bot.write('block_dig', { status: 0, location: locationOfmaxCommandChainLength, face: 1 });
				  bot.write('block_dig', { status: 0, location: locationOfcommandModificationBlockLimit, face: 1 });
				  bot.write('block_dig', { status: 0, location: locationOfRefill, face: 1 });
				}, 500);
				
			},
		
			run(command, output = false, timeout = 5000) {
				if (!command || command === '' || command.length > 32767) return Promise.resolve(undefined); // Command too long OR nothing
				const corePos = bot.core.position;
				if (!corePos) { 
					bot.core.needRefillCore = true;
					return Promise.resolve(undefined);
				}
				const { start, end } = bot.core.area;
				let relativePosition = bot.core.currentBlockRelative;

				relativePosition.x++;
				relativePosition.x > end.x && (relativePosition.x = start.x, relativePosition.z++);
				relativePosition.z > end.z && (relativePosition.z = start.z, relativePosition.y++);
				relativePosition.y > end.y && (relativePosition.x = start.x, relativePosition.y = start.y, relativePosition.z = start.z);
				
				const CommandBlockLocation = { x: relativePosition.x + corePos.x, y: relativePosition.y + corePos.y, z: relativePosition.z + corePos.z }
				bot.write("update_command_block", {
					command: command,
					location: CommandBlockLocation,
					mode: 1,
					flags: 5
				});
				
				if (bot.task.timeout.commandBlockNeverRepeating !== undefined) {
					clearTimeout(bot.task.timeout.commandBlockNeverRepeating.get(CommandBlockLocation));
					bot.task.timeout.commandBlockNeverRepeating.set(CommandBlockLocation, setTimeout(() => {
						bot.write("update_command_block", {
							command: "",
							location: CommandBlockLocation,
							mode: 0,
							flags: 1
						});
						bot.task.timeout.commandBlockNeverRepeating.delete(CommandBlockLocation);
					}, 10000));
				}
				
				if (!output) return Promise.resolve(undefined);
				let outputRelativePosition = JSON.parse(JSON.stringify(CommandBlockLocation)); // prevent relativePosition change pos
				return new Promise((resolve) => {
					if (!bot?.task?.timeout?.nbtQueries) return resolve(undefined);
					const secretString = Afunction.generateRandomString(30, 1, 2, 3);
					bot.task.data.nbtQueries[secretString] = { resolve };
					bot.core.run(`minecraft:tellraw @a[name="${bot.username}"] {"translate":"NilBot_CommandBlock_Output %s %s","with":["${secretString}",{"nbt":"LastOutput","block":"${CommandBlockLocation.x} ${CommandBlockLocation.y} ${CommandBlockLocation.z}","interpret":true}]}`);
					bot.task.timeout.nbtQueries[secretString] = setTimeout(() => {
						resolve(undefined);
						delete bot.task.data.nbtQueries[secretString];
						delete bot.task.timeout.nbtQueries[secretString];
					}, timeout);
				});
			}
		}
		
		// bot.task.data.core = bot.core;
		
		// end core

		bot.on("custom_systemChat", (_, systemChat) => {
			let jsonMsg = systemChat.jsonMsg.message;
			if (jsonMsg.translate === 'NilBot_CommandBlock_Output %s %s' && typeof jsonMsg.with?.[0]?.text === "string" && typeof jsonMsg.with?.[1] === "object") {
				const secretString = jsonMsg.with[0].text;
				if (bot?.task?.timeout?.nbtQueries?.[secretString]) {
					clearTimeout(bot.task.timeout.nbtQueries[secretString])
					delete bot.task.timeout.nbtQueries[secretString]
					bot.task.data.nbtQueries[secretString].resolve(jsonMsg.with[1]);
					delete bot.task.data.nbtQueries[secretString];
				}
			}
		});
		
		bot.core.autoRefill();
		
	}
}