#include "camera.hpp"


#define DOV 10000.0


Camera::Camera(const vertex_t& origin, const vertex_t& direction, unsigned w, unsigned h)
    : ray(origin, direction), depth(direction.len()), raster_w((w*2+1)/2), raster_h((h*2+1)/2) {
    update_raster();
}


void Camera::update_raster() {
    raster_center = ray.origin + ray.direction;

    static const vertex_t y_axis(0, 1, 0);
    //if (y_axis.dot(ray.direction) >= 0.0) { // orthogonal or acute angle
    raster_x = y_axis.crossed(ray.direction);
    if (raster_x.nulls() == 3) { // direction was along the y axis
        raster_x.x = 1;
    }
    raster_y = ray.direction.crossed(raster_x);
    raster_x.norm();
    raster_y.norm();
}


void Camera::get_move(const vertex_t& moveo, ray_t& rv) const {
    rv.origin = ray.origin;
    rv.direction = (raster_x * moveo.x) + (raster_y * moveo.y) + (ray.direction.normed() * moveo.z);
}


void Camera::move(const vertex_t& moveo, const vertex_t& movel) {
    ray.origin += (raster_x * moveo.x) + (raster_y * moveo.y) + (ray.direction.normed() * moveo.z);
    ray.direction += (raster_x * movel.x) + (raster_y * movel.y);
    ray.direction.norm();
    ray.direction *= depth;
    update_raster();
}


void Camera::get_ray(unsigned x, unsigned y, ray_t& r, vertex_t& nrm) const {
    assert(x < raster_w);
    assert(y < raster_h);
#if 1
    // from origin through raster
    r.origin = ray.origin;
    nrm = raster_center + (raster_x * ((int)x-((int)raster_w/2))) + (raster_y * ((int)y-((int)raster_h/2))) - ray.origin;
#else
    // start at raster, s.t. we wont see anything between origin and raster and for better box checks
    r.origin = raster_center + (raster_x * ((int)x-((int)raster_w/2))) + (raster_y * ((int)y-((int)raster_h/2)));
    nrm = r.origin - ray.origin;
#endif
    // parallel projection: { raster_center + (raster_x * ((int)x-((int)raster_w/2))) + (raster_y * ((int)y-((int)raster_h/2))), ray.direction }
    nrm.norm(); // will be cached
    r.direction = nrm * DOV;
}