import { system, world } from "@minecraft/server";
import { ActionFormData } from "@minecraft/server-ui";
import { McfSofa } from './Sofa';
import { McfBlinds } from './Blinds';
import { McfGrill } from './Grill';
import { McfFenceGate } from './FenceGate';
import { McfBath, McfSink, McfTap } from './Sink';
import { MrCounter } from './Counter';
import { MrRockPath } from './RockPath';
import { McfPlaceContainer, McfInteractContainer, MrMailBox, MrPostBox, playerPlaceMailbox } from './Container';
import { McfOldBreakLargeBlock, McfOldPlaceLargeBlocks, McfOldCkeckLargeBlock } from './PlaceBlockParts';
import { McfFreezer } from './FreezerEvent';
import { McfBlockConnections } from './Fence';
import { McfAddItem, McfBounce, McfCreateInterface, McfItemPlacer, McfPlaceBlockFace, McfPlaceBlockWall, McfRotateBy45, McfSit, McfUpdateCardinalBlocks, McfUpdateOldCardinalBlocks, McfUpdateOldSideBlocks, McfUpdateSideBlocks } from './Global';
import { spawnEntityRotatedByBlock } from "../util/utils";
import { directionState } from "../util/globalVariables";
import { McfLamp } from './Lamp';
import { McfBlender } from './Blender';
import { McfChoppingBoard } from './ChoppingBoard';
import { McfContainerManager } from './DynamicContainers';
import { McfWashingMachine } from './WashingMachine';
import { McfDishwasher } from './Dishwasher';
import { McfMicrowave } from './Microwave';
import { McfOven } from './Oven';
import { McfBreakLargeBlock, McfLargeBlockPlacer } from'./LargeBlockPlacer';
import { McfPrinter, McfPrinterBlock } from './Printer';
import { McfOpedSealedItem, McfOpenPresent, McfPlacePresent, McfSealItem } from './SealItems';
import { McfStereo, McfStereoRegister } from './Stereo';
import { McfModernStairs, McfModernStairsCheck } from './Stairs';
import { McfDeviceUse, McfSwitchPlace, McfSwitchUse } from './Switch';
import { McfBin, McfCampfire, McfCeilingFan, McfChimney, McfClock, McfComputer, McfFairyLight, McfFireAlarm, McfPlaySound, McfShowerHead, McfToaster, McfUnpressDoorBell } from './General';

McfWashingMachine.registerEvent();
McfDishwasher.registerEvent();
McfMicrowave.registerEvent();
McfOven.registerEvent();
McfStereoRegister.registerEvent();
McfGrill.registerEvent();
McfContainerManager.init();
McfFreezer.registerEvent();
McfPrinter.registerEvent();
playerPlaceMailbox();

const SLEEP_ANIM = {
	"mcf:sleeping_bed": "animation.mcf.player.sleep"
}
world.afterEvents.dataDrivenEntityTrigger.subscribe(({ entity }) => {
	try {
		if (!entity?.isValid) return;
		const player = entity?.getComponent("rideable")?.getRiders()?.[0];
		if (!player?.isValid) return;
		player.playAnimation(SLEEP_ANIM[entity?.typeId], { stopExpression: "!q.is_riding" } );
	} catch (error) {}
}, {
	entityTypes: [ "mcf:sleeping_bed" ],
	eventTypes: [ "mcf:set_player_anim" ]
});
const MrCrayfishConfig = {
	accelerateTime: false,
	onlySleepAtNight: true
}
class SleepSystem {
	static sleepingPlayers = [];
    static isFading = false;
    static targetTime = null;
    static requiredSleepingPlayers = false;
    static sleepIntervalId = null;
    static addTimeLoop = null;
	static overworld = null;

	static getOverworld() {
		if (!this.overworld) {
			this.overworld = world.getDimension("overworld");
		}
		return this.overworld;
	}
	static isNightTime() {
		const time = world.getTimeOfDay();
		return time >= 13000 && time < 23000;
	}
	static getMinecraftTime(time) {
		const adjustedTime = (time + 6000) % 24000;
		const hours = Math.floor(adjustedTime / 1000);
		const minutes = Math.floor(((adjustedTime % 1000) / 1000) * 60);
		return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
	}
	static stopTimeAcceleration() {
		if (this.addTimeLoop) {
			system.clearRun(this.addTimeLoop);
			this.targetTime = null;
			this.addTimeLoop = null;
			this.stopSleepInterval();
		}
	}
	static showWakeUpEffects() {
		if (this.isFading) return;
		this.isFading = true;
		const allPlayers = world.getAllPlayers();
		this.sleepingPlayers.forEach(player => {
			player.camera.fade({
				fadeTime: { fadeInTime: 2.0, holdTime: 0.0, fadeOutTime: 1.0 },
				fadeColor: { red: 0.0, green: 0.0, blue: 0.0 }
			});
		});
		system.runTimeout(() => {
			if (!this.requiredSleepingPlayers) return;
			this.getOverworld().setWeather("Clear");
			world.setTimeOfDay(this.targetTime || 0);
			allPlayers.forEach(player => {
				player.onScreenDisplay.setActionBar(this.getMinecraftTime(this.targetTime || 0));
			});
			this.isFading = false;
			this.targetTime = null;
			this.stopSleepInterval();
		}, 40);
	};
	static accelerateTimeCycle() {
        if (this.addTimeLoop) return;
        this.addTimeLoop = system.runInterval(() => {
            const currentTime = world.getTimeOfDay();
            const newTime = (currentTime + 30) % 24000;
            world.setTimeOfDay(newTime);
            const timeDifference = (this.targetTime - currentTime + 24000) % 24000;
            this.sleepingPlayers.forEach(player => { player.onScreenDisplay.setActionBar(this.getMinecraftTime(newTime)) });
            if (!this.requiredSleepingPlayers) return this.stopTimeAcceleration();
            if (timeDifference <= 40) {
                this.getOverworld().setWeather("Clear");
                world.setTimeOfDay(this.targetTime || 0);
                this.stopTimeAcceleration();
            }
        }, 1);
    }
	static startSleepInterval() {
        if (this.sleepIntervalId !== null) return;
        this.sleepIntervalId = system.runInterval(() => {
            const allPlayers = world.getAllPlayers();
            this.sleepingPlayers = allPlayers.filter(player => 
                player.getComponent("minecraft:riding")?.entityRidingOn?.matches({ type: "mcf:sleeping_bed" })
            );
            const requiredCount = Math.ceil(world.gameRules.playersSleepingPercentage / 100 * allPlayers.length);
            this.requiredSleepingPlayers = this.sleepingPlayers.length >= requiredCount;

            if (this.sleepingPlayers.length === 0) return this.stopSleepInterval();
            if (this.isFading) return;
            if (!this.requiredSleepingPlayers) {
                this.sleepingPlayers.forEach(player => {
                    player.onScreenDisplay.setActionBar({ 
                        translate: "mc.actionbar.sleeping_players", 
                        with: [`${this.sleepingPlayers.length}/${requiredCount}`] 
                    });
                });
                return;
            } else if (this.targetTime !== null && this.requiredSleepingPlayers) {
                if (MrCrayfishConfig.accelerateTime) this.accelerateTimeCycle();
                else this.showWakeUpEffects();
            }
        }, 20);
    }
	static stopSleepInterval() {
        if (this.sleepIntervalId) {
            system.clearRun(this.sleepIntervalId);
            this.sleepIntervalId = null;
            this.targetTime = null;
            this.isFading = false;
        }
    }
	static PlayersSleep(player) {
		if (!this.sleepIntervalId) this.startSleepInterval();
		if (MrCrayfishConfig.onlySleepAtNight) {
			this.targetTime = 0;
			return;
		}
		const SleepUI = new ActionFormData()
			.title({ translate: 'mc.bed.select_time_of_day' })
			.button({ translate: 'hostOption.time.sunrise' }, "textures/global/ui/time_sunrise")     // 23000
			.button({ translate: 'hostOption.time.day' }, "textures/global/ui/time_day")             // 1000
			.button({ translate: 'hostOption.time.noon' }, "textures/global/ui/time_noon")           // 6000
			.button({ translate: 'hostOption.time.sunset' }, "textures/global/ui/time_sunset")       // 12000
			.button({ translate: 'hostOption.time.night' }, "textures/global/ui/time_night")         // 13000
			.button({ translate: 'hostOption.time.midnight' }, "textures/global/ui/time_midnight");  // 18000
		SleepUI.show(player).then(r => {
			if (r.canceled) return;
			const selectedTime = [23000, 1000, 6000, 12000, 13000, 18000][r.selection];
			this.targetTime = selectedTime;
		});
	}
	static checkSpawnPoint(player, playerSpawnpoint, newSpawnpoint) {
		const areSpawnPointsEqual = playerSpawnpoint?.dimension.id === newSpawnpoint.dimension.id && playerSpawnpoint?.x === newSpawnpoint.x && playerSpawnpoint?.y === newSpawnpoint.y && playerSpawnpoint?.z === newSpawnpoint.z;
		if (!areSpawnPointsEqual) {
			player.setSpawnPoint(newSpawnpoint);
			player.sendMessage({ rawtext: [{ text: '§i' }, { translate: 'tile.bed.respawnSet' }] });
		}
	}
	static checkDimensionExplosion(dimension, center) {
		if (dimension.id !== "minecraft:overworld") {
			dimension.createExplosion(center, 5, {breaksBlocks: true, causesFire: true});
			return true;
		} return false;
	}
}
class McfBed {
	constructor() {
		this.onPlayerInteract = this.onPlayerInteract.bind(this);
	}
	sleepPlayer(player, bed) {
		bed.getComponent("rideable").addRider(player);
		if (!SleepSystem.targetTime) SleepSystem.PlayersSleep(player);
	}
	onPlayerInteract(e, p) {
		const { player, block, dimension } = e;
		let bed = dimension.getEntitiesAtBlockLocation(block.location).find((entity) => entity.matches({ type: p.params.entityId }));
		if (bed) return;
		const blockPart = block.permutation.getState("mc:block_parts");
		if (blockPart === 0) return;
		const direction = block.permutation.getState(directionState);
		const spawnOffset = p.params.offsets;
		bed = spawnEntityRotatedByBlock("mcf:sleeping_bed", block, direction, spawnOffset);
		if (SleepSystem.checkDimensionExplosion(dimension, block.center())) return;
		SleepSystem.checkSpawnPoint(player, player.getSpawnPoint(), { 
			x: Math.round(bed.location.x),
			y: Math.round(bed.location.y),
			z: Math.round(bed.location.z),
			dimension: player.dimension
		});
		if (MrCrayfishConfig.onlySleepAtNight) {
			if (SleepSystem.isNightTime()) this.sleepPlayer(player, bed);
			else player.sendMessage({ rawtext: [{ text: '§i' }, { translate: 'tile.bed.noSleep' }] });
		} else this.sleepPlayer(player, bed);
	}
}
system.beforeEvents.startup.subscribe((i) => {
	i.blockComponentRegistry.registerCustomComponent("mcf:update_old_cardinal_blocks", new McfUpdateOldCardinalBlocks());
	i.blockComponentRegistry.registerCustomComponent("mcf:update_cardinal_blocks", new McfUpdateCardinalBlocks());
	i.blockComponentRegistry.registerCustomComponent("mcf:update_old_side_blocks", new McfUpdateOldSideBlocks());
	i.blockComponentRegistry.registerCustomComponent("mcf:update_side_blocks", new McfUpdateSideBlocks());
	i.blockComponentRegistry.registerCustomComponent("mcf:sit_old", new McfSit());
	i.blockComponentRegistry.registerCustomComponent("mcf:bounce", new McfBounce());
	i.blockComponentRegistry.registerCustomComponent("mcf:blinds", new McfBlinds());
	i.blockComponentRegistry.registerCustomComponent("mcf:fill_sink", new McfSink());
	i.blockComponentRegistry.registerCustomComponent("mcf:grill", new McfGrill());
	i.blockComponentRegistry.registerCustomComponent("mcf:place_container", new McfPlaceContainer());
	i.blockComponentRegistry.registerCustomComponent("mcf:interact_container", new McfInteractContainer());
	i.blockComponentRegistry.registerCustomComponent("mcf:sofa", new McfSofa());
	i.blockComponentRegistry.registerCustomComponent("mcf:play_sound", new McfPlaySound());
	i.blockComponentRegistry.registerCustomComponent("mcf:unpressed_event", new McfUnpressDoorBell());
	i.blockComponentRegistry.registerCustomComponent("mcf:place_wall", new McfPlaceBlockWall());
	i.blockComponentRegistry.registerCustomComponent("mcf:fairy_light", new McfFairyLight());
	i.blockComponentRegistry.registerCustomComponent("mcf:large_block_placer", new McfLargeBlockPlacer());
	i.blockComponentRegistry.registerCustomComponent("mcf:bath", new McfBath());
	i.blockComponentRegistry.registerCustomComponent("mcf:tap", new McfTap());
	i.blockComponentRegistry.registerCustomComponent("mcf:add_amount_items", new McfAddItem());
	i.blockComponentRegistry.registerCustomComponent("mcf:chopping_board", new McfChoppingBoard());
	i.blockComponentRegistry.registerCustomComponent("mcf:fire_alarm", new McfFireAlarm());
	i.blockComponentRegistry.registerCustomComponent("mcf:toaster", new McfToaster());
	i.blockComponentRegistry.registerCustomComponent("mcf:device_use", new McfDeviceUse());
	i.blockComponentRegistry.registerCustomComponent("mcf:switch_place", new McfSwitchPlace());
	i.blockComponentRegistry.registerCustomComponent("mcf:place_item", new McfItemPlacer());
	i.blockComponentRegistry.registerCustomComponent("mcf:chimney", new McfChimney());
	i.blockComponentRegistry.registerCustomComponent("mcf:campfire", new McfCampfire());
	i.blockComponentRegistry.registerCustomComponent("mcf:computer", new McfComputer());
	i.blockComponentRegistry.registerCustomComponent("mcf:printer", new McfPrinterBlock());
	i.blockComponentRegistry.registerCustomComponent("mcf:stairs", new McfModernStairs());
	i.blockComponentRegistry.registerCustomComponent("mcf:stairs_check", new McfModernStairsCheck());
	i.blockComponentRegistry.registerCustomComponent("mcf:open_present", new McfOpenPresent());
	i.blockComponentRegistry.registerCustomComponent("mcf:rotate_by_45", new McfRotateBy45());
	i.blockComponentRegistry.registerCustomComponent("mcf:place_ceiling_fan", new McfCeilingFan());
	i.blockComponentRegistry.registerCustomComponent("mcf:stereo_play_sound", new McfStereo());
	i.blockComponentRegistry.registerCustomComponent("mcf:fence_gate", new McfFenceGate());
	i.blockComponentRegistry.registerCustomComponent("mcf:blender", new McfBlender());
	i.blockComponentRegistry.registerCustomComponent("mcf:place_block_face", new McfPlaceBlockFace());
	i.blockComponentRegistry.registerCustomComponent("mcf:shower_head", new McfShowerHead());
	i.blockComponentRegistry.registerCustomComponent("mcf:bin", new McfBin());
	i.blockComponentRegistry.registerCustomComponent("mcf:interface", new McfCreateInterface());
	i.blockComponentRegistry.registerCustomComponent("mcf:lamp", new McfLamp());
	i.blockComponentRegistry.registerCustomComponent("mcf:sleep", new McfBed());
	i.blockComponentRegistry.registerCustomComponent("mcf:break_new_large_block", new McfBreakLargeBlock());
	try {
		i.blockComponentRegistry.registerCustomComponent("mcf:block_connection", { onPlace: (e, p) => McfBlockConnections.onPlace(e, p) });
		McfBlockConnections.registerEvent();
	} catch (e) {}

	i.itemComponentRegistry.registerCustomComponent("mcf:switch_use", new McfSwitchUse());
	i.itemComponentRegistry.registerCustomComponent("mcf:seal_item", new McfSealItem());
	i.itemComponentRegistry.registerCustomComponent("mcf:open_sealed_item", new McfOpedSealedItem());
	i.itemComponentRegistry.registerCustomComponent("mcf:place_present", new McfPlacePresent());

	i.blockComponentRegistry.registerCustomComponent("mr:rock_path", new MrRockPath());
	i.blockComponentRegistry.registerCustomComponent("mr:mail_box", new MrMailBox());
	i.blockComponentRegistry.registerCustomComponent("mr:post_box", new MrPostBox());
	i.blockComponentRegistry.registerCustomComponent("mr:counter_detection", new MrCounter());
	i.blockComponentRegistry.registerCustomComponent("mcf:break_large_block", new McfOldBreakLargeBlock());
	i.blockComponentRegistry.registerCustomComponent("mcf:old_check_large_block", new McfOldCkeckLargeBlock());
	i.blockComponentRegistry.registerCustomComponent("mcf:old_place_large_block", new McfOldPlaceLargeBlocks());
	i.blockComponentRegistry.registerCustomComponent("mr:place_clock", new McfClock());
});
system.afterEvents.scriptEventReceive.subscribe((e) => {
	const { id, sourceEntity, message } = e;
	if (!sourceEntity?.isValid) return
	if (!sourceEntity.matches({ families: [ "mr_return_inv" ] })) return;
	if (id !== "mr:return_inventory_slots") return;
	const inv = sourceEntity.getComponent("inventory").container;
	for (let i = 0; i < Number(message); i++) {
		const item = inv.getItem(i);
		if (item) sourceEntity.dimension.spawnItem(item, sourceEntity.location);
	}
	sourceEntity.remove();
});