package worldgen

import (
	"sync"

	packworld "edgaru089.ink/go/gl01/internal/world"
	"edgaru089.ink/go/gl01/internal/world/blocks"
	"github.com/aquilax/go-perlin"
)

const (
	Alpha = 5 // Weight forming the noise sum
	Beta  = 2 // harmonic scaling/spacing
	N     = 3 // iterations

	AltitudeDensity = 20 // Density of the noise in altitude

	Sealevel = 63 // Space with Y=63 and lower should be the sea
	Highest  = 74 // Highest part of the terrain
	Lowest   = 54 // Lowest part of the terrain
)

var perlins map[int64]*perlin.Perlin
var perlinsLock sync.RWMutex

func init() {
	perlins = make(map[int64]*perlin.Perlin)
}

func fractal(p *perlin.Perlin, x, y float64, levels int) float64 {
	ans := 0.0
	amp := 1.0
	for i := 0; i < levels; i++ {
		ans += amp * p.Noise2D(x, y)
		amp /= 3
		x *= 2
		y *= 2
	}
	return ans
}

// Chunk generates the chunk (must with chunkID set!!) with seed.
func Chunk(chunk *packworld.Chunk, world *packworld.World, seed int64) {

	perlinsLock.RLock()
	p, ok := perlins[seed]
	if !ok {
		perlinsLock.RUnlock()
		perlinsLock.Lock()
		p = perlin.NewPerlin(Alpha, Beta, N, seed)
		perlins[seed] = p
		perlinsLock.Unlock()
	} else {
		perlinsLock.RUnlock()
	}

	//log.Printf("noise=%.5f", p.Noise2D(0.1, 0.1))

	offX := packworld.ChunkSizeX * chunk.X
	offZ := packworld.ChunkSizeZ * chunk.Z

	for x := 0; x < packworld.ChunkSizeX; x++ {
		for z := 0; z < packworld.ChunkSizeZ; z++ {

			height := Lowest + int(float64(Highest-Lowest)*(fractal(
				p,
				float64(offX+x)/AltitudeDensity,
				float64(offZ+z)/AltitudeDensity, 6)/2+0.5))
			//log.Printf("height = %d (noise=%.5f)", height, p.Noise2D(float64(offX+x)/AltitudeDensity, float64(offZ+z)/AltitudeDensity)/2+0.5)

			// water
			for y := Sealevel; y > height; y-- {
				chunk.Id[x][y][z] = blocks.Water
			}

			// covering dirt
			if height >= Sealevel {
				chunk.Id[x][height][z] = blocks.Grass
			} else {
				chunk.Id[x][height][z] = blocks.Dirt
			}
			//chunk.Id[x][height][z] = blocks.DebugDir
			chunk.Id[x][height-1][z] = blocks.Dirt
			chunk.Id[x][height-2][z] = blocks.Dirt
			chunk.Id[x][height-3][z] = blocks.Dirt
			height -= 4

			// stone in the middle
			for y := height; y >= 1; y-- {
				chunk.Id[x][y][z] = blocks.Stone
			}

			// bedrock at the bottom
			chunk.Id[x][0][z] = blocks.Bedrock

			// plant trees
			if height >= 50 && height <= 70 {
				if p.Noise2D(float64(offX+x), float64(offZ+z)) > 0.9 {
				}
			}
		}
	}

	chunk.InvalidateRender()
}
