/* © 2019-2023, <@JeanLucasMCPE> - All Rights Reserved. */

import { BlockPermutation, system, world } from '@minecraft/server';

class Compostables {
  static registerCompostables = [
    { "item": "sunflowerdelight:sunflower_seed", "chance": 0.1 },
    { "item": "sunflowerdelight:peeled_sunflower_seed", "chance": 0.1 },
    { "item": "sunflowerdelight:halva", "chance": 0.3 },
        { "item": "sunflowerdelight:peony_flower", "chance": 0.15 },
            { "item": "sunflowerdelight:rosebud", "chance": 0.15 },
                { "item": "sunflowerdelight:lilac_flower", "chance": 0.15 },
                    { "item": "sunflowerdelight:shortbread_cookie", "chance": 0.25 },
                        { "item": "sunflowerdelight:gozinaki", "chance": 0.35 }
  ]
  static vanillaCompostables = [
    { "item": "minecraft:beetroot_seeds", "chance": 0.3 },
    { "item": "minecraft:dried_kelp", "chance": 0.3 },
    { "item": "minecraft:glow_berries", "chance": 0.3 },
    { "item": "minecraft:tallgrass", "chance": 0.3 },
    { "item": "minecraft:grass", "chance": 0.3 },
    { "item": "minecraft:hanging_roots", "chance": 0.3 },
    { "item": "minecraft:mangrove_roots", "chance": 0.3 },
    { "item": "minecraft:kelp", "chance": 0.3 },
    { "item": "minecraft:leaves", "chance": 0.3 },
    { "item": "minecraft:leaves2", "chance": 0.3 },
    { "item": "minecraft:mangrove_leaves", "chance": 0.3 },
    { "item": "minecraft:cherry_leaves", "chance": 0.3 },
    { "item": "minecraft:azalea_leaves", "chance": 0.3 },
    { "item": "minecraft:melon_seeds", "chance": 0.3 },
    { "item": "minecraft:moss_carpet", "chance": 0.3 },
    { "item": "minecraft:pink_petals", "chance": 0.3 },
    { "item": "minecraft:pitcher_pod", "chance": 0.3 },
    { "item": "minecraft:pumpkin_seeds", "chance": 0.3 },
    { "item": "minecraft:sapling", "chance": 0.3 },
    { "item": "minecraft:cherry_sapling", "chance": 0.3 },
    { "item": "minecraft:seagrass", "chance": 0.3 },
    { "item": "minecraft:small_dripleaf_block", "chance": 0.3 },
    { "item": "minecraft:sweet_berries", "chance": 0.3 },
    { "item": "minecraft:torchflower_seeds", "chance": 0.3 },
    { "item": "minecraft:wheat_seeds", "chance": 0.3 },
    { "item": "minecraft:cactus", "chance": 0.5 },
    { "item": "minecraft:dried_kelp_block", "chance": 0.5 },
    { "item": "minecraft:azalea_leaves_flowered", "chance": 0.5 },
    { "item": "minecraft:glow_lichen", "chance": 0.5 },
    { "item": "minecraft:melon_slice", "chance": 0.5 },
    { "item": "minecraft:nether_sprouts", "chance": 0.5 },
    { "item": "minecraft:sugar_cane", "chance": 0.5 },
    // { "item": "minecraft:tall_grass", "chance": 0.5 }, Here go the Tall Grass, according to the wiki. But for now it's data of "minecraft:double_plant", so dropping it in a composter will compost with a chance of 65% (prioritizing double flowers) and not 50%. Waiting for mojang to give it a unique identifier :/
    { "item": "minecraft:twisting_vines", "chance": 0.5 },
    { "item": "minecraft:vine", "chance": 0.5 },
    { "item": "minecraft:weeping_vines", "chance": 0.5 },
    { "item": "minecraft:apple", "chance": 0.65 },
    { "item": "minecraft:azalea", "chance": 0.65 },
    { "item": "minecraft:beetroot", "chance": 0.65 },
    { "item": "minecraft:big_dripleaf", "chance": 0.65 },
    { "item": "minecraft:carrot", "chance": 0.65 },
    { "item": "minecraft:cocoa_beans", "chance": 0.65 },
    { "item": "minecraft:yellow_flower", "chance": 0.65 },
    { "item": "minecraft:red_flower", "chance": 0.65 },
    { "item": "minecraft:double_plant", "chance": 0.65 }, // Sadly includes the Tall Grass, which should compost in a chance of 50% instead.
    { "item": "minecraft:crimson_fungus", "chance": 0.65 },
    { "item": "minecraft:warped_fungus", "chance": 0.65 },
    { "item": "minecraft:waterlily", "chance": 0.65 },
    { "item": "minecraft:melon_block", "chance": 0.65 },
    { "item": "minecraft:moss_block", "chance": 0.65 },
    { "item": "minecraft:brown_mushroom", "chance": 0.65 },
    { "item": "minecraft:red_mushroom", "chance": 0.65 },
    // { "item": "minecraft:mushroom_stem", "chance": 0.85 }, Here go the Mushroom Stem, according to the wiki. But for now it's data of "minecraft:brown_mushroom_block" and "minecraft:red_mushroom_block", so dropping it in a composter will compost with a chance of 85% (prioritizing mushroom blocks) and not 65%. Waiting for mojang to give it a unique identifier :/
    { "item": "minecraft:nether_wart", "chance": 0.65 },
    { "item": "minecraft:potato", "chance": 0.65 },
    { "item": "minecraft:pumpkin", "chance": 0.65 },
    { "item": "minecraft:carved_pumpkin", "chance": 0.65 },
    { "item": "minecraft:crimson_roots", "chance": 0.65 },
    { "item": "minecraft:warped_roots", "chance": 0.65 },
    { "item": "minecraft:sea_pickle", "chance": 0.65 },
    { "item": "minecraft:shroomlight", "chance": 0.65 },
    { "item": "minecraft:spore_blossom", "chance": 0.65 },
    { "item": "minecraft:wheat", "chance": 0.65 },
    { "item": "minecraft:baked_potato", "chance": 0.85 },
    { "item": "minecraft:bread", "chance": 0.85 },
    { "item": "minecraft:cookie", "chance": 0.85 },
    { "item": "minecraft:flowering_azalea", "chance": 0.85 },
    { "item": "minecraft:hay_block", "chance": 0.85 },
    { "item": "minecraft:brown_mushroom_block", "chance": 0.85 }, // Sadly includes the Stem, which should compost in a chance of 65% instead.
    { "item": "minecraft:red_mushroom_block", "chance": 0.85 }, // Sadly includes the Stem, which should compost in a chance of 65% instead.
    { "item": "minecraft:nether_wart_block", "chance": 0.85 },
    { "item": "minecraft:pitcher_plant", "chance": 0.85 },
    { "item": "minecraft:torchflower", "chance": 0.85 },
    { "item": "minecraft:warped_wart_block", "chance": 0.85 },
    { "item": "minecraft:cake", "chance": 1.0 },
    { "item": "minecraft:pumpkin_pie", "chance": 1.0 }
  ]
  static getCompostable(item) {
    return item = this.registerCompostables.findIndex(compostable => item === compostable.item);
  }
  static getVanillaCompostable(item) {
    return item = this.vanillaCompostables.findIndex(compostable => item === compostable.item);
  }
  static isCompostable(block) {
    for (let i = 0, state; i < 7; i++) {
      state = block.permutation.matches('minecraft:composter', {'composter_fill_level': i});
      if (state) return state;
    }
  }
  static getFillLevel(composter) {
    for (let i = 0; i < 9; i++) {
      if (composter.permutation.matches('minecraft:composter', {'composter_fill_level': i})) return i;
    }
  }
  static composting(composter, chance, hopper = false) {
    let composterFillLevel = this.getFillLevel(composter);
    if (Math.random() < chance || composterFillLevel <= 0) {
      composter.setPermutation(BlockPermutation.resolve('minecraft:composter', {'composter_fill_level': ++composterFillLevel}));
      if (composterFillLevel == 7) {
        system.runTimeout(() => {
          if (this.getFillLevel(composter) != composterFillLevel || !composter.isValid()) return;
          composter.setPermutation(BlockPermutation.resolve('minecraft:composter', {'composter_fill_level': 8}));
          world.playSound('block.composter.ready', composter.center());
        }, 20);
      }
      world.playSound('block.composter.fill_success', composter.center());
    } else {
      world.playSound('block.composter.fill', composter.center());
    }
    if (hopper) return;
    world.playSound('item.bone_meal.use', composter.center(), {pitch: Math.random() * (1.1 - 0.9) + 0.9});
    composter.dimension.spawnParticle('minecraft:crop_growth_emitter', composter.center());
  }
  static decrementStack(player) {
    if (player.matches({gameMode:'creative'})) return;
    const equippable = player.getComponent('equippable');
    const item = equippable.getEquipment('Mainhand');
    const stack = item.amount;
    if (stack > 1) --item.amount;
    equippable.setEquipment('Mainhand', stack > 1 ? item : null);
  }
}

world.beforeEvents.itemUseOn.subscribe(data => {
  if (
    Compostables.getCompostable(data.itemStack?.typeId) > -1 &&
    Compostables.isCompostable(data.block) &&
    data.source?.isSneaking === false
  ) {
    system.run(() => {
      Compostables.composting(data.block, Compostables.registerCompostables[Compostables.getCompostable(data.itemStack.typeId)].chance);
      Compostables.decrementStack(data.source);
    });
  }
});

// Compost-by-dropping system

world.afterEvents.entitySpawn.subscribe(data => {
  if (data.entity?.typeId === 'minecraft:item' && world?.getDynamicProperty('maxis.compostables:compostByDropping') !== false) {
    const item = data.entity;
    const itemStack = item?.getComponent('item').itemStack;
    let vanillaCompostables;
    if (Compostables.getVanillaCompostable(itemStack.typeId) > -1) vanillaCompostables = true;
    else if (Compostables.getCompostable(itemStack.typeId) === -1) return;
    const fallingItem = system.runInterval(() => {
      if (!item.isValid() || world?.getDynamicProperty('maxis.compostables:compostByDropping') === false) return system.clearRun(fallingItem);
      if (!item.isFalling) {
        const block = item.dimension.getBlock(item.location);
        let stackAmount = itemStack.amount;
        if (Compostables.isCompostable(block)) {
          for (; stackAmount > 0; stackAmount--) {
            if (!Compostables.isCompostable(block)) break;
            else if (vanillaCompostables) Compostables.composting(block, Compostables.vanillaCompostables[Compostables.getVanillaCompostable(itemStack.typeId)].chance);
            else Compostables.composting(block, Compostables.registerCompostables[Compostables.getCompostable(itemStack.typeId)].chance);
            if (stackAmount > 1) --itemStack.amount;
          }
          if (stackAmount > 0) item.dimension.spawnItem(itemStack, item.location);
          item.remove();
        } else if (block.permutation.matches('minecraft:composter') || block.below().permutation.matches('minecraft:composter')) return;
        system.clearRun(fallingItem);
      }
    }, 5);
  }
});

system.afterEvents.scriptEventReceive.subscribe(event => {
  if (event.id === 'maxis.compostables:compostByDropping') {
    if (world?.getDynamicProperty('maxis.compostables:compostByDropping') === false) {
      world.setDynamicProperty('maxis.compostables:compostByDropping');
    } else world.setDynamicProperty('maxis.compostables:compostByDropping', false);
  }
});

// Hoppers system

let hopperTicking;

class hopperRegistry {
  constructor(dimension, location) {
    this.dimension = dimension.id;
    this.location = location;
  }
  static add(registry) {
    let idCount = 0;
    world.getDynamicPropertyIds().forEach(id => {
      if (id.startsWith('maxis.compostables:hopper')) idCount++;
    });
    registry = JSON.stringify(registry);
    const id = 'maxis.compostables:hopper_' + idCount;
    world.setDynamicProperty(id, registry);
    if (!idCount) this.hopperTick();
  }
  static remove(registryId) {
    const registries = new Array();
    world.getDynamicPropertyIds().forEach(id => {
      if (id.startsWith('maxis.compostables:hopper') && id !== registryId) {
        registries.push(world.getDynamicProperty(id));
      }
    });
    const compostByDropping = world?.getDynamicProperty('maxis.compostables:compostByDropping');
    world.clearDynamicProperties();
    let id, idCount = 0;
    registries.forEach(reg => {
      id = 'maxis.compostables:hopper_' + idCount;
      world.setDynamicProperty(id, reg);
      idCount++;
    });
    if (compostByDropping === false) world.setDynamicProperty('maxis.compostables:compostByDropping', false);
    if (!idCount) system.clearRun(hopperTicking);
  }
  static getHopperLocation(block) {
    if (block.permutation.matches('minecraft:hopper', {facing_direction: 0}) && block.below().permutation.matches('minecraft:composter')) return block.location;
    else if (block.permutation.matches('minecraft:composter') && block.above().permutation.matches('minecraft:hopper', {facing_direction: 0})) return block.above().location;
  }
  static hopperTicking() {
    world.getDynamicPropertyIds().forEach(id => {
      if (id.startsWith('maxis.compostables:hopper')) {
        const hopperData = JSON.parse(world.getDynamicProperty(id));
        const block = world.getDimension(hopperData.dimension).getBlock(hopperData.location);
        if (!block?.isValid()) return;
        if (!hopperRegistry.getHopperLocation(block)) return system.run(() => hopperRegistry.remove(id));
        if (block.permutation.matches('minecraft:hopper', {toggle_bit: true})) return;
        const hopperContainer = block.getComponent('inventory').container;
        const composter = block.below();
        for (let slot = 0; slot < hopperContainer.size; slot++) {
          if (Compostables.getVanillaCompostable(hopperContainer.getItem(slot)?.typeId) > -1) return;
        }
        for (let slot = 0; slot < hopperContainer.size; slot++) {
          if (!Compostables.isCompostable(composter) || Compostables.getCompostable(hopperContainer.getItem(slot)?.typeId) === -1) continue;
          const item = hopperContainer.getItem(slot);
          const stack = item.amount;
          if (stack > 1) --item.amount;
          hopperContainer.setItem(slot, stack > 1 ? item : null);
          Compostables.composting(composter, Compostables.registerCompostables[Compostables.getCompostable(item.typeId)].chance, true);
          break;
        }
      }
    });
  }
  static hopperTick() {
    hopperTicking = system.runInterval(this.hopperTicking, 8);
  }
}

world.afterEvents.playerPlaceBlock.subscribe(data => {
  if (hopperRegistry.getHopperLocation(data.block)) {
    hopperRegistry.add(new hopperRegistry(data.dimension, hopperRegistry.getHopperLocation(data.block)));
  }
});

world.afterEvents.playerJoin.subscribe(() => {
  if (world.getPlayers().length <= 1 && world.getDynamicPropertyIds().find(id => id.startsWith('maxis.compostables:hopper'))) {
    hopperRegistry.hopperTick();
  }
});


/* © 2019-2023, <@JeanLucasMCPE> - All Rights Reserved. */