package raylib

import (
	"bytes"
	"compress/flate"
	"encoding/binary"
	"fmt"
	"os"
	"unsafe"
)

// rRES file header (8 byte)
type RRESFileHeader struct {
	// File identifier: rRES (4 byte)
	Id [4]int8
	// File version and subversion (2 byte)
	Version uint16
	// Number of resources in this file (2 byte)
	Count uint16
}

// rRES info header, every resource includes this header (16 byte + 16 byte)
type RRESInfoHeader struct {
	// Resource unique identifier (4 byte)
	Id uint32
	// Resource data type (1 byte)
	DataType uint8
	// Resource data compression type (1 byte)
	CompType uint8
	// Resource data encryption type (1 byte)
	CryptoType uint8
	// Resource data parts count, used for splitted data (1 byte)
	PartsCount uint8
	// Resource data size (compressed or not, only DATA) (4 byte)
	DataSize uint32
	// Resource data size (uncompressed, only DATA) (4 byte)
	UncompSize uint32
	// Resouce parameter 1 (4 byte)
	Param1 uint32
	// Resouce parameter 2 (4 byte)
	Param2 uint32
	// Resouce parameter 3 (4 byte)
	Param3 uint32
	// Resouce parameter 4 (4 byte)
	Param4 uint32
}

// rRES data types
const (
	RRESTypeRaw = iota
	RRESTypeImage
	RRESTypeWave
	RRESTypeVertex
	RRESTypeText
	RRESTypeFontImage
	RRESTypeFontData
	RRESTypeDirectory
)

// Compression types
const (
	// No data compression
	RRESCompNone = iota
	// DEFLATE compression
	RRESCompDeflate
	// LZ4 compression
	RRESCompLz4
	// LZMA compression
	RRESCompLzma
	// BROTLI compression
	RRESCompBrotli
)

const (
	// 8 bit per pixel (no alpha)
	RRESImUncompGrayscale = iota + 1
	// 16 bpp (2 channels)
	RRESImUncompGrayAlpha
	// 16 bpp
	RRESImUncompR5g6b5
	// 24 bpp
	RRESImUncompR8g8b8
	// 16 bpp (1 bit alpha)
	RRESImUncompR5g5b5a1
	// 16 bpp (4 bit alpha)
	RRESImUncompR4g4b4a4
	// 32 bpp
	RRESImUncompR8g8b8a8
	// 4 bpp (no alpha)
	RRESImCompDxt1Rgb
	// 4 bpp (1 bit alpha)
	RRESImCompDxt1Rgba
	// 8 bpp
	RRESImCompDxt3Rgba
	// 8 bpp
	RRESImCompDxt5Rgba
	// 4 bpp
	RRESImCompEtc1Rgb
	// 4 bpp
	RRESImCompEtc2Rgb
	// 8 bpp
	RRESImCompEtc2EacRgba
	// 4 bpp
	RRESImCompPvrtRgb
	// 4 bpp
	RRESImCompPvrtRgba
	// 8 bpp
	RRESImCompAstc4x4Rgba
	// 2 bpp
	RRESImCompAstc8x8Rgba
)

const (
	RRESVertPosition = iota
	RRESVertTexcoord1
	RRESVertTexcoord2
	RRESVertTexcoord3
	RRESVertTexcoord4
	RRESVertNormal
	RRESVertTangent
	RRESVertColor
	RRESVertIndex
)

const (
	RRESVertByte = iota
	RRESVertShort
	RRESVertInt
	RRESVertHfloat
	RRESVertFloat
)

// Load resource from file (only one)
// NOTE: Returns uncompressed data with parameters, only first resource found
func LoadResource(fileName string) []byte {
	return LoadResourceById(fileName, 0)
}

// Load resource from file by id
// NOTE: Returns uncompressed data with parameters, search resource by id
func LoadResourceById(fileName string, rresId int) (data []byte) {
	file, err := os.Open(fileName)
	if err != nil {
		TraceLog(LogWarning, "[%s] rRES raylib resource file could not be opened", fileName)
		return
	}
	defer file.Close()

	fileHeader := RRESFileHeader{}
	infoHeader := RRESInfoHeader{}

	// Read rres file header
	err = binary.Read(file, binary.LittleEndian, &fileHeader)
	if err != nil {
		TraceLog(LogWarning, err.Error())
		return
	}

	//fmt.Printf("%+v\n", fileHeader)

	// Verify "rRES" identifier
	id := fmt.Sprintf("%c", fileHeader.Id)
	if id != "[r R E S]" {
		TraceLog(LogWarning, "[%s] This is not a valid raylib resource file", fileName)
		return
	}

	file.Seek(int64(unsafe.Sizeof(fileHeader)), os.SEEK_CUR)

	for i := 0; i < int(fileHeader.Count); i++ {
		// Read resource info and parameters
		err = binary.Read(file, binary.LittleEndian, &infoHeader)
		if err != nil {
			TraceLog(LogWarning, err.Error())
			return
		}

		//fmt.Printf("%+v\n", infoHeader)

		file.Seek(int64(unsafe.Sizeof(infoHeader)), os.SEEK_CUR)

		if int(infoHeader.Id) == rresId {
			// Read resource data block
			data = make([]byte, infoHeader.DataSize)
			file.Read(data)

			if infoHeader.CompType == RRESCompDeflate {
				// Uncompress data
				b := bytes.NewReader(data)
				r := flate.NewReader(b)

				data = make([]byte, infoHeader.UncompSize)
				r.Read(data)
			}

			if len(data) > 0 {
				TraceLog(LogInfo, "[%s][ID %d] Resource data loaded successfully", fileName, infoHeader.Id)
			}
		} else {
			// Skip required data to read next resource infoHeader
			file.Seek(int64(infoHeader.DataSize), os.SEEK_CUR)
		}
	}

	if len(data) == 0 {
		TraceLog(LogInfo, "[%s][ID %d] Requested resource could not be found", fileName, rresId)
	}

	return
}
