package net.engin33r.luaspigot.lua.type;

import net.engin33r.luaspigot.lua.WrapperType;
import net.engin33r.luaspigot.lua.LinkedField;
import net.engin33r.luaspigot.lua.Method;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.luaj.vm2.*;

/**
 * Wrapper type representing a single block within the world.
 */
public class LuaBlock extends WrapperType<Block> {
    private static LuaValue typeMetatable = LuaValue.tableOf();

    private BlockState state;
    private boolean dynamic = false;
    private Material matCache;

    public LuaBlock(Block block) {
        super(block);

        this.dynamic = true;
        this.matCache = block.getType();

        registerField("dynamic", LuaBoolean.valueOf(true));

        registerLinkedField("material", new MaterialField(this));
        registerField("x", LuaNumber.valueOf(block.getX()));
        registerField("y", LuaNumber.valueOf(block.getY()));
        registerField("z", LuaNumber.valueOf(block.getZ()));

        registerField("biome", LuaString.valueOf(block.getBiome().toString()));
        registerField("empty", LuaBoolean.valueOf(block.isEmpty()));
        registerField("humidity", LuaNumber.valueOf(block.getHumidity()));
        registerField("liquid", LuaBoolean.valueOf(block.isLiquid()));
        registerField("location", new LuaLocation(block.getLocation()));
        registerField("temperature", LuaNumber.valueOf(block
                .getTemperature()));
        registerField("world", new LuaWorld(block.getWorld()));
    }

    public LuaBlock(BlockState state) {
        super(null);

        this.state = state;
        this.matCache = state.getType();

        registerField("block", new LuaBlock(state.getBlock()));
        registerLinkedField("material", new MaterialField(this));
        registerField("x", LuaNumber.valueOf(state.getX()));
        registerField("y", LuaNumber.valueOf(state.getY()));
        registerField("z", LuaNumber.valueOf(state.getZ()));
        registerField("light", LuaNumber.valueOf(state.getLightLevel()));
        registerField("location", new LuaLocation(state.getLocation()));
        registerField("world", new LuaWorld(state.getWorld()));

        registerMethod("update", new Method<LuaBlock>(this) {
            @Override
            public Varargs call(Varargs args) {
                state.update(args.optboolean(1, false),
                        args.optboolean(2, true));
                return NIL;
            }
        });
    }

    public LuaBlock(Location loc) {
        this(loc.getBlock());
    }

    @Override
    protected LuaValue getMetatable() {
        return typeMetatable;
    }

    @Override
    public String getName() {
        return "block";
    }

    private class MaterialField extends LinkedField<LuaBlock> {
        public MaterialField(LuaBlock self) { super(self); }

        @Override
        public void update(LuaValue val) {
            matCache = Material.getMaterial(val.checkjstring());
            if (dynamic)
                getHandle().setType(matCache);
            else
                state.setType(matCache);
        }

        @Override
        public LuaValue query() {
            if (dynamic) {
                if (!matCache.equals(getHandle().getType())) {
                    matCache = getHandle().getType();
                }
            } else {
                if (!matCache.equals(state.getType())) {
                    matCache = state.getType();
                }
            }

            return LuaString.valueOf(matCache.toString());
        }
    }
}

@@ -70,22 +70,19 @@
 ot.lua.W
-rapper
+eak
 Type;%0Aim
@@ -291,16 +291,57 @@
 kState;%0A
+import org.bukkit.material.MaterialData;%0A
 import o
@@ -461,25 +461,15 @@
 ds W
-rapperType%3CBlock%3E
+eakType
  %7B%0A 
@@ -528,24 +528,49 @@
 tableOf();%0A%0A
+    private Block block;%0A
     private 
@@ -703,23 +703,28 @@
     
-super(
+this.block = 
 block
-)
 ;%0A
-%0A
     
@@ -901,36 +901,86 @@
 w MaterialField(
-this
+));%0A        registerLinkedField(%22data%22, new DataField(
 ));%0A        regi
@@ -1613,24 +1613,98 @@
 rature()));%0A
+        registerField(%22light%22, LuaNumber.valueOf(block.getLightLevel()));%0A
         regi
@@ -1806,30 +1806,8 @@
 ) %7B%0A
-        super(null);%0A%0A
     
@@ -1932,24 +1932,25 @@
 tBlock()));%0A
+%0A
         regi
@@ -1995,20 +1995,70 @@
 alField(
-this
+));%0A        registerLinkedField(%22data%22, new DataField(
 ));%0A    
@@ -2232,24 +2232,25 @@
 e.getZ()));%0A
+%0A
         regi
@@ -2814,32 +2814,111 @@
 lock());%0A    %7D%0A%0A
+    public Object getHandle() %7B%0A        return dynamic ? block : state;%0A    %7D%0A%0A
     @Override%0A  
@@ -3135,70 +3135,8 @@
 %3E %7B%0A
-        public MaterialField(LuaBlock self) %7B super(self); %7D%0A%0A
     
@@ -3298,27 +3298,21 @@
         
-getHandle()
+block
 .setType
@@ -3508,27 +3508,21 @@
 .equals(
-getHandle()
+block
 .getType
@@ -3559,27 +3559,21 @@
 Cache = 
-getHandle()
+block
 .getType
@@ -3745,32 +3745,32 @@
 %0A            %7D%0A%0A
-
             retu
@@ -3827,11 +3827,648 @@
  %7D%0A    %7D
+%0A%0A    @SuppressWarnings(%22deprecation%22)%0A    private class DataField extends LinkedField%3CLuaBlock%3E %7B%0A        @Override%0A        public void update(LuaValue val) %7B%0A            if (dynamic)%0A                block.setData((byte) val.checkint());%0A            else%0A                state.setData(new MaterialData(block.getType(),%0A                        (byte) val.checkint()));%0A        %7D%0A%0A        @Override%0A        public LuaValue query() %7B%0A            if (dynamic) %7B%0A                return LuaNumber.valueOf(block.getData());%0A            %7D else %7B%0A                return LuaNumber.valueOf(state.getData().getData());%0A            %7D%0A        %7D%0A    %7D
 %0A%7D%0A

Improve Bukkit interaction for Block