from .utils.vector3 import vec3, rgb
from .utils.random import random_in_unit_disk
import numpy as np
from .ray import Ray

class Camera():
    def __init__(self, look_from, look_at, screen_width = 400 ,screen_height = 300,  field_of_view = 90., aperture = 0., focal_distance = 1.):
        self.screen_width = screen_width
        self.screen_height = screen_height
        self.aspect_ratio = float(screen_width) / screen_height

        self.look_from = look_from
        self.look_at = look_at
        self.camera_width = np.tan(field_of_view * np.pi/180   /2.)*2.
        self.camera_height = self.camera_width/self.aspect_ratio

        #camera reference basis in world coordinates
        self.cameraFwd = (look_at - look_from).normalize()
        self.cameraRight = (self.cameraFwd.cross(vec3(0.,1.,0.))).normalize()
        self.cameraUp = self.cameraRight.cross(self.cameraFwd)



        #if you use a lens_radius >= 0.0 make sure that samples_per_pixel is a large number. Otherwise you'll get a lot of noise
        self.lens_radius =  aperture / 2.
        self.focal_distance = focal_distance



        # Pixels coordinates in camera basis:
        self.x = np.linspace(-self.camera_width/2., self.camera_width/2., self.screen_width)
        self.y = np.linspace(self.camera_height/2., -self.camera_height/2., self.screen_height)

        # we are going to cast a total of screen_width * screen_height * samples_per_pixel rays 
        # xx,yy store the origin of each ray in a 3d array where the first and second dimension are the x,y coordinates of each pixel
        # and the third dimension is the sample index of each pixel
        xx,yy = np.meshgrid(self.x,self.y)
        self.x = xx.flatten()
        self.y = yy.flatten()

    def get_ray(self,n): # n = index of refraction of scene main medium (for air n = 1.)

        # in each pixel, take a random position to avoid aliasing.
        x = self.x + (np.random.rand(len(self.x )) - 0.5)*self.camera_width  /(self.screen_width)
        y = self.y + (np.random.rand(len(self.y )) - 0.5)*self.camera_height /(self.screen_height)

        # set ray direction in world space:
        rx, ry = random_in_unit_disk(x.shape[0])
        ray_origin = self.look_from  +   self.cameraRight *rx* self.lens_radius   +   self.cameraUp *ry* self.lens_radius
        ray_dir = (self.look_from  +   self.cameraUp*y*self.focal_distance  +  self.cameraRight*x*self.focal_distance  + self.cameraFwd*self.focal_distance  - ray_origin  ).normalize()
        return Ray(origin=ray_origin, dir=ray_dir, depth=0,  n=n, reflections = 0, transmissions = 0, diffuse_reflections = 0)

