package render

import (
	"edgaru089.ink/go/gl01/internal/util/itype"
	"github.com/go-gl/mathgl/mgl32"
)

// View represents a viewport in the 3-d world.
// It is essentialy a projection and a view matrix.
type View struct {
	EyePos           itype.Vec3f // position of the eye
	rotY, rotZ, fovY itype.Angle
	aspect           float32
	near, far        float32
	clipSet          bool
}

// View returns the View matrix from parameters set with LookAt().
func (r *View) View() mgl32.Mat4 {
	delta := mgl32.Rotate3DY(r.rotY.Radians()).Mul3(mgl32.Rotate3DZ(r.rotZ.Radians())).Mul3x1(mgl32.Vec3{1, 0, 0})

	return mgl32.LookAt(
		r.EyePos[0], r.EyePos[1], r.EyePos[2],
		r.EyePos[0]+delta[0],
		r.EyePos[1]+delta[1],
		r.EyePos[2]+delta[2],
		0, 1, 0,
	)
}

// Perspective returns the Perspective matrix with parameters set with Aspect(), FovY() and Clip().
func (r *View) Perspective() mgl32.Mat4 {
	if r.clipSet {
		return mgl32.Perspective(r.fovY.Radians(), r.aspect, r.near, r.far)
	} else {
		return mgl32.Perspective(r.fovY.Radians(), r.aspect, 0.1, 200)
	}
}

// LookAt resets the View matrix with a camera looking from Eye.
// The camera points to +RotY from X+ axis rotating around Y axis (right-handed);
//                    +/-RotZ form X+ axis rotating up/down around Z axis.
//     ( RotZ should be in the (-90,90) degrees range )
//
// It also returns itself so that operations can be chained.
func (r *View) LookAt(eye itype.Vec3f, rotY, rotZ itype.Angle) *View {
	r.EyePos = eye
	r.rotY = rotY
	r.rotZ = rotZ
	return r
}

// Aspect sets the aspect (width / height) of the viewport.
//
// It also returns itself so that operations can be chained.
func (r *View) Aspect(aspectRatio float32) *View {
	r.aspect = aspectRatio
	return r
}

// FovY sets the Field of View (an angle) on the Y axis.
// FovX = FovY * aspect.
//
// With FovX, call SetFovY(FovX / aspect).
//
// It also returns itself so that operations can be chained.
func (r *View) FovY(fovy itype.Angle) *View {
	r.fovY = fovy
	return r
}

// Clip sets the near/far clipping distance of the Perspective.
// It defaults to 0.1 / 200.
//
// It also returns itself so that operations can be chained.
func (r *View) Clip(near, far float32) *View {
	r.near, r.far = near, far
	r.clipSet = true
	return r
}
