#include <stdio.h>
#include <math.h>

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

extern double atof();

extern char *optarg;
extern int optind,  opterr;


#define RESOLUTION    5
#define FLOORSIZE     15.0


Marble_desc teapot_surf = {
    0.4, 
    0.5, 
    0.05, 
    8.0, 
    {0.90, 0.80, 0.65}, 
    {0.30, 0.08, 0.08}, 
    {1.0, 1.0, 1.0}
};


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} }
};


/*
 * 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);
}



main(argc, argv)
    int     argc;
    char  **argv;
{
    Object  *teapot;		/* The teapot and its parts. */
    Object  *handle;
    Object  *spout;
    Object  *body;
    Object  *lid;
    Object  *bottom;
    Transf_mat  * teapot_transf;

    Object  *floor;		/* The floor. */

    FILE    *infile;
    int      image_size;
    FILE    *image;
    int      frame;
    int      mode;

    double   time_start;	/* Animation time values. */
    double   time_stop;
    double   time_step;
    double   time;
    double   t_jump;
    double   height;
    double   t_scale;
    double   xyscaling;
    double   zscaling;
    double   angle;
    char     filename[256];
    char     *file_ext;
    int      c;

    mode = LINE;
    time_start = 0.0;
    time_stop  = 1.0;
    time_step  = 0.04;
    image_size = 256;
    file_ext = "pbm";

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

          case 'g':
            mode = GOURAUD;
            file_ext = "ppm";
            break;

          case 'f':
            mode = FLAT;
            file_ext = "ppm";
            break;

          case 'l':
            mode = LINE;
            file_ext = "pbm";
            break;

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


    sipp_init();


    /* Create the floor. */
    floor = sipp_block(FLOORSIZE, FLOORSIZE, 1.0, &floor_surf, floor_shader,
                       WORLD);
    object_move(floor, 0.0, 0.0, -0.5);
    object_add_subobj(sipp_world, floor);


    /* Create the teapot and its parts. */
    infile = fopen("../tpt_handle.bez", "r");
    handle = sipp_bezier_file(infile, RESOLUTION, &teapot_surf, marble_shader,
                              WORLD);
    fclose(infile);

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

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

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

    bottom = sipp_cylinder(0.375, 0.01, RESOLUTION * 4, &teapot_surf,
                           marble_shader, WORLD);

    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);


    /* Lit the stage! */
    lightsource_create(0.2, -2.0, 1.0, 1.0, 1.0, 1.0, LIGHT_DIRECTION);
    lightsource_create(1.0, 0.0, 0.5, 0.4, 0.4, 0.4, LIGHT_DIRECTION);


    /* Viewing parameters. */
    camera_position(sipp_camera, 16.0, -24.0, 4.0);
    camera_look_at(sipp_camera, 0.0, 0.0, 1.4);
    camera_up(sipp_camera, 0.0, 0.0, 1.0);
    camera_focal(sipp_camera, 0.0625);


    /*
     * The following code is quite ugly and full of magic numbers.
     * It is basically two parabolas that describe the teapots jump 
     * and its "squashing" when it lands.
     */
    frame = time_start / time_step;
    time = frame * time_step;
    while (time < time_stop) {

        if (time < 0.65) {
	    /* During the jump */
            t_jump = time * 1.94464;
            height = 6.2 * t_jump - 9.81 * t_jump * t_jump / 2.0;
            angle = 2.0 * M_PI * time / 0.65;
            xyscaling = 1.0;
            zscaling = 1.0;

        } else {
	    /* During the squashing phase. */
            height = 0.0;
            angle = 0.0;
            t_scale = (time - 0.65) * 0.64533;
            xyscaling = 1.0 + (1.0 - zscaling) * M_SQRT1_2;
            zscaling = 1.0 - 6.2 * t_scale + 54.9 * t_scale * t_scale / 2.0;
        }

	/* 
	 * Save the original transformation state of the teapot.
	 * It is easier to recreate the proper position from scratch
	 * for each new frame than to calculate the difference between
	 * one image and the next.
	 */
	teapot_transf = object_get_transf(teapot, NULL);

	/* Place the teapot in its proper position. */
        object_scale(teapot, xyscaling, xyscaling, zscaling);
        object_move(teapot, 0.0, 0.0, -0.4);
        object_rot_y(teapot, -angle);
        object_move(teapot, 0.0, 0.0, 0.4);
        object_move(teapot, 0.0, 0.0, height);

        sprintf(filename, "anim%02d.%s", frame, file_ext);
        image = fopen(filename, "w");
        printf("\rRendering frame %2d...", frame);
        fflush(stdout);

	/* Render the image. */
        render_image_file(image_size, image_size, image, mode, 3);
        fclose(image);

	/* Reset the teapot to its original position and shape. */
	object_set_transf(teapot, teapot_transf);

        frame++;
        time = frame * time_step;
    }

    printf("done.\n");
    exit(0);
}
