package assets

import (
	"fmt"
	"sync"

	"gitea.boner.be/bdnugget/goonscape/logging"
	"gitea.boner.be/bdnugget/goonscape/types"
	rl "github.com/gen2brain/raylib-go/raylib"
)

var (
	assetMutex       sync.RWMutex
	loadedModels     map[string]types.ModelAsset
	audioMutex       sync.Mutex
	audioInitialized bool
)

func init() {
	loadedModels = make(map[string]types.ModelAsset)
}

// Helper function to load animations for a model
func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error) {
	var animSet types.AnimationSet

	// Load idle animations if specified
	if idlePath, ok := animPaths["idle"]; ok {
		idleAnims := rl.LoadModelAnimations(idlePath)
		if len(idleAnims) > 0 {
			animSet.Idle = idleAnims
			rl.TraceLog(rl.LogInfo, "Loaded idle animation: %s (%d frames, %f seconds)",
				idlePath, idleAnims[0].FrameCount, float32(idleAnims[0].FrameCount)/60.0)
		}
	}

	// Load walk animations if specified
	if walkPath, ok := animPaths["walk"]; ok {
		walkAnims := rl.LoadModelAnimations(walkPath)
		if len(walkAnims) > 0 {
			animSet.Walk = walkAnims
			rl.TraceLog(rl.LogInfo, "Loaded walk animation: %s (%d frames, %f seconds)",
				walkPath, walkAnims[0].FrameCount, float32(walkAnims[0].FrameCount)/60.0)
		}
	}

	return animSet, nil
}

func LoadModels() ([]types.ModelAsset, error) {
	logging.Info.Println("Loading models")
	assetMutex.Lock()
	defer assetMutex.Unlock()

	if len(loadedModels) > 0 {
		logging.Info.Println("Returning cached models")
		models := make([]types.ModelAsset, 0, len(loadedModels))
		for _, model := range loadedModels {
			models = append(models, model)
		}
		return models, nil
	}

	// Goonion model and animations
	goonerModel := rl.LoadModel("resources/models/gooner/walk_no_y_transform.glb")
	goonerAnims, err := loadModelAnimations(map[string]string{
		"idle": "resources/models/gooner/idle_no_y_transform.glb",
		"walk": "resources/models/gooner/walk_no_y_transform.glb",
	})
	if err != nil {
		return nil, err
	}

	// Apply transformations
	transform := rl.MatrixIdentity()
	transform = rl.MatrixMultiply(transform, rl.MatrixRotateY(180*rl.Deg2rad))
	transform = rl.MatrixMultiply(transform, rl.MatrixRotateX(-90*rl.Deg2rad))
	transform = rl.MatrixMultiply(transform, rl.MatrixScale(1.0, 1.0, 1.0))
	goonerModel.Transform = transform

	// Coomer model (ready for animations)
	coomerModel := rl.LoadModel("resources/models/coomer/idle_notransy.glb")
	coomerAnims, err := loadModelAnimations(map[string]string{
		"idle": "resources/models/coomer/idle_notransy.glb",
		"walk": "resources/models/coomer/unsteadywalk_notransy.glb",
	})
	if err != nil {
		return nil, err
	}
	coomerModel.Transform = transform

	// Shreke model (ready for animations)
	shrekeModel := rl.LoadModel("resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb")
	shrekeAnims, err := loadModelAnimations(map[string]string{
		"idle": "resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb",
		"walk": "resources/models/shreke/Animation_Excited_Walk_M_withSkin.glb",
	})
	if err != nil {
		return nil, err
	}
	shrekeModel.Transform = transform

	// Store loaded models
	models := []types.ModelAsset{
		{
			Name:       "gooner",
			Model:      goonerModel,
			Animation:  append(goonerAnims.Idle, goonerAnims.Walk...),
			AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
			Animations: goonerAnims,
			YOffset:    0.0,
		},
		{
			Model:      coomerModel,
			Animation:  append(coomerAnims.Idle, coomerAnims.Walk...),
			AnimFrames: int32(len(coomerAnims.Idle) + len(coomerAnims.Walk)),
			Animations: coomerAnims,
			YOffset:    -4.0,
		},
		{
			Model:      shrekeModel,
			Animation:  append(shrekeAnims.Idle, shrekeAnims.Walk...),
			AnimFrames: int32(len(shrekeAnims.Idle) + len(shrekeAnims.Walk)),
			Animations: shrekeAnims,
			YOffset:    0.0,
		},
	}

	for _, model := range models {
		loadedModels[model.Name] = model
	}

	return models, nil
}

func LoadMusic(filename string) (rl.Music, error) {
	logging.Info.Printf("Loading music from %s", filename)
	audioMutex.Lock()
	defer audioMutex.Unlock()

	if !rl.IsAudioDeviceReady() {
		err := fmt.Errorf("audio device not initialized")
		logging.Error.Println(err)
		return rl.Music{}, err
	}

	music := rl.LoadMusicStream(filename)
	if music.CtxType == 0 {
		err := fmt.Errorf("failed to load music stream")
		logging.Error.Println(err)
		return rl.Music{}, err
	}
	logging.Info.Println("Music loaded successfully")
	return music, nil
}

func UnloadModels(models []types.ModelAsset) {
	assetMutex.Lock()
	defer assetMutex.Unlock()

	for _, model := range models {
		if model.Animation != nil {
			for i := int32(0); i < model.AnimFrames; i++ {
				rl.UnloadModelAnimation(model.Animation[i])
			}
		}
		rl.UnloadModel(model.Model)
		rl.UnloadTexture(model.Texture)
		delete(loadedModels, model.Name)
	}
}

func UnloadMusic(music rl.Music) {
	rl.UnloadMusicStream(music)
}
