1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::ops::Range;

use crate::{
    dimension, pre13, pre18, Block, Chunk as DimensionChunk, CurrentJavaChunk, HeightMode,
    JavaChunk,
};
// Chunk as DimensionChunk need because complete::Chunk and dimension::Chunk are both called Chunk maybe rename one
use crate::biome::Biome;
use crate::complete::section_tower::SectionTower;

pub struct Chunk {
    pub status: String,

    pub sections: SectionTower,

    //todo more | different Heightmaps
    pub heightmap: [i16; 256],
}

impl Chunk {
    pub fn from_bytes(data: &[u8]) -> fastnbt::error::Result<Self> {
        match JavaChunk::from_bytes(data)? {
            JavaChunk::Post18(chunk) => Ok(chunk.into()),
            JavaChunk::Pre18(chunk) => Ok(chunk.into()),
            JavaChunk::Pre13(chunk) => Ok(chunk.into()),
        }
    }

    pub fn iter_blocks(&self) -> impl Iterator<Item = &Block> {
        self.sections.iter_blocks()
    }

    pub fn is_valid_block_cord(&self, x: usize, y: isize, z: usize) -> bool {
        self.y_range().contains(&y) && (0..16).contains(&x) && (0..16).contains(&z)
    }
}

impl dimension::Chunk for Chunk {
    fn status(&self) -> String {
        self.status.clone()
    }

    fn surface_height(&self, x: usize, z: usize, mode: HeightMode) -> isize {
        match mode {
            HeightMode::Trust => self.heightmap[z * 16 + x] as isize,
            HeightMode::Calculate => {
                todo!()
            }
        }
    }

    fn biome(&self, x: usize, y: isize, z: usize) -> Option<Biome> {
        if !self.is_valid_block_cord(x, y, z) {
            return None;
        }

        self.sections.biome(x, y, z)
    }

    fn block(&self, x: usize, y: isize, z: usize) -> Option<&Block> {
        if !self.is_valid_block_cord(x, y, z) {
            return None;
        }

        self.sections.block(x, y, z)
    }

    fn y_range(&self) -> Range<isize> {
        self.sections.y_range()
    }
}

impl From<CurrentJavaChunk> for Chunk {
    fn from(current_java_chunk: CurrentJavaChunk) -> Self {
        //probably find better way. maybe always recalculate if need and then cache
        current_java_chunk.recalculate_heightmap(HeightMode::Trust);
        let heightmap = current_java_chunk.lazy_heightmap.read().unwrap().unwrap();

        Chunk {
            status: current_java_chunk.status.clone(),
            sections: current_java_chunk.sections.unwrap().into(),
            heightmap,
        }
    }
}

impl From<pre18::JavaChunk> for Chunk {
    fn from(java_chunk: pre18::JavaChunk) -> Self {
        //probably find better way. maybe always recalculate if need and then cache
        java_chunk.recalculate_heightmap(HeightMode::Trust);
        let heightmap = java_chunk.level.lazy_heightmap.read().unwrap().unwrap();

        let biomes = create_biome_vec(&java_chunk);

        Chunk {
            status: java_chunk.status(),
            sections: (java_chunk.level.sections.unwrap(), biomes).into(),
            heightmap,
        }
    }
}

impl From<pre13::JavaChunk> for Chunk {
    fn from(java_chunk: pre13::JavaChunk) -> Self {
        //probably find better way. maybe always recalculate if need and then cache
        java_chunk.recalculate_heightmap(HeightMode::Trust);

        let heightmap = java_chunk.level.lazy_heightmap.read().unwrap().unwrap();

        let biomes = create_biome_vec(&java_chunk);
        let blocks = create_block_vec(&java_chunk);

        Chunk {
            status: java_chunk.status(),
            sections: (java_chunk.level.sections.unwrap(), blocks, biomes).into(),
            heightmap,
        }
    }
}

fn create_biome_vec(java_chunk: &dyn dimension::Chunk) -> Vec<Biome> {
    let mut biomes = vec![];

    for y in java_chunk.y_range().step_by(4) {
        for z in (0..16).step_by(4) {
            for x in (0..16).step_by(4) {
                biomes.push(java_chunk.biome(x, y, z).unwrap())
            }
        }
    }

    biomes
}

fn create_block_vec(java_chunk: &dyn dimension::Chunk) -> Vec<Block> {
    let mut blocks = vec![];

    for y in java_chunk.y_range() {
        for z in 0..16 {
            for x in 0..16 {
                blocks.push(java_chunk.block(x, y, z).unwrap().clone())
            }
        }
    }

    blocks
}