#include "box.hpp"

static float point::world_scale = 12 * 12;      // ie 12 feet
static float point::vertical_shift = 0;

// Functions which could be inlined
point::point( float x0, float y0, float z0 ) : x(x0), y(y0), z(z0) { }
point::point( point& src ) : x(src.x), y(src.y), z(src.z) { }
void point::move_left( float amount ) { x -= amount; }
void point::move_right( float amount ) { x += amount; }
void point::move_up( float amount ) { y += amount; }
void point::move_down( float amount ) { y -= amount; }
void point::move_forward( float amount ) { z -= amount; }
void point::move_back( float amount ) { z += amount; }

color::color(unsigned char red,unsigned char green,unsigned char blue)
                : r(red), g(green), b(blue) { }
color::color(const color& src) : r(src.r), g(src.g), b(src.b) { }
color::color() : r(0), g(0), b(0) { }
void color::rgb(unsigned char red, unsigned char green, unsigned char blue)
                { r = red; g = green; b = blue; }
void color::rgb(color& src) { rgb( src.r, src.g, src.b); }
void color::rgb(color* src) { rgb( src->r, src->g, src->b); }
void color::red( unsigned char intensity ) { r = intensity; }
void color::green( unsigned char intensity ) { g = intensity; }
void color::blue( unsigned char intensity ) { b = intensity; }

float box::width() { return size_vector.x; }
float box::height() { return size_vector.y; }
float box::depth() { return size_vector.z; }
void box::left( float x0 ) { x = x0; }
void box::right( float x0 ) { x = x0 - size_vector.x; }
void box::front( float z0 ) { z = z0 - size_vector.z; }
void box::back( float z0 ) { z = z0; }
void box::top( float y0 ) { y = y0 - size_vector.y; }
void box::bottom( float y0 ) { y = y0; }
float box::left() { return x; }
float box::right() { return x + size_vector.x; }
float box::top() { return y + size_vector.y; }
float box::bottom() { return y; }
float box::front() { return( z + size_vector.z ); }
float box::back() { return z; }
float box::mid_width() { return x + size_vector.x / 2; }
float box::mid_height() { return y + size_vector.y / 2; }
float box::mid_depth() { return z + size_vector.z / 2; }


void point::rotate( orientation move )
{
    float       temp;

    switch( move % 4 ) {
    case FRONT:
        // nothing to do
        break;
    case RIGHT:
        temp = x;
        x = z;
        z = -temp;
        break;
    case BACK:
        x = -x;
        z = -z;
        break;
    case LEFT:
        temp = x;
        x = -z;
        z = temp;
        break;
    }
}

void point::moveto( float x0, float y0, float z0 )
{
    x = x0;
    y = y0;
    z = z0;
}

void point::moveto( point& origin )
{
    x = origin.x;
    y = origin.y;
    z = origin.z;
}

void point::shift( float xdelta, float ydelta, float zdelta )
{
    x += xdelta;
    y += ydelta;
    z += zdelta;
}

void point::shift( point& vector )
{
    shift( vector.x, vector.y, vector.z );
}

box::box( float across, float depth, float height )
    : point(0,0,0), size_vector(across, height, depth), facing(FRONT)
{
    children = 0;
    sibling = 0;
}

box::~box()
{
    delete children;
    delete sibling;
}

static void point::world_size( float wide, float deep, float high )
{
    // To keep proportions correct, just remember the largest of the dimensions

    world_scale = wide;
    if( world_scale < deep ) {
        world_scale = deep;
    }
    if( world_scale < high ) {
        world_scale = high;
        vertical_shift = 0;
    } else {
        vertical_shift = (world_scale - high) / 2;
    }
}

void box::add(box* child)
{
    child->sibling = children;
    children = child;
}

void box::orient( orientation rotation )
{
    facing = (orientation) ((facing + rotation) % 4);
}

void box::orient( box * other )
{
    orient( other->facing );
}

void box::position_to_right_of( box& to_left )
{
    // Move to same position & facing as to_left
    moveto( to_left );
    orient( to_left.facing );
    // Now move across by the width of to_left
    point shift_vector( to_left.width(), 0, 0 );
    shift_vector.rotate( facing );
    shift( shift_vector );
}

void box::change_color( color newclr )
{
    rgb( newclr );
    for( box * curr = children; curr; curr = curr->sibling ) {
        curr->change_color( newclr );
    }
}

void box::draw( point& origin, orientation face )
{
    // First, combine the rotations
    face = (orientation) ((face + facing) % 4);
    if( children ) {
        for( box * curr = children; curr; curr = curr->sibling ) {
            point curr_origin = *curr;
            curr_origin.rotate( face );
            curr_origin.shift( origin );
            curr->draw( curr_origin, face );
        }
    } else {
        // Only draw outer box if there is no children
        point new_size = size_vector;
        new_size.rotate( face );
        point other_corner = origin;
        other_corner.shift( new_size );
        draw_box( origin.x / world_scale, other_corner.x / world_scale,
                  (origin.y + vertical_shift) / world_scale,
                  (other_corner.y + vertical_shift) / world_scale,
                  origin.z / world_scale, other_corner.z / world_scale,
                  r, g, b );
    }
}

