package com.github.steveice10.mc.protocol.packet.ingame.server.world;

import com.github.steveice10.mc.protocol.data.MagicValues;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.world.block.value.BlockValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.BlockValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.ChestValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.ChestValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.EndGatewayValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.GenericBlockValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.GenericBlockValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.MobSpawnerValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.MobSpawnerValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.NoteBlockValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.NoteBlockValueType;
import com.github.steveice10.mc.protocol.data.game.world.block.value.PistonValue;
import com.github.steveice10.mc.protocol.data.game.world.block.value.PistonValueType;
import com.github.steveice10.packetlib.io.NetInput;
import com.github.steveice10.packetlib.io.NetOutput;
import com.github.steveice10.packetlib.packet.Packet;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.With;

import java.io.IOException;

@Data
@With
@Setter(AccessLevel.NONE)
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
public class ServerBlockValuePacket implements Packet {
    private static final int NOTE_BLOCK = 74;
    private static final int STICKY_PISTON = 93;
    private static final int PISTON = 100;
    private static final int MOB_SPAWNER = 143; // Value does not show in 1.16
    private static final int CHEST = 147;
    private static final int ENDER_CHEST = 270;
    private static final int TRAPPED_CHEST = 329;
    private static final int END_GATEWAY = 499;
    private static final int SHULKER_BOX_LOWER = 509;
    private static final int SHULKER_BOX_HIGHER = 525;

    private @NonNull Position position;
    private @NonNull BlockValueType type;
    private @NonNull BlockValue value;
    private int blockId;

    @Override
    public void read(NetInput in) throws IOException {
        this.position = Position.read(in);
        int type = in.readUnsignedByte();
        int value = in.readUnsignedByte();
        this.blockId = in.readVarInt() & 0xFFF;

        if(this.blockId == NOTE_BLOCK) {
            this.type = MagicValues.key(NoteBlockValueType.class, type);
            this.value = new NoteBlockValue(value);
        } else if(this.blockId == STICKY_PISTON || this.blockId == PISTON) {
            this.type = MagicValues.key(PistonValueType.class, type);
            this.value = MagicValues.key(PistonValue.class, value);
        } else if(this.blockId == MOB_SPAWNER) {
            this.type = MagicValues.key(MobSpawnerValueType.class, type);
            this.value = new MobSpawnerValue();
        } else if(this.blockId == CHEST || this.blockId == ENDER_CHEST || this.blockId == TRAPPED_CHEST
                || (this.blockId >= SHULKER_BOX_LOWER && this.blockId <= SHULKER_BOX_HIGHER)) {
            this.type = MagicValues.key(ChestValueType.class, type);
            this.value = new ChestValue(value);
        } else if(this.blockId == END_GATEWAY) {
            this.type = MagicValues.key(EndGatewayValueType.class, type);
            this.value = new EndGatewayValue();
        } else {
            this.type = MagicValues.key(GenericBlockValueType.class, type);
            this.value = new GenericBlockValue(value);
        }
    }

    @Override
    public void write(NetOutput out) throws IOException {
        int val = 0;
        if(this.type instanceof NoteBlockValueType) {
            val = ((NoteBlockValue) this.value).getPitch();
        } else if(this.type instanceof PistonValueType) {
            val = MagicValues.value(Integer.class, this.value);
        } else if(this.type instanceof ChestValueType) {
            val = ((ChestValue) this.value).getViewers();
        } else if(this.type instanceof GenericBlockValueType) {
            val = ((GenericBlockValue) this.value).getValue();
        }

        Position.write(out, this.position);
        out.writeByte(MagicValues.value(Integer.class, this.type));
        out.writeByte(val);
        out.writeVarInt(this.blockId & 4095);
    }

    @Override
    public boolean isPriority() {
        return false;
    }
}

@@ -1983,10 +1983,10 @@
 K = 
-74
+80
 ;%0A  
@@ -2029,17 +2029,17 @@
 STON = 9
-3
+9
 ;%0A    pr
@@ -2072,17 +2072,17 @@
 TON = 10
-0
+6
 ;%0A    pr
@@ -2123,10 +2123,10 @@
  = 1
-43
+51
 ; //
@@ -2196,10 +2196,10 @@
  = 1
-47
+53
 ;%0A  
@@ -2244,10 +2244,10 @@
  = 2
-70
+83
 ;%0A  
@@ -2294,10 +2294,10 @@
  = 3
+4
 2
-9
 ;%0A  
@@ -2341,11 +2341,11 @@
 Y = 
-499
+513
 ;%0A  
@@ -2392,18 +2392,18 @@
 OWER = 5
-09
+23
 ;%0A    pr
@@ -2451,10 +2451,10 @@
  = 5
-25
+39
 ;%0A%0A 

Update block values