package world

import (
	"encoding/gob"
	"io"
	"log"

	"edgaru089.ink/go/gl01/internal/util/itype"
)

const (
	ChunkSizeX = 16
	ChunkSizeY = 128
	ChunkSizeZ = 16
)

type Chunk struct {
	X, Z int // Chunk coordinate in global coordinate (y is always zero)

	Id  [ChunkSizeX][ChunkSizeY][ChunkSizeZ]uint16
	Aux [ChunkSizeX][ChunkSizeY][ChunkSizeZ]int16

	// render data kept unexported (therefore excluded from gob encoding)
	renderChanged bool
	vao, vbo      uint32
	vbolen        int
	water         struct {
		vao, vbo uint32
		vbolen   int
	}
	vertUpdate chan [2][]Vertex

	world *World
}

func (c *Chunk) SetChunkID(x, z int) {
	c.X = x
	c.Z = z
	c.renderChanged = true
}

func (c *Chunk) SetBlock(x, y, z int, id, aux int) {
	c.Id[x][y][z] = uint16(id)
	c.Aux[x][y][z] = int16(aux)
	c.renderChanged = true

	switch x {
	case 0:
		if cb, ok := c.world.Chunks[itype.Vec2i{c.X - 1, c.Z}]; ok {
			cb.InvalidateRender()
		}
	case ChunkSizeX - 1:
		if cb, ok := c.world.Chunks[itype.Vec2i{c.X + 1, c.Z}]; ok {
			cb.InvalidateRender()
		}
	}
	switch z {
	case 0:
		if cb, ok := c.world.Chunks[itype.Vec2i{c.X, c.Z - 1}]; ok {
			cb.InvalidateRender()
		}
	case ChunkSizeZ - 1:
		if cb, ok := c.world.Chunks[itype.Vec2i{c.X, c.Z + 1}]; ok {
			cb.InvalidateRender()
		}
	}
}

// InvalidateRender should be called if a block inside the chunk has changed
// and the chunk needs to re-rendered.
func (c *Chunk) InvalidateRender() {
	c.renderChanged = true
}

// LoadFromGob loads the chunk from a gob-encoded file.
func (c *Chunk) LoadFromGob(file io.Reader) {
	d := gob.NewDecoder(file)
	err := d.Decode(c)
	if err != nil {
		log.Print("LoadFromGob Error: ", err)
	}
}

// LoadFromGobIndexed loads the chunk from a gob-encoded file, with the
// chunk Index overwritten.
func (c *Chunk) LoadFromGobIndexed(file io.Reader, x, z int) {
	d := gob.NewDecoder(file)
	err := d.Decode(c)
	if err != nil {
		log.Printf("LoadFromGobIndexed(x=%d, z=%d) Error: %s", x, z, err)
	}
	c.X = x
	c.Z = z
}

// WriteToGob writes the chunk to a gob-encoded file.
func (c *Chunk) WriteToGob(file io.Writer) {
	e := gob.NewEncoder(file)
	err := e.Encode(c)
	if err != nil {
		log.Printf("WriteToGob(x=%d, z=%d) Error: %s", c.X, c.Z, err)
	}
}
