import math
from typing import Self

#"pip install PyOpenGL"
from OpenGL.GL  import *
from OpenGL.GLU import *

from .. import config
from ..glbase import gl_misc
from ..math_util import *

from . import marble



class PerspectiveCamera:
	__slots__ = ( "res", "vfov_rad", "clip_near","clip_far", "center","pos","up" )

	def __init__( self:Self, res:tuple[int,int], vfov_rad:float, clip_near:float,clip_far:float ):
		self.res = tuple(res)

		self.vfov_rad = vfov_rad

		self.clip_near = clip_near
		self.clip_far  = clip_far

		self.center = Vector3(0.0)
		self.pos    = Vector3(0.0)
		self.up = Vector3( 0.0, 0.0, 1.0 )

	def get_proj_3D( self:Self ) -> Matrix:
		aspect = float(self.res[0]) / float(self.res[1])
		return Matrix.get_proj_persp_rad(
			self.vfov_rad, aspect, self.clip_near,self.clip_far
		)
	def get_view_3D( self:Self ) -> Matrix:
		return Matrix.get_view_lookat( self.pos, self.center, self.up )
	def get_projview_3D( self:Self ):
		return self.get_proj_3D() * self.get_view_3D()

	def set_gl2_projview_3D( self:Self ):
		gl_misc.set_gl2_projview_3D(
			self.res, math.degrees(self.vfov_rad), self.clip_near,self.clip_far,
			self.pos, self.center, self.up
		)

class GameCamera(PerspectiveCamera):
	__slots__ = ( "angles", "radius", "center_target","pos_target" )

	def __init__( self:Self ):
		PerspectiveCamera.__init__( self,
			config.window_res, math.radians(config.camera_fov_deg), config.camera_near,config.camera_far
		)

		self.angles = [ config.CAMERA_DEFAULT_AZI, config.CAMERA_DEFAULT_ELEV ]
		self.radius = config.CAMERA_DEFAULT_RADIUS

		self.center_target = Vector3(0.0)
		self.pos_target    = Vector3(0.0)

	def reset( self:Self ):
		self.angles = [ config.CAMERA_DEFAULT_AZI, config.CAMERA_DEFAULT_ELEV ]
		self.radius = config.CAMERA_DEFAULT_RADIUS

	def set_center( self:Self, center:Vector3 ):
		self.center = Vector3(center)
		self.pos    = self.center + Vector3(0.0,-3.57,4.3)
		self.center_target = Vector3(self.center)
		self.pos_target    = Vector3(self.pos   )

	def set_target( self:Self, target:marble.Marble ):
		self.center_target = target.center #+ 0.3*target.vel

	def update( self:Self, dt:float ):
		#camera_pos = self.center + Vector3(
		#	0.0,
		#	-3.57, 4.3
		#)
		azi=math.radians(self.angles[0]); elev=math.radians(self.angles[1])
		self.pos_target = self.center_target + Vector3(
			self.radius * math.cos(azi) * math.cos(elev),
			self.radius * math.sin(azi) * math.cos(elev),
			self.radius *                 math.sin(elev)
		)

		t = 1.0 - math.exp( math.log(1.0-config.camera_converge) * dt )
		self.center = lerp( self.center,self.center_target, t )
		self.pos    = lerp( self.pos   ,self.pos_target   , t )
