package camera

import (
	"github.com/bloeys/gglm/gglm"
)

type Type int32

const (
	Type_Unknown Type = iota
	Type_Perspective
	Type_Orthographic
)

type Camera struct {
	Type Type

	Pos     gglm.Vec3
	Forward gglm.Vec3
	WorldUp gglm.Vec3

	NearClip float32
	FarClip  float32

	// Perspective data
	Fov         float32
	AspectRatio float32

	// Ortho data
	Left, Right, Top, Bottom float32

	// Matrices
	ViewMat gglm.Mat4
	ProjMat gglm.Mat4
}

// Update recalculates view matrix and projection matrix.
// Should be called whenever a camera parameter changes
func (c *Camera) Update() {

	c.ViewMat = gglm.LookAtRH(&c.Pos, c.Pos.Clone().Add(&c.Forward), &c.WorldUp).Mat4

	if c.Type == Type_Perspective {
		c.ProjMat = gglm.Perspective(c.Fov, c.AspectRatio, c.NearClip, c.FarClip)
	} else {
		c.ProjMat = gglm.Ortho(c.Left, c.Right, c.Top, c.Bottom, c.NearClip, c.FarClip).Mat4
	}
}

// UpdateRotation calculates a new forward vector and then calls camera.Update()
func (c *Camera) UpdateRotation(pitch, yaw float32) {

	dir := gglm.NewVec3(
		gglm.Cos32(yaw)*gglm.Cos32(pitch),
		gglm.Sin32(pitch),
		gglm.Sin32(yaw)*gglm.Cos32(pitch),
	)
	c.Forward = *dir.Normalize()
	c.Update()
}

func NewPerspective(pos, forward, worldUp *gglm.Vec3, nearClip, farClip, fovRadians, aspectRatio float32) Camera {

	cam := Camera{
		Type:    Type_Perspective,
		Pos:     *pos,
		Forward: *forward,
		WorldUp: *worldUp,

		NearClip: nearClip,
		FarClip:  farClip,

		Fov:         fovRadians,
		AspectRatio: aspectRatio,
	}
	cam.Update()

	return cam
}

func NewOrthographic(pos, forward, worldUp *gglm.Vec3, nearClip, farClip, left, right, top, bottom float32) Camera {

	cam := Camera{
		Type:    Type_Orthographic,
		Pos:     *pos,
		Forward: *forward,
		WorldUp: *worldUp,

		NearClip: nearClip,
		FarClip:  farClip,

		Left:   left,
		Right:  right,
		Top:    top,
		Bottom: bottom,
	}
	cam.Update()

	return cam
}
