#ifndef _RAYTRACER_H_
#define _RAYTRACER_H_

#include <system.h>
#include <io.h>

#define CAM_X 0
#define CAM_Y 2
#define CAM_Z 4

// r, g, and b must be at least 0 and at most 3
static alt_u8 raw_rgb_to_color(alt_u8 r, alt_u8 g, alt_u8 b) {
    return (r << 4 | g << 2 | b);
}

// The compiler should optimize away this function.
static void write_rect(unsigned rect_num,
                       alt_u8 x1, alt_u8 y1, alt_u8 z1,
                       alt_u8 x2, alt_u8 y2, alt_u8 z2,
                       alt_u8 color) {
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14, x1);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 2, y1);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 4, z1);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 6, x2);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 8, y2);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 10, z2);
    IOWR_16DIRECT(RAY_TRACER_AVALON_MODULE_INST_BASE, 6 + rect_num * 14 + 12, color);
}

// This craziness is to provide compile time range checking on the arguments to
// rgb_to_color.
// This can only take constants. It seems to work without compiler optimizations 
// being turned on, but I don't know how.
// It works as follows: 
// If one of the args is an illegal value, the expression will want to use the
// variable assertion_failed. This variable is listed as extern, but we don't
// actually define it anywhere. So we'll get a linker error.
// If all the values are ok, the expression will use the part which does the 
// bit manipulation. In this case, the compiler never looks at the alternative 
// and the linker never sees assertion_fails.
// Based on stuff from:
// http://www.embedded.com/columns/programmingpointers/164900888?_requestid=1002553 
#define rgb_to_color(r, g, b) \
    (((r) >= 0 && (r) <= 3 && (g) >= 0 && (g) <= 3 && (b) >= 0 && (b) <= 3) ? \
     raw_rgb_to_color((r), (g), (b)) : \
     assertion_failed)
extern int assertion_failed;

#endif
