// Path: app/src/main/java/com/protolambda/blocktopograph/chunk/Chunk.java
// public class Chunk {
// 
//     public final WorldData worldData;
// 
//     public final int x, z;
//     public final Dimension dimension;
// 
//     private Version version;
// 
//     private AtomicReferenceArray<TerrainChunkData>
//             terrain = new AtomicReferenceArray<>(256);
// 
//     private volatile NBTChunkData entity, blockEntity;
// 
//     public Chunk(WorldData worldData, int x, int z, Dimension dimension) {
//         this.worldData = worldData;
//         this.x = x;
//         this.z = z;
//         this.dimension = dimension;
//     }
// 
//     public TerrainChunkData getTerrain(byte subChunk) throws Version.VersionException {
//         TerrainChunkData data = terrain.get(subChunk & 0xff);
//         if(data == null){
//             data = this.getVersion().createTerrainChunkData(this, subChunk);
//             terrain.set(subChunk & 0xff, data);
//         }
//         return data;
//     }
// 
//     public NBTChunkData getEntity() throws Version.VersionException {
//         if(entity == null) entity = this.getVersion().createEntityChunkData(this);
//         return entity;
//     }
// 
// 
//     public NBTChunkData getBlockEntity() throws Version.VersionException {
//         if(blockEntity == null) blockEntity = this.getVersion().createBlockEntityChunkData(this);
//         return blockEntity;
//     }
// 
//     public Version getVersion(){
//         if(this.version == null) try {
//             byte[] data = this.worldData.getChunkData(x, z, ChunkTag.VERSION, dimension, (byte) 0, false);
//             this.version = Version.getVersion(data);
//         } catch (WorldData.WorldDBLoadException | WorldData.WorldDBException e) {
//             e.printStackTrace();
//             this.version = Version.ERROR;
//         }
// 
//         return this.version;
//     }
// 
// 
//     //TODO: should we use the heightmap???
//     public int getHighestBlockYAt(int x, int z) throws Version.VersionException {
//         Version cVersion = getVersion();
//         TerrainChunkData data;
//         for(int subChunk = cVersion.subChunks - 1; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
// 
//             for (int y = cVersion.subChunkHeight; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) != 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
//         }
//         return -1;
//     }
// 
//     public int getHighestBlockYUnderAt(int x, int z, int y) throws Version.VersionException {
//         Version cVersion = getVersion();
//         int offset = y % cVersion.subChunkHeight;
//         int subChunk = y / cVersion.subChunkHeight;
//         TerrainChunkData data;
// 
//         for(; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
// 
//             for (y = offset; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) != 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
// 
//             //start at the top of the next chunk! (current offset might differ)
//             offset = cVersion.subChunkHeight - 1;
//         }
//         return -1;
//     }
// 
//     public int getCaveYUnderAt(int x, int z, int y) throws Version.VersionException {
//         Version cVersion = getVersion();
//         int offset = y % cVersion.subChunkHeight;
//         int subChunk = y / cVersion.subChunkHeight;
//         TerrainChunkData data;
// 
//         for(; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
//             for (y = offset; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) == 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
// 
//             //start at the top of the next chunk! (current offset might differ)
//             offset = cVersion.subChunkHeight - 1;
//         }
//         return -1;
//     }
// 
// 
// }
// 
// Path: app/src/main/java/com/protolambda/blocktopograph/chunk/ChunkData.java
// public abstract class ChunkData {
// 
//     public final Chunk chunk;
// 
// 
// 
//     public ChunkData(Chunk chunk){
//         this.chunk = chunk;
//     }
// 
//     public abstract void createEmpty();
// 
//     public abstract void write() throws IOException, WorldData.WorldDBException;
// 
// }


import com.protolambda.blocktopograph.chunk.Chunk;
import com.protolambda.blocktopograph.chunk.ChunkData;
package com.protolambda.blocktopograph.chunk.terrain;



public abstract class TerrainChunkData extends ChunkData {

    public final byte subChunk;

    public TerrainChunkData(Chunk chunk, byte subChunk) {
        super(chunk);
        this.subChunk = subChunk;
    }

    public abstract boolean loadTerrain();

    public abstract boolean load2DData();

    public abstract byte getBlockTypeId(int x, int y, int z);

    public abstract byte getBlockData(int x, int y, int z);

    public abstract byte getSkyLightValue(int x, int y, int z);

    public abstract byte getBlockLightValue(int x, int y, int z);

    public abstract boolean supportsBlockLightValues();

// Path: app/src/main/java/com/protolambda/blocktopograph/chunk/Chunk.java
// public class Chunk {
// 
//     public final WorldData worldData;
// 
//     public final int x, z;
//     public final Dimension dimension;
// 
//     private Version version;
// 
//     private AtomicReferenceArray<TerrainChunkData>
//             terrain = new AtomicReferenceArray<>(256);
// 
//     private volatile NBTChunkData entity, blockEntity;
// 
//     public Chunk(WorldData worldData, int x, int z, Dimension dimension) {
//         this.worldData = worldData;
//         this.x = x;
//         this.z = z;
//         this.dimension = dimension;
//     }
// 
//     public TerrainChunkData getTerrain(byte subChunk) throws Version.VersionException {
//         TerrainChunkData data = terrain.get(subChunk & 0xff);
//         if(data == null){
//             data = this.getVersion().createTerrainChunkData(this, subChunk);
//             terrain.set(subChunk & 0xff, data);
//         }
//         return data;
//     }
// 
//     public NBTChunkData getEntity() throws Version.VersionException {
//         if(entity == null) entity = this.getVersion().createEntityChunkData(this);
//         return entity;
//     }
// 
// 
//     public NBTChunkData getBlockEntity() throws Version.VersionException {
//         if(blockEntity == null) blockEntity = this.getVersion().createBlockEntityChunkData(this);
//         return blockEntity;
//     }
// 
//     public Version getVersion(){
//         if(this.version == null) try {
//             byte[] data = this.worldData.getChunkData(x, z, ChunkTag.VERSION, dimension, (byte) 0, false);
//             this.version = Version.getVersion(data);
//         } catch (WorldData.WorldDBLoadException | WorldData.WorldDBException e) {
//             e.printStackTrace();
//             this.version = Version.ERROR;
//         }
// 
//         return this.version;
//     }
// 
// 
//     //TODO: should we use the heightmap???
//     public int getHighestBlockYAt(int x, int z) throws Version.VersionException {
//         Version cVersion = getVersion();
//         TerrainChunkData data;
//         for(int subChunk = cVersion.subChunks - 1; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
// 
//             for (int y = cVersion.subChunkHeight; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) != 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
//         }
//         return -1;
//     }
// 
//     public int getHighestBlockYUnderAt(int x, int z, int y) throws Version.VersionException {
//         Version cVersion = getVersion();
//         int offset = y % cVersion.subChunkHeight;
//         int subChunk = y / cVersion.subChunkHeight;
//         TerrainChunkData data;
// 
//         for(; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
// 
//             for (y = offset; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) != 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
// 
//             //start at the top of the next chunk! (current offset might differ)
//             offset = cVersion.subChunkHeight - 1;
//         }
//         return -1;
//     }
// 
//     public int getCaveYUnderAt(int x, int z, int y) throws Version.VersionException {
//         Version cVersion = getVersion();
//         int offset = y % cVersion.subChunkHeight;
//         int subChunk = y / cVersion.subChunkHeight;
//         TerrainChunkData data;
// 
//         for(; subChunk >= 0; subChunk--) {
//             data = this.getTerrain((byte) subChunk);
//             if (data == null || !data.loadTerrain()) continue;
//             for (y = offset; y >= 0; y--) {
//                 if (data.getBlockTypeId(x & 15, y, z & 15) == 0)
//                     return (subChunk * cVersion.subChunkHeight) + y;
//             }
// 
//             //start at the top of the next chunk! (current offset might differ)
//             offset = cVersion.subChunkHeight - 1;
//         }
//         return -1;
//     }
// 
// 
// }
// 
// Path: app/src/main/java/com/protolambda/blocktopograph/chunk/ChunkData.java
// public abstract class ChunkData {
// 
//     public final Chunk chunk;
// 
// 
// 
//     public ChunkData(Chunk chunk){
//         this.chunk = chunk;
//     }
// 
//     public abstract void createEmpty();
// 
//     public abstract void write() throws IOException, WorldData.WorldDBException;
// 
// }


// Path: app/src/main/java/com/protolambda/blocktopograph/chunk/terrain/TerrainChunkData.java
import com.protolambda.blocktopograph.chunk.Chunk;
import com.protolambda.blocktopograph.chunk.ChunkData;

package com.protolambda.blocktopograph.chunk.terrain;



public abstract class TerrainChunkData extends ChunkData {

    public final byte subChunk;

    public TerrainChunkData(Chunk chunk, byte subChunk) {
        super(chunk);
        this.subChunk = subChunk;
    }

    public abstract boolean loadTerrain();

    public abstract boolean load2DData();

    public abstract byte getBlockTypeId(int x, int y, int z);

    public abstract byte getBlockData(int x, int y, int z);

    public abstract byte getSkyLightValue(int x, int y, int z);

    public abstract byte getBlockLightValue(int x, int y, int z);

    public abstract boolean supportsBlockLightValues();

    public abstract void setBlockTypeId(int x, int y, int z, int type);