#include "math.h"
#include "shader.h"


// Some references:
// http://www.edharriss.com/tutorials/tutorial_xsi_mental_ray_primer/Writing%20shaders.htm
// http://www.thedott.net/shaders/domeAFL/index.html
// http://www.pixero.com/downloads_mr.html
// http://www.andrewhazelden.com/blog/2010/11/cubemap-lens-shader-compiled-for-mental-ray-for-maya-2011-on-mac-os-x/


struct witness_fisheye_params {
    miInteger projection_type;
    miScalar vertical_fov;      // Cylindrical projection only.
};


DLLEXPORT int witness_fisheye_version(void)
{
	return 1;
}


DLLEXPORT miBoolean witness_fisheye(miColor	*result, miState *state, struct witness_fisheye_params *params)
{
    miVector dir, org;

    // Evaluate parameters in case they are animated.
    miInteger projection_type = *mi_eval_integer(&params->projection_type);
    miScalar vertical_fov = *mi_eval_scalar(&params->vertical_fov);

    // Use raster coordinate directly. This means the lens plugin cannot be combined with other plugins that transform the camera direction.
    miScalar x = state->raster_x / state->camera->x_resolution * 2 - 1;
    miScalar y = state->raster_y / state->camera->y_resolution * 2 - 1;

    if (projection_type == 0) { // FishEye
        // This is all based on the equation for R at http://wiki.panotools.org/Fisheye_Projection
        // JB: This code is based on the reversal for the fisheye transformation that I tested in XXX_temporary_test() in client.cpp.

        miScalar r2 = x * x + y * y;

        if (r2 < 1) {
            // Set ray direction.
            miScalar c = 1 - r2;
            miScalar s = sqrtf(2 - r2);

            dir.x = x * s;
            dir.y = y * s;
            dir.z = -c;    // I think z is supposed to be negative?

            mi_vector_from_camera(state, &dir, &dir);

            // Reset ray origin.
            org.x = org.y = org.z = 0;
            mi_point_from_camera(state, &org, &org);

            return mi_trace_eye(result, state, &org, &dir);
        }
    }
    else if (projection_type == 1) { // Cylindrical
        // Set ray direction.
        dir.x = sinf(0.5 * M_PI * x);
        dir.y = y * tanf(params->vertical_fov * 0.5f * (M_PI * 2) / 360); // We probaly want to offset this up or down.
        dir.z = -cosf(0.5 * M_PI * x);
        mi_vector_normalize(&dir);

        mi_vector_from_camera(state, &dir, &dir);

        // Reset ray origin.
        org.x = org.y = org.z = 0;
        mi_point_from_camera(state, &org, &org);

        return mi_trace_eye(result, state, &org, &dir);
    }
    else if (projection_type == 2) { // LatLong
        // Set ray direction.
	    dir.x = cosf(0.5 * M_PI * y) * sinf(0.5 * M_PI * x);
	    dir.y = sinf(0.5 * M_PI * y);
	    dir.z = -cosf(0.5 * M_PI * y) * cosf(0.5 * M_PI * x);

        mi_vector_normalize(&dir);

        mi_vector_from_camera(state, &dir, &dir);

        // Reset ray origin.
        org.x = org.y = org.z = 0;
        mi_point_from_camera(state, &org, &org);

        return mi_trace_eye(result, state, &org, &dir);
    }

    result->r = result->g =
    result->b = result->a = 0;
    return miFALSE;
}

