import { world, system, Entity, ItemStack, BlockPermutation, Block, Player } from '@minecraft/server';
import { getRandomQuantity, getMultipleLoot, lookAtEntity, notBreakableBlocks } from '../GlobalVars';
import { Vec3 } from "../Vec3";

/**
 * 
 * @param {String} id 
 * @param {Entity} sourceEntity 
 * @param {*} message 
 */
export function machines(id, sourceEntity, message) {
    if (id === `cy_cr:potion_farm`) {
        if (message === `tryToAddWitch`) {
            const tag = `cy_cr:potion_farm_interact`;
            const mob = `minecraft:witch`;
            const property = `cy_cr:has_witch`;
            const text = `translate.cy_cr.only_witch`;

            addMob(sourceEntity, mob, tag, property, text);
        };
        if (message === `eating`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:decrease_food`);
                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            resetHunger(sourceEntity)
                        }
                    }, 30)
                }
            }, 74.4)
        }
    };
    if (id === `cy_cr:string_farm`) {
        if (message === `tryToAddSpider`) {
            const tag = `cy_cr:string_farm_interact`;
            const mob = `minecraft:spider`;
            const property = `cy_cr:has_spider`;
            const text = `translate.cy_cr.only_spider`;

            addMob(sourceEntity, mob, tag, property, text);
        };
        if (message === `work`) {
            system.runTimeout(() => {
                const interval = system.runInterval(() => {
                    if (sourceEntity?.isValid() === true) {
                        const state = sourceEntity?.getProperty(`cy_cr:on`) || "off";
                        if (state === `off`) {
                            system.clearRun(interval)
                        };
                        world.playSound(`random.pop`, sourceEntity?.location);
                        sourceEntity?.getComponent("inventory").container?.addItem(new ItemStack(`minecraft:string`, getRandomQuantity(1, 3)))
                    } else {
                        system.clearRun(interval)
                    }
                }, 24)
            }, 19.2)
        };
        if (message === `eating`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:decrease_food`);
                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            sourceEntity?.triggerEvent(`cy_cr:decrease_water`);
                            system.runTimeout(() => {
                                if (sourceEntity?.isValid() === true) {
                                    resetHunger(sourceEntity)
                                }
                            }, 12.6)
                        }
                    }, 26.4)
                }
            }, 25.6)
        }
    };
    if (id === `cy_cr:arrow_farm`) {
        if (message === `tryToAddSkeleton`) {
            const tag = `cy_cr:arrow_farm_interact`;
            const mob = `minecraft:skeleton`;
            const property = `cy_cr:has_skeletons`;
            const text = `translate.cy_cr.only_skeleton`;

            addMob(sourceEntity, mob, tag, property, text);
        };
        if (message === `eating`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:decrease_food`);
                    sourceEntity?.triggerEvent(`cy_cr:decrease_water`);
                    resetHunger(sourceEntity)
                }
            }, 41.6)
        }
    };
    if (id === `cy_cr:tear_farm`) {
        if (message === `tryToAddGhast`) {
            const tag = `cy_cr:tear_farm_interact`;
            const mob = `minecraft:ghast`;
            const property = `cy_cr:has_ghast`;
            const text = `translate.cy_cr.only_ghast`;

            addMob(sourceEntity, mob, tag, property, text);
        }
    };
    if (id === `cy_cr:eggs_farm`) {
        if (message === `tryToAddChicken`) {
            const tag = `cy_cr:eggs_farm_interact`;
            const mob = `minecraft:chicken`;
            const property = `cy_cr:has_chickens`;
            const text = `translate.cy_cr.only_chicken`;

            addMob(sourceEntity, mob, tag, property, text);
        };
        if (message === `eating`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:decrease_water`);
                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            sourceEntity?.triggerEvent(`cy_cr:decrease_food`);
                            resetHunger(sourceEntity)
                        }
                    }, 24)
                }
            }, 32.8)
        }
    };
    if (id === `cy_cr:turret`) {
        if (message === `arrow`) {
            waitForItems(sourceEntity, new ItemStack(`minecraft:arrow`, 1), 1, `cy_cr:has_arrow`)
        };
        if (message === `start`) {
            start()
        };
        function start() {
            const interval = system.runInterval(() => {
                if (sourceEntity?.isValid() === true) {
                    const state = sourceEntity?.getProperty(`cy_cr:state`) || `off`;
                    const reloaded = sourceEntity?.getProperty(`cy_cr:reloaded`) || false;
                    const hasArrow = sourceEntity?.getProperty(`cy_cr:has_arrow`) || false;

                    if (state === `on`) {
                        const entities = sourceEntity.dimension.getEntities({ location: sourceEntity?.location, families: [`monster`], excludeFamilies: [`inanimate`, `despawn`], maxDistance: 20, minDistance: 1 });

                        const forwardVector = sourceEntity.getViewDirection();
                        const thresholdAngle = Math.PI / 4; // 45 degrees

                        const entitiesInFront = entities.filter(entity => {
                            const toEntityVector = Vec3.subtract(entity.location, sourceEntity.location);
                            const toEntityVectorNormal = Vec3.normalize(toEntityVector);

                            const dotProduct = Vec3.dot(forwardVector, toEntityVectorNormal);
                            const angle = Math.acos(dotProduct);

                            if (angle <= thresholdAngle) {
                                const rayCast = sourceEntity?.dimension.getEntitiesFromRay(sourceEntity.getHeadLocation(), toEntityVectorNormal, { families: [`monster`], excludeFamilies: [`inanimate`, `despawn`], maxDistance: 20, includePassableBlocks: false, includeLiquidBlocks: false, ignoreBlockCollision: false })[0];
                                return rayCast !== undefined;
                            };
                        });

                        let closestEntity = null;
                        let minDistance = Infinity;

                        if (entitiesInFront.length > 0) {
                            entitiesInFront.forEach(entity => {
                                const distance = Vec3.distance(entity.location, sourceEntity.location);
                                if (distance < minDistance) {
                                    minDistance = distance;
                                    closestEntity = entity;
                                }
                            });
                        };
                        if (closestEntity !== null) {
                            const result = lookAtEntity(closestEntity, sourceEntity);
                            sourceEntity?.setProperty(`cy_cr:rot2_y`, Math.max(-45, Math.min(45, result.yaw)));
                            sourceEntity?.setProperty(`cy_cr:rot2_x`, Math.max(-45, Math.min(45, -result.pitch)));
                            system.run(() => {
                                if (reloaded === true && hasArrow === true) {
                                    system.clearRun(interval);
                                    shoot()
                                }
                            })
                        }
                    }
                } else {
                    system.clearRun(interval)
                }
            }, 5)
        }
        function shoot() {
            sourceEntity?.playAnimation(`animation.cy_cr_turret.ShootFront`, { controller: "cy_cr:turret", stopExpression: "q.any_animations_finished" });

            const arrow = sourceEntity?.dimension.spawnEntity(`minecraft:arrow`, sourceEntity?.getHeadLocation());
            const yaw = sourceEntity?.getProperty(`cy_cr:rot2_y`) ?? 0;
            const yaw2 = sourceEntity?.getProperty(`cy_cr:rot_y`) ?? 0;
            const pitch = sourceEntity?.getProperty(`cy_cr:rot2_x`) ?? 0;
            arrow.setRotation({ x: -pitch, y: yaw + yaw2 });
            const view = arrow?.getViewDirection();
            arrow.getComponent("projectile").owner = sourceEntity;
            arrow.getComponent("projectile").shoot(Vec3.multiply(view, 2));

            waitForItems(sourceEntity, new ItemStack(`minecraft:arrow`, 1), 1, `cy_cr:has_arrow`);

            system.runTimeout(() => {
                sourceEntity?.triggerEvent(`cy_cr:shoot`);
                start();
            }, 5)
        }
    };
    if (id === `cy_cr:propulser`) {
        const interval = system.runInterval(() => {
            if (sourceEntity?.isValid()) {
                const state = sourceEntity?.getProperty('cy_cr:state') || 'off';
                if (state === 'on') {
                    const entities = sourceEntity?.dimension.getEntities({
                        location: {
                            x: sourceEntity.getHeadLocation().x - 1,
                            y: sourceEntity.getHeadLocation().y,
                            z: sourceEntity.getHeadLocation().z - 1
                        },
                        volume: { x: 1, y: 20, z: 1 },
                        excludeFamilies: ['cy_cr:machine']
                    });
                    if (entities.length > 0) {
                        entities.forEach(entity => {
                            if (entity.typeId !== 'minecraft:player') {
                                const vel = entity?.getVelocity();
                                if (vel) {
                                    const desiredVelY = 0.4;
                                    let impulseY = desiredVelY - vel.y;
                                    const maxImpulse = 0.05;

                                    impulseY = Math.max(Math.min(impulseY, maxImpulse), -maxImpulse);

                                    entity.applyImpulse({ x: 0, y: impulseY, z: 0 });
                                } else {
                                    entity.applyImpulse({ x: 0, y: 0.4, z: 0 });
                                }
                            } else {
                                entity.applyKnockback(0, 0, 0, 0.4);
                            }
                        });
                    }
                } else {
                    system.clearRun(interval);
                }
            } else {
                system.clearRun(interval);
            }
        }, 3);
    };
    if (id === `cy_cr:charge_station`) {
        if (message === `start`) {
            const golem = sourceEntity?.dimension.spawnEntity(`cy_cr:redstone_golem`, sourceEntity.location);
            sourceEntity?.getComponent("rideable")?.addRider(golem);
            sourceEntity?.addTag(`${sourceEntity.id}`)
            golem?.addTag(`${sourceEntity.id}`)
        };
        if (message === `on`) {
            const golem = sourceEntity.dimension.getEntities({ location: sourceEntity.location, type: `cy_cr:redstone_golem`, tags: [`${sourceEntity.id}`] })[0];
            golem.setProperty(`cy_cr:state`, `on`);
        };
        if (message === `off`) {
            const golem = sourceEntity.dimension.getEntities({ location: sourceEntity.location, type: `cy_cr:redstone_golem`, tags: [`${sourceEntity.id}`] })[0];
            golem.setProperty(`cy_cr:state`, `off`);
        };
        if (message === `destroy`) {
            const golem = sourceEntity.dimension.getEntities({ location: sourceEntity.location, type: `cy_cr:redstone_golem`, tags: [`${sourceEntity.id}`] })[0];
            golem.kill()
        }
    };
    if (id === `cy_cr:redstone_golem`) {
        if (message === `stand`) {
            const interval = system.runInterval(() => {
                if (sourceEntity?.isValid() === true) {
                    const state = sourceEntity?.getProperty(`cy_cr:state`) || `off`;
                    const battery = sourceEntity?.getProperty(`cy_cr:battery`) ?? 0;
                    const id = sourceEntity?.getTags()[0];
                    const station = sourceEntity?.dimension.getEntities({ location: sourceEntity?.location, type: `cy_cr:charge_station`, tags: [`${id}`] })[0];
                    const distance = Vec3.distance(station?.location, sourceEntity?.location);

                    if (state === `off` || battery === 0) {
                        system.clearRun(interval);
                        return;
                    };
                    if (distance >= 20 && !sourceEntity?.hasTag(`cy_cr:go_home`)) {
                        sourceEntity?.triggerEvent(`cy_cr:go_home`)
                    } else if (distance <= 10 && sourceEntity?.hasTag(`cy_cr:go_home`)) {
                        sourceEntity?.triggerEvent(`cy_cr:stop_home`)
                    }
                } else {
                    system.clearRun(interval);
                    return;
                }
            }, 10)
        };
        if (message === `ride`) {
            const id = sourceEntity?.getTags()[0];
            const station = sourceEntity?.dimension.getEntities({ location: sourceEntity?.location, type: `cy_cr:charge_station`, tags: [`${id}`] })[0];
            station.getComponent("rideable").addRider(sourceEntity)
        }
    };
    if (id === `cy_cr:drill`) {
        if (message === `start`) {
            const cardinal = sourceEntity?.getProperty(`cy_cr:cardinal`) || `Up`;
            sourceEntity.addTag(`cy_cr:drill_activated`);
            function startOtherDrills() {
                function drillPosVertical() {
                    const coords1 = { x: 0.5, y: 0, z: 0 };
                    const coords2 = { x: -0.5, y: 0, z: 0 };
                    const coords3 = { x: 0, y: 0, z: 0.5 };
                    const coords4 = { x: 0, y: 0, z: -0.5 };

                    return [coords1, coords2, coords3, coords4];
                };
                function drillPosX() {
                    const coords1 = { x: 0.5, y: 0, z: 0 };
                    const coords2 = { x: -0.5, y: 0, z: 0 };
                    const coords3 = { x: 0, y: 0.5, z: 0 };
                    const coords4 = { x: 0, y: -0.5, z: 0 };

                    return [coords1, coords2, coords3, coords4];
                };
                function drillPosZ() {
                    const coords1 = { x: 0.0, y: 0, z: 0.5 };
                    const coords2 = { x: 0.0, y: 0, z: -0.5 };
                    const coords3 = { x: 0, y: 0.5, z: 0 };
                    const coords4 = { x: 0, y: -0.5, z: 0 };

                    return [coords1, coords2, coords3, coords4];
                };

                let otherDrillCoords = [];

                if (cardinal === `Up`) {
                    const drillPos = drillPosVertical()
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos);
                    })
                };
                if (cardinal === `Down`) {
                    const drillPos = drillPosVertical();
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos);
                    })
                };
                if (cardinal === `North`) {
                    const drillPos = drillPosX();
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos)
                    })
                };
                if (cardinal === `South`) {
                    const drillPos = drillPosX();
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos)
                    })
                };
                if (cardinal === `East`) {
                    const drillPos = drillPosZ();
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos)
                    })
                };
                if (cardinal === `West`) {
                    const drillPos = drillPosZ();
                    drillPos.forEach(pos => {
                        otherDrillCoords.push(pos)
                    })
                };

                otherDrillCoords.forEach(coord => {
                    const pos = Vec3.add(sourceEntity.location, coord);
                    const otherDrill = sourceEntity.dimension.getEntities({ location: pos, maxDistance: 1, type: `cy_cr:drill`, propertyOptions: [{ propertyId: "cy_cr:state", value: `off` }, { propertyId: `cy_cr:cardinal`, value: cardinal }], closest: 1, excludeTags: [`cy_cr:drill_activated`] })[0];

                    if (otherDrill) {
                        otherDrill.triggerEvent(`cy_cr:state_on`)
                    }
                });
                return;
            };
            function removeTag(tag) {
                system.runTimeout(() => {
                    if (sourceEntity?.isValid() === true) {
                        sourceEntity?.removeTag(tag)
                    }
                }, 20)
            };

            startOtherDrills();

            const interval = system.runInterval(() => {
                if (sourceEntity?.isValid() === true) {
                    const state = sourceEntity?.getProperty(`cy_cr:state`) || `off`;
                    const breakState = sourceEntity?.getDynamicProperty(`cy_cr:break_state`) ?? 0;
                    const player = sourceEntity?.dimension.getPlayers({ location: sourceEntity?.location, maxDistance: 20, closest: 1 })[0];
                    let coords;
                    let block;

                    startOtherDrills();

                    if (state === `off`) {
                        sourceEntity?.runCommandAsync(`stopsound @a[r=10] machine.cy_cr.drill.loop`);
                        system.clearRun(interval);
                        return;
                    };
                    if (player === undefined) {
                        sourceEntity?.triggerEvent(`cy_cr:state_off`);
                        sourceEntity?.runCommandAsync(`stopsound @a[r=10] machine.cy_cr.drill.loop`);
                        system.clearRun(interval);
                        return;
                    }

                    if (cardinal === `Up`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: 0, y: -1, z: 0 }));
                        coords = { x: 0, y: 0.5, z: 0 };
                    };
                    if (cardinal === `Down`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: 0, y: 2, z: 0 }));
                        coords = { x: 0, y: 1.5, z: 0 };
                    };
                    if (cardinal === `North`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: 0, y: 0, z: 1 }));
                        coords = { x: 0, y: 0.5, z: 0 }
                    };
                    if (cardinal === `South`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: 0, y: 0, z: -1 }));
                        coords = { x: 0, y: 0.5, z: 0 };
                    };
                    if (cardinal === `East`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: -1, y: 0, z: 0 }));
                        coords = { x: 0, y: 0.5, z: 0 };
                    };
                    if (cardinal === `West`) {
                        block = sourceEntity?.dimension.getBlock(Vec3.add(sourceEntity.location, { x: 1, y: 0, z: 0 }));
                        coords = { x: 0, y: 0.5, z: 0 };
                    };

                    if (block && !notBreakableBlocks.includes(block?.typeId)) {
                        world.playSound(`dig.stone`, sourceEntity?.location);
                        if (breakState === 5) {
                            const item = block?.getItemStack(1, true);

                            sourceEntity?.teleport({ x: block.center().x - coords.x, y: block.center().y - coords.y, z: block.center().z - coords.z });
                            block?.setPermutation(BlockPermutation.resolve(`minecraft:air`));
                            block?.dimension.spawnItem(item, block.center());
                            sourceEntity?.setDynamicProperty(`cy_cr:break_state`, 0);
                        } else {
                            sourceEntity?.setDynamicProperty(`cy_cr:break_state`, breakState + 1);
                        }
                    } else if (notBreakableBlocks.includes(block?.typeId)) {
                        sourceEntity?.runCommandAsync(`titleraw @a[r=10] actionbar { "rawtext": [ { "translate": "translate.cy_cr.drill.cant_break" }]}`)
                        sourceEntity?.triggerEvent(`cy_cr:state_off`);
                        sourceEntity?.runCommandAsync(`stopsound @a[r=10] machine.cy_cr.drill.loop`);
                        system.clearRun(interval);
                        removeTag(`cy_cr:drill_activated`);
                        return;
                    } else {
                        sourceEntity?.triggerEvent(`cy_cr:state_off`);
                        sourceEntity?.runCommandAsync(`stopsound @a[r=10] machine.cy_cr.drill.loop`);
                        system.clearRun(interval);
                        removeTag(`cy_cr:drill_activated`);
                        return;
                    }
                } else {
                    system.clearRun(interval);
                    return;
                }
            }, 5);
        }
    };
    if (id === `cy_cr:milk_farm`) {
        if (message === `tryToAddCow`) {
            const tag = `cy_cr:milk_farm_interact`;
            const mob = `minecraft:cow`;
            const property = `cy_cr:has_cows`;
            const text = `translate.cy_cr.only_cow`;

            addMob(sourceEntity, mob, tag, property, text);
        };
        if (message === `eating`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:decrease_water`);
                    sourceEntity?.triggerEvent(`cy_cr:decrease_food`);
                    resetHunger(sourceEntity);
                }
            }, 32)
        }
    };
    if (id === `cy_cr:trap`) {
        if (message === `activate`) {
            const entities = sourceEntity?.dimension.getEntities({ location: sourceEntity.location, maxDistance: 2, families: [`monster`] });

            if (entities.length > 0) {
                entities.forEach(entity => {
                    entity?.applyDamage(4, { cause: "contact" });
                    entity?.teleport(Vec3.add(entity?.location, { x: 0, y: 1, z: 0 }), { keepVelocity: true, checkForBlocks: true })
                })
            }
        }
    };
    if (id === `cy_cr:big_door`) {
        if (message === `open`) {
            const interval = system.runInterval(() => {
                if (sourceEntity?.isValid() === true) {
                    const player = sourceEntity?.dimension.getPlayers({ location: sourceEntity?.location, maxDistance: 6, closest: 1 })[0];
                    if (player === undefined) {
                        sourceEntity?.triggerEvent(`cy_cr:off`);
                        system.clearRun(interval);
                        return;
                    }
                } else {
                    system.clearRun(interval);
                    return;
                }
            }, 10)
        };
        if (message === `tag`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.removeTag(`cy_cr:open_door`)
                }
            }, 2)
        }
    };
    if (id === `cy_cr:cannon`) {
        if (message === `shoot`) {
            const canShoot = sourceEntity?.getProperty(`cy_cr:can_shoot_again`);
            if (canShoot) {
                const yaw = sourceEntity?.getProperty(`cy_cr:rot_y`) ?? 0;

                const directionVector = Vec3.directionFromRotation(0, yaw);

                const tntSpawnCoords = {
                    x: sourceEntity.location.x + directionVector.x * 2,
                    y: sourceEntity.location.y + directionVector.y * 2 + 1,
                    z: sourceEntity.location.z + directionVector.z * 2
                };

                const tnt = sourceEntity?.dimension.spawnEntity(`cy_cr:tnt_projectile`, tntSpawnCoords);
                tnt.getComponent("projectile").owner = sourceEntity;
                tnt.getComponent("projectile").shoot(Vec3.multiply(directionVector, 3));
                tnt.setProperty(`cy_cr:rot_y`, yaw);
                sourceEntity?.triggerEvent(`cy_cr:shoot`);
                sourceEntity?.setProperty(`cy_cr:can_shoot_again`, false)
            } else return;
        };
    };
    if (id === `cy_cr:furnace`) {
        if (message === `waitItems`) {
            const inv = sourceEntity?.getComponent("inventory")?.container;
            const putFoodProp = sourceEntity.getProperty(`cy_cr:put_food`);
            const itemsToCook = [];
            const itemsToGet = [];

            if (!putFoodProp) {
                const interval = system.runInterval(() => {
                    if (sourceEntity?.isValid() === true) {
                        for (let i = 0; i < inv.size; i++) {
                            const itemInv = inv.getItem(i);
                            let itemInvCooked;
                            if (itemInv) {
                                const noNamespace = itemInv.typeId.replace(`minecraft:`, '')
                                let newId;
                                if (noNamespace.includes(`raw`)) {
                                    newId = noNamespace.replace(`raw`, '')
                                } else {
                                    newId = noNamespace;
                                };
                                try {
                                    itemInvCooked = new ItemStack(`minecraft:cooked_${newId}`);
                                } catch { }
                            };

                            if (itemInvCooked) {
                                if (itemInv.amount >= 2) {
                                    itemsToCook.push(itemInv.typeId);
                                    itemsToGet.push(itemInvCooked.typeId);
                                    system.clearRun(interval);
                                    pushItems();
                                    removeItems(sourceEntity, itemInv, 2);
                                    break;
                                }
                            }
                        }
                    } else {
                        system.clearRun(interval);
                        return;
                    }
                }, 5);

                function pushItems() {
                    if (itemsToCook.length > 0 && itemsToGet.length > 0) {
                        sourceEntity?.setProperty(`cy_cr:put_food`, true);
                        sourceEntity?.setDynamicProperty(`cy_cr:itemsToCook`, JSON.stringify(itemsToCook));
                        sourceEntity?.setDynamicProperty(`cy_cr:itemsToGet`, JSON.stringify(itemsToGet));
                        putFood();
                    }
                }
            } else {
                putFood()
            }

            function putFood() {
                const itemsToCookProp = JSON.parse(sourceEntity?.getDynamicProperty(`cy_cr:itemsToCook`));

                system.runTimeout(() => {
                    if (sourceEntity?.isValid() === true) {
                        system.runTimeout(() => {
                            if (sourceEntity?.isValid() === true) {
                                const coords1 = { x: -1, y: -2, z: 0 };
                                const coords2 = { x: 1, y: -2, z: 0 };
                                const localCoords1 = Vec3.getLocalCoords(sourceEntity, coords1);
                                const localCoords2 = Vec3.getLocalCoords(sourceEntity, coords2);

                                const entity1 = sourceEntity?.dimension.spawnEntity(`cy_cr:fake_item`, localCoords1);
                                entity1.addTag(`cy_cr:${sourceEntity.id}`);

                                if (itemsToCookProp.length > 1) {
                                    entity1.runCommandAsync(`replaceitem entity @s slot.weapon.mainhand 0 ${itemsToCookProp[0]}`);
                                } else {
                                    entity1.runCommandAsync(`replaceitem entity @s slot.weapon.mainhand 0 ${itemsToCookProp[0]}`);
                                };
                                entity1.setProperty(`cy_cr:furnace`, true);

                                system.runTimeout(() => {
                                    if (sourceEntity?.isValid() === true) {
                                        const entity2 = sourceEntity?.dimension.spawnEntity(`cy_cr:fake_item`, localCoords2);
                                        entity2.addTag(`cy_cr:${sourceEntity.id}`);

                                        if (itemsToCookProp.length > 1) {
                                            entity2.runCommandAsync(`replaceitem entity @s slot.weapon.mainhand 0 ${itemsToCookProp[1]}`);
                                        } else {
                                            entity2.runCommandAsync(`replaceitem entity @s slot.weapon.mainhand 0 ${itemsToCookProp[0]}`);
                                        };
                                        entity2.setProperty(`cy_cr:furnace`, true);

                                        system.runTimeout(() => {
                                            if (sourceEntity?.isValid() === true) {
                                                sourceEntity?.setProperty(`cy_cr:cooking`, true);
                                                sourceEntity?.setProperty(`cy_cr:put_food`, false)
                                            }
                                        }, 12.8)
                                    }
                                }, 16)
                            }
                        }, 24)
                    }
                }, 17.6)
            }
        };
        if (message === `despawn`) {
            const foods = sourceEntity?.dimension.getEntities({ location: sourceEntity.location, maxDistance: 5, tags: [`cy_cr:${sourceEntity.id}`] });
            foods.forEach(food => {
                food.remove();
            })
        };
        if (message === `coocked`) {
            const itemsToGetProp = JSON.parse(sourceEntity?.getDynamicProperty(`cy_cr:itemsToGet`));
            const foods = sourceEntity?.dimension.getEntities({ location: sourceEntity.location, maxDistance: 5, tags: [`cy_cr:${sourceEntity.id}`] });
            foods.forEach(food => {
                food.runCommandAsync(`replaceitem entity @s slot.weapon.mainhand 0 ${itemsToGetProp[0]}`)
            });

            const coords1 = { x: -1, y: -2, z: 0 };
            const coords2 = { x: 1, y: -2, z: 0 };
            const localCoords1 = Vec3.getLocalCoords(sourceEntity, coords1);
            const localCoords2 = Vec3.getLocalCoords(sourceEntity, coords2);

            const entity1 = sourceEntity?.dimension.getEntities({ location: localCoords1, maxDistance: 1, tags: [`cy_cr:${sourceEntity.id}`] })[0];
            const entity2 = sourceEntity?.dimension.getEntities({ location: localCoords2, maxDistance: 1, tags: [`cy_cr:${sourceEntity.id}`] })[0];

            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    entity2.remove();

                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            entity1.remove()
                        }
                    }, 4)
                }
            }, 16)
        }
        if (message === `collect`) {
            const itemsToGetProp = JSON.parse(sourceEntity?.getDynamicProperty(`cy_cr:itemsToGet`));
            const inv = sourceEntity?.getComponent("inventory").container;
            const item = new ItemStack(`${itemsToGetProp[0]}`, 2);
            inv.addItem(item);

            const foods = sourceEntity?.dimension.getEntities({ location: sourceEntity.location, maxDistance: 5, tags: [`cy_cr:${sourceEntity.id}`] });
            if (foods.length > 0) {
                foods.forEach(food => {
                    food.remove()
                });
            }
        }
    };
    //Vegetable farms
    if (id === `cy_cr:vegetable_farm`) {
        let property = ``;
        const messages = message.split(' ');
        const itemId = messages[0];
        let action = ``;
        if (messages.length > 0) {
            action = messages[1];
            if (sourceEntity.typeId === `cy_cr:cake_farm`) {
                if (messages[0] === `minecraft:wheat_seeds`) {
                    property = `cy_cr:wheat_seeds`
                } else if (messages[0] === `minecraft:sweet_berries`) {
                    property = `cy_cr:berry_seeds`
                } else if (messages[0] === `minecraft:sugar_cane`) {
                    property = `cy_cr:sugar_seeds`
                }
            } else {
                property = `cy_cr:has_seeds`
            }
        };
        const hasSeeds = sourceEntity?.getProperty(property);
        if (hasSeeds === false) {
            const amountNine = ["minecraft:sweet_berries", "minecraft:beetroot_seeds", "minecraft:carrot", "minecraft:nether_wart", "minecraft:potato", "minecraft:wheat_seeds"];
            const amountThree = ["minecraft:sugar_cane"];
            let amount = 0;
            if (amountNine.includes(itemId)) {
                amount = 9;
                if (sourceEntity?.typeId === `cy_cr:cake_farm`) {
                    amount = 4;
                }
            } else if (amountThree.includes(itemId)) {
                amount = 3;
                if (sourceEntity?.typeId === `cy_cr:cake_farm`) {
                    amount = 2
                }
            }
            const item = new ItemStack(itemId);
            waitForItems(sourceEntity, item, amount, property)
        };
        if (hasSeeds === true) {
            const amountNine = ["minecraft:sweet_berries", "minecraft:beetroot_seeds", "minecraft:carrot", "minecraft:nether_wart", "minecraft:potato", "minecraft:wheat_seeds"];
            const amountThree = ["minecraft:sugar_cane"];
            let amount = 0;
            if (amountNine.includes(itemId)) {
                amount = 9
            } else if (amountThree.includes(itemId)) {
                amount = 3
            }
            const item = new ItemStack(itemId);
            if (action === `removeItems`) {
                removeItems(sourceEntity, item, amount)
            }
        }
    };
    if (id === `cy_cr:beetroot_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:beetroot_seeds`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:carrot_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:carrot`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:netherwart_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:nether_wart`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:potato_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:potato`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:wheat_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:wheat_seeds`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:sugar_farm`) {
        if (message === `onCollect`) {
            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:reset_stages`);
                    const item = new ItemStack(`minecraft:sugar_cane`);
                    verifySeeds(sourceEntity, item, 9)
                }
            }, 64)
        }
    };
    if (id === `cy_cr:cake_farm`) {
        if (message === `tryToAddMob`) {
            const tag = `cy_cr:cake_farm_mob_interact`;
            const player = sourceEntity.dimension.getPlayers({ location: sourceEntity?.location, maxDistance: 10, tags: [tag], closest: 1 })[0];
            const text = `translate.cy_cr.only_mob`;
            const item = player.getComponent("equippable").getEquipmentSlot("Mainhand").getItem();

            if (player) {
                if (item && item.typeId === `cy_cr:redstone_capsule`) {
                    const itemLore = item.getLore();
                    let mobType = '';
                    let property = ``;
                    if (itemLore.length > 0) {
                        mobType = itemLore[3]?.split(' ')[2];
                    };
                    if (mobType === `minecraft:chicken`) {
                        property = `cy_cr:has_chickens`
                    };
                    if (mobType === `minecraft:cow`) {
                        property = `cy_cr:has_cow`
                    };
                    const property2 = sourceEntity.getProperty(property);

                    if (property !== ``) {
                        if (property === `cy_cr:has_cow`) {
                            if (property2 === false) {
                                addMob(sourceEntity, mobType, tag, property, text);
                            } else return;
                        };
                        if (property === `cy_cr:has_chickens`) {
                            if (property2 < 3) {
                                addMob(sourceEntity, mobType, tag, property, text);
                            } else return;
                        }
                    }
                }
            }
        };
        if (message.includes(`removeSeeds`)) {
            const messages = message.split(' ');
            const itemId = messages[1];
            const amountFour = ["minecraft:sweet_berries", "minecraft:wheat_seeds"];
            const amountTwo = ["minecraft:sugar_cane"];
            let amount = 0;
            if (amountFour.includes(itemId)) {
                amount = 4
            } else if (amountTwo.includes(itemId)) {
                amount = 2
            }
            const item = new ItemStack(itemId);
            removeItems(sourceEntity, item, amount);
        };
        if (message === `chickenEat`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:chicken_decrease_water`);
                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            sourceEntity?.triggerEvent(`cy_cr:chicken_decrease_food`);
                            sourceEntity?.setProperty(`cy_cr:chicken_hunger_time`, getRandomQuantity(240, 480))
                        }
                    }, 24)
                }
            }, 32.8)
        };
        if (message === `cowEat`) {
            system.runTimeout(() => {
                if (sourceEntity?.isValid() === true) {
                    sourceEntity?.triggerEvent(`cy_cr:milk_decrease_food`);
                    system.runTimeout(() => {
                        if (sourceEntity?.isValid() === true) {
                            sourceEntity?.triggerEvent(`cy_cr:milk_decrease_water`);
                            sourceEntity?.setProperty(`cy_cr:milk_hunger_time`, getRandomQuantity(240, 480))
                        }
                    }, 47.2)
                }
            }, 33.6)
        };
        if (message.includes(`onCollect`)) {
            const messages = message.split(' ');
            const property = messages[1];
            const type = messages[2];
            const itemId = messages[3];
            let amount = 0;

            sourceEntity?.triggerEvent(`cy_cr:${type}reset_stages`);
            const item = new ItemStack(itemId);
            if (itemId === `minecraft:sugar_cane`) {
                amount = 2;
            } else amount = 4;

            sourceEntity?.setProperty(`cy_cr:collecting`, true);
            verifySeeds(sourceEntity, item, amount, property)
        };
    };

    if (id === `cy_cr:clearInv`) {
        clearInventory(sourceEntity);
    };
    if (id === `cy_cr:rotate`) {
        system.run(() => {
            if (sourceEntity?.isValid() === true) {
                let rot = sourceEntity.getRotation().y;
                if (Math.round(-rot / 90) * 90 === 0 || Math.round(-rot / 90) * 90 === -180) {
                    rot -= 180;
                }
                sourceEntity?.setProperty(`cy_cr:rot_y`, Math.round(-rot / 90) * 90);
                sourceEntity?.setRotation({ x: 0, y: Math.round(-rot / 90) * 90 });
                sourceEntity?.triggerEvent(`cy_cr:barriers`);
            }
        })
    };
    if (id === `cy_cr:item_conveyor`) {
        switch (message) {
            case "cake_farm":
                itemConveyor({ x: 0, y: 0, z: -4 });
                break;
            case "small_door_wireless":
                itemConveyor({ x: 0, y: -3, z: -3 });
                break;
            case "big_door_wireless":
                itemConveyor({ x: 0, y: -3, z: -2 });
                break;
            case "propulser":
                itemConveyor({ x: 0, y: 0, z: -2 });
                break;
            case "tear_farm":
                itemConveyor({ x: 0, y: 0, z: -5 });
                itemConveyor({ x: 0, y: 0, z: -6 });
                break;
            case "wood_factory":
                itemConveyor({ x: 0, y: 0, z: -3 });
                break;
            case "cobble_factory":
                itemConveyor({ x: 0, y: 0, z: -3 });
                break;
            case "antirain_station": 
                itemConveyor({x: 0, y: 0, z: -1});
                break;
        };

        function itemConveyor(coord) {
            const pos = Vec3.getLocalCoords(sourceEntity, coord);
            const block = sourceEntity.dimension.getBlock(pos);

            if (block && block.typeId === `cy_cr:item_conveyor_end` || block.typeId === `cy_cr:item_conveyor_middle`) {
                const onProp = block.permutation.getState(`cy_cr:on`);

                if (onProp) {
                    sourceEntity?.triggerEvent(`cy_cr:state_on`)
                } else {
                    sourceEntity?.triggerEvent(`cy_cr:state_off`);
                    return;
                }
            }
        }
    }
};
function addMob(sourceEntity, mob, tag, property, text) {
    const player = sourceEntity.dimension.getPlayers({ location: sourceEntity?.location, maxDistance: 10, tags: [tag], closest: 1 })[0];

    if (player) {
        const item = player.getComponent("equippable").getEquipmentSlot("Mainhand").getItem();
        player.removeTag(tag);

        if (item && item.typeId === `cy_cr:redstone_capsule`) {
            const itemLore = item.getLore();
            let mobType = '';
            if (itemLore.length > 0) {
                mobType = itemLore[3]?.split(' ')[2];
            };
            if (mobType === mob) {
                if (property !== `cy_cr:has_skeletons` && property !== `cy_cr:has_chickens` && property !== `cy_cr:has_cows`) {
                    sourceEntity?.setProperty(property, true);
                } else {
                    const newProperty = sourceEntity?.getProperty(property);
                    sourceEntity?.setProperty(property, newProperty + 1);
                }
                item.setLore(null);
                player.getComponent("equippable").setEquipment("Mainhand", item);
            } else if (mobType !== '') {
                player.onScreenDisplay.setActionBar([{ translate: text }]);
                world.playSound(`note.banjo`, sourceEntity.location, { pitch: 0.5 })
            } else if (mobType === '') {
                player.onScreenDisplay.setActionBar([{ translate: "translate.cy_cr.capsule_empty" }]);
                world.playSound(`note.banjo`, sourceEntity.location, { pitch: 0.5 })
            }
        }
    }
};
function resetHunger(sourceEntity) {
    sourceEntity?.setProperty(`cy_cr:hunger_time`, getRandomQuantity(240, 480))
};
/**
 * 
 * @param {Entity} sourceEntity 
 * @param {ItemStack} item
 * @param {Number} amount 
 */
function waitForItems(sourceEntity, item, amount, property) {
    const inv = sourceEntity?.getComponent("inventory").container;
    const interval = system.runInterval(() => {
        if (sourceEntity?.isValid() === true) {
            for (let i = 0; i < inv.size; i++) {
                const itemInv = inv.getItem(i);

                if (itemInv && (itemInv.typeId === item.typeId && itemInv.amount >= amount)) {
                    sourceEntity?.setProperty(property, true);
                    system.clearRun(interval);
                    if (sourceEntity?.typeId === `cy_cr:berry_farm` || sourceEntity?.typeId === `cy_cr:turret`) {
                        for (let a = 0; a < amount; a++) {
                            if (itemInv.amount > 1) {
                                itemInv.amount -= 1;
                                inv.setItem(i, itemInv)
                            } else if (itemInv.amount === 1) {
                                inv.setItem(i, undefined)
                            }
                        };
                    }
                    break;
                } else {
                    if (sourceEntity?.typeId === `cy_cr:turret`) {
                        sourceEntity?.setProperty(property, false)
                    }
                }
            }
        } else {
            system.clearRun(interval)
        }
    }, 5)
};
/**
 * 
 * @param {Entity} sourceEntity 
 * @param {ItemStack} item
 * @param {Number} amount 
 */
function removeItems(sourceEntity, item, amount) {
    const inv = sourceEntity?.getComponent("inventory").container;
    for (let i = 0; i < inv.size; i++) {
        const itemInv = inv.getItem(i);
        if (itemInv && itemInv.typeId === item.typeId) {
            for (let a = 0; a < amount; a++) {
                if (itemInv.amount > 1) {
                    itemInv.amount -= 1;
                    inv.setItem(i, itemInv)
                } else if (itemInv.amount === 1) {
                    inv.setItem(i, undefined)
                }
            };
            break;
        }
    }
};
/**
 * 
 * @param {Entity} sourceEntity 
 * @param {ItemStack} item
 * @param {Number} amount 
 */
function verifySeeds(sourceEntity, item, amount, propertyId) {
    const inv = sourceEntity?.getComponent("inventory").container;
    let property;
    if (propertyId === undefined) {
        property = `cy_cr:has_seeds`
    } else {
        property = propertyId
    }
    for (let i = 0; i < inv.size; i++) {
        const itemInv = inv.getItem(i);
        if (itemInv && (itemInv.typeId === item.typeId && itemInv.amount >= amount)) {
            sourceEntity?.setProperty(property, true)
            break;
        } else {
            sourceEntity?.setProperty(property, false);
            waitForItems(sourceEntity, item, amount, property)
        }
    };
    system.runTimeout(() => {
        sourceEntity?.setProperty(`cy_cr:collecting`, false);
    }, 2)
};
/**
 * 
 * @param {Entity} sourceEntity 
 */
function clearInventory(sourceEntity) {
    const inv = sourceEntity?.getComponent("inventory").container;
    for (let i = 0; i < inv.size; i++) {
        const item = inv.getItem(i);
        if (item) {
            const random = (Math.random() - 0.3) * 0.3;
            const random2 = Math.random() * 360;
            const value = Math.random() * 0.5;
            system.runTimeout(() => {
                const itemEntity = sourceEntity?.dimension.spawnEntity(`cy_cr:fake_item`, Vec3.add(sourceEntity.location, { x: random, y: random + 0.2, z: random }));
                itemEntity?.setProperty(`cy_cr:rot_y`, random2);
                itemEntity?.runCommand(`replaceitem entity @s slot.weapon.mainhand 0 ${item.typeId}`);
                system.runTimeout(() => {
                    if (itemEntity?.isValid() === true) {
                        itemEntity?.remove();
                    }
                }, 15)
            }, value * 20)
        };
        inv.setItem(i, undefined)
    }
};
/**
 * 
 * @param {Player} player 
 * @param {Block} block 
 * @param {*} data 
 * @returns 
 */
export function drillPlace(player, block, data) {
    const blockFace = data.blockFace;
    const faceLoc = data.faceLocation;

    const existingDrill = player.dimension.getEntities({
        location: Vec3.add(block.location, faceLoc),
        maxDistance: 1,
        type: `cy_cr:drill`,
        closest: 1
    })[0];

    if (existingDrill) {
        data.cancel = true;
        return;
    };

    system.run(() => {
        const drill = player.dimension.getEntities({ location: Vec3.add(block.location, faceLoc), maxDistance: 1, type: `cy_cr:drill`, closest: 1 })[0];

        if (blockFace === `Up`) {
            drill?.teleport(Vec3.add(block.center(), { x: 0, y: 0.5, z: 0 }));
        };
        if (blockFace === `Down`) {
            drill?.teleport(Vec3.add(block.center(), { x: 0, y: -2.5, z: 0 }));
        };
        if (blockFace === `North`) {
            drill?.teleport(Vec3.add(block.center(), { x: 0, y: -0.5, z: -1 }));
        };
        if (blockFace === `South`) {
            drill?.teleport(Vec3.add(block.center(), { x: 0, y: -0.5, z: 1 }));
            drill?.setProperty(`cy_cr:rot_y`, 180);
            drill?.setRotation({ x: 0, y: 180 });
        };
        if (blockFace === `East`) {
            drill?.teleport(Vec3.add(block.center(), { x: 1, y: -0.5, z: 0 }));
            drill?.setProperty(`cy_cr:rot_y`, 90);
            drill?.setRotation({ x: 0, y: 90 });
        };
        if (blockFace === `West`) {
            drill?.teleport(Vec3.add(block.center(), { x: -1, y: -0.5, z: 0 }));
            drill?.setProperty(`cy_cr:rot_y`, -90);
            drill?.setRotation({ x: 0, y: -90 });
        };

        drill?.setProperty(`cy_cr:cardinal`, `${blockFace}`);
    })
};
/**
 * 
 * @param {Entity} entity 
 */
export function factoryPlace(entity) {
    const spawned = entity?.getDynamicProperty(`cy_cr:spawned`) || false;
    if (spawned === false) {
        let offsetVal = 0;

        if (entity?.typeId === `cy_cr:tear_farm`) {
            offsetVal = 4;
        } else if (entity?.typeId === `cy_cr:propulser`) {
            offsetVal = 1;
        } else if (entity?.typeId === `cy_cr:cake_farm`) {
            offsetVal = 4;
        } else {
            offsetVal = 2;
        }
        const coords = { x: 0, y: 0, z: -offsetVal };
        const result = Vec3.getLocalCoords(entity, coords);

        const block = entity?.dimension.getBlock(Vec3.add(result, { x: 0, y: -1, z: 0 }));

        const entity2 = entity?.dimension.spawnEntity(entity.typeId, Vec3.add(block.center(), { x: 0, y: 0.5, z: 0 }));
        entity2.setRotation({ x: 0, y: entity.getRotation().y });
        entity2?.setDynamicProperty(`cy_cr:spawned`, true);
        entity?.remove();
    }
};
