#include <stdio.h>

#include <sipp.h>
#include <primitives.h>
#include <geometric.h>
#include <noise.h>
#include <shaders.h>


#define RESOLUTION 9

Surf_desc teapot_surf = {
    0.4, 
    0.5,
    0.1, 
    {0.9, 0.6, 0.6}, 
    {0.45, 0.45, 0.45},
};

typedef struct {
    double   sqsize;
    Surf_desc   col1;
    Surf_desc   col2;
} Floor_desc;


Floor_desc floor_surf = {
    1.0, 
    { 0.4, 0.0, 0.1, {0.9900, 0.9000, 0.7900}, {1.0, 1.0, 1.0} },
    { 0.4, 0.0, 0.1, {0.8300, 0.2400, 0.1000}, {1.0, 1.0, 1.0} }
};


extern bool noise_ready;


void
hole_shader(pos, normal, texture, view_vec, lights, sd, color, transp)
    Vector      *pos;
    Vector      *normal;
    Vector      *texture;
    Vector      *view_vec;
    Lightsource *lights;
    Surf_desc   *sd;
    Color       *color;
    Color       *transp;
{
    Vector     tmp;

    if (!noise_ready) {
        noise_init();
    }

    VecCopy(tmp, *texture);
    VecScalMul(tmp, 35.0, tmp);
    
    if (noise(&tmp) < -0.1) {
        sd->opacity.red = 0.0;
        sd->opacity.grn = 0.0;
        sd->opacity.blu = 0.0;
    } else {
        sd->opacity.red = 1.0;
        sd->opacity.grn = 1.0;
        sd->opacity.blu = 1.0;
    }

    basic_shader(pos, normal, texture, view_vec, lights, sd, color, transp);
}


/*
 * A shader to produce a checkered floor.
 */
static void
floor_shader(pos, normal, texture, view_vec, lights, fd, color, transp)
    Vector      *pos;
    Vector      *normal;
    Vector      *texture;
    Vector      *view_vec;
    Lightsource *lights;
    Floor_desc  *fd;
    Color       *color;
    Color       *transp;
{
    Surf_desc  * col;
    int          intu;
    int          intv;

    intu = floor(texture->x / fd->sqsize);
    if (intu < 0) 
	intu = -intu;

    intv = floor(texture->y / fd->sqsize);
    if (intv < 0) 
	intv = -intv;

    if ((intu ^ intv) & 1)
	col = &fd->col1;
    else
        col = &fd->col2;

    basic_shader(pos, normal, texture, view_vec, lights, col, color, transp);
}



extern char *optarg;

main(argc, argv)
    int argc;
    char **argv;
{
    Object  *teapot;
    Object  *handle;
    Object  *spout;
    Object  *body;
    Object  *lid;
    Object  *bottom;
    FILE    *infile;
    FILE    *image;

    Object  *floor;

    char    *imfile_name;
    int      mode;
    int      c;
    int      size;

    imfile_name = "teapot.ppm";
    mode = PHONG;
    size = 256;

    while ((c = getopt(argc, argv, "pgfls:")) != EOF) {
        switch (c) {
          case 'p':
            mode = PHONG;
            imfile_name = "teapot.ppm";
            break;

          case 'g':
            mode = GOURAUD;
            imfile_name = "teapot.ppm";
            break;

          case 'f':
            mode = FLAT;
            imfile_name = "teapot.ppm";
            break;

          case 'l':
            mode = LINE;
            imfile_name = "teapot.pbm";
            break;

          case 's':
            size = atoi(optarg);
            break;
        }
    }

    sipp_init();
    sipp_show_backfaces(TRUE);
    sipp_shadows(TRUE, ((size<512)?2*size:size));
    sipp_background(0.078, 0.361, 0.753); /* UNC sky blue */

    infile = fopen("tpt_handle.bez", "r");
    handle = sipp_bezier_file(infile, RESOLUTION, &teapot_surf, hole_shader);
    fclose(infile);

    infile = fopen("tpt_spout.bez", "r");
    spout = sipp_bezier_file(infile, RESOLUTION, &teapot_surf, hole_shader);
    fclose(infile);

    infile = fopen("tpt_body.bez", "r");
    body = sipp_bezier_file(infile, RESOLUTION, &teapot_surf, hole_shader);
    fclose(infile);

    infile = fopen("tpt_lid.bez", "r");
    lid = sipp_bezier_file(infile, RESOLUTION, &teapot_surf, hole_shader);
    fclose(infile);

    bottom = sipp_cylinder(0.375, 0.01, RESOLUTION * 4, &teapot_surf,
                           hole_shader);
    object_move(bottom, 0.0, 0.0, 0.005);

    teapot = object_create();
    object_add_subobj(teapot, body);
    object_add_subobj(teapot, lid);
    object_add_subobj(teapot, handle);
    object_add_subobj(teapot, spout);
    object_add_subobj(teapot, bottom);

    object_add_subobj(sipp_world, teapot);

    floor = sipp_block(7.0, 7.0, 0.2, &floor_surf, floor_shader);
    object_move(floor, 0.0, 0.0, -0.1);
    object_add_subobj(sipp_world, floor);

    lightsource_create(-3.0, -2.0, 6.0, 0.35, 0.35, 0.35, LIGHT_DIRECTION);
    spotlight_create(-3.0, -2.0, 6.0,  
                     0.0, 0.0, 0.0, 
                     25.0, 
                     0.45, 0.45, 0.45, 
                     SPOT_SOFT, TRUE);

    camera_params(sipp_camera, 1.65, -7.7, 3.3,  0.0, 0.0, 0.4,  
                  0.0, 0.0, 1.0,  0.125);

    printf("Rendering, wait...");
    fflush(stdout);

    image = fopen(imfile_name, "w");
    render_image_file(size, size, image, mode, 3);
    printf("Done.\n");

    exit(0);
}
