#include <GLFW/glfw3.h>
#ifdef __APPLE__
#include <OpenGL/glu.h>
#include <OpenGL/gl.h>
// Tell Xcode IDE to not gripe about OpenGL deprecation
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
#include <math.h>
#include <stdio.h>

#include "draw_steve.h"
#include "main.h"

void 
draw_leg(float hipX, float hipY, float hipZ, float angle)
{
    glPushMatrix();

    /* ensure the leg renders "behind" the chest to prevent z fighting */
    glEnable(GL_POLYGON_OFFSET_FILL);
    glPolygonOffset(1.0f, 1.0f);

    glTranslatef(hipX, hipY, hipZ);
    glRotatef(angle, 1.0f, 0.0f, 0.0f);

    for (int row = 0; row < S_LEG_ROWS; row++)
    {
        Color color;
        if (row < 2)
        {
            color = COLOR_SHOES;
        }
        else
        {
            color = COLOR_PANTS;
        }

        for (int col = 0; col < S_LEG_WIDTH; col++)
        {
            for (int d = 0; d < S_LEG_DEPTH; d++)
            {
                /* center the depth on the hip so the leg pivots around its middle */
                float z = (d - (S_LEG_DEPTH - 1) / 2.0f) * OFFSET_UNIT;

                drawCube(col * OFFSET_UNIT,
                         (row - (S_LEG_ROWS - 1)) * OFFSET_UNIT,
                         z,
                         SIZE_UNIT, SIZE_UNIT, SIZE_UNIT,
                         90.0f, color);
            }
        }
    }

    glDisable(GL_POLYGON_OFFSET_FILL);

    glPopMatrix();
}

void 
draw_chest(float x, float y, float z)
{
    glPushMatrix();
    glTranslatef(x, y, z);

    for (int row = 0; row < S_CHEST_ROWS; row++)
    {

        int row_from_top = S_CHEST_ROWS - row;

        for (int col = 0; col < S_TOROSO_WIDTH; col++)
        {
            int right = S_TOROSO_WIDTH - 1;

            int corner = (row == 0 && col == right) ||
                         (row == 1 && col == right - 1) ||
                         (row == 1 && col == right);

            int c = col + 1;
            int skin = (row_from_top == 1 && c >= 3 && c <= 6) ||
                       (row_from_top == 2 && c >= 4 && c <= 5);

            for (int d = 0; d < S_CHEST_DEPTH; d++)
            {
                Color color;
                if (skin)
                {
                    color = COLOR_SKIN;
                }
                else if (row < 2 && !(corner && d == S_LEG_DEPTH - 1))
                {
                    color = COLOR_PANTS; /* pants */
                }
                else
                {
                    color = COLOR_SHIRT; /* shirt */
                }

                float dz = (d - (S_CHEST_DEPTH - 1) / 2.0f) * OFFSET_UNIT;

                drawCube(col * OFFSET_UNIT,
                         row * OFFSET_UNIT,
                         dz,
                         SIZE_UNIT, SIZE_UNIT, SIZE_UNIT,
                         90.0f, color);
            }
        }
    }

    glPopMatrix();
}

void 
draw_arm(float x, float y, float z, float angle)
{
    glPushMatrix();
    glTranslatef(x, y, z);
    glRotatef(angle, 1.0f, 0.0f, 0.0f);

    for (int row = 0; row < S_ARM_ROWS; row++)
    {
        Color color;
        if (row < S_ARM_SLEEVE_ROWS)
        {
            color = COLOR_SHIRT; /* shirt sleeve */
        }
        else
        {
            color = COLOR_SKIN; /* arm */
        }

        for (int col = 0; col < S_ARM_WIDTH; col++)
        {
            for (int d = 0; d < S_ARM_DEPTH; d++)
            {
                float dz = (d - (S_ARM_DEPTH - 1) / 2.0f) * OFFSET_UNIT;

                drawCube(col * OFFSET_UNIT,
                         -row * OFFSET_UNIT, /* row 0 is the top, arm hangs down */
                         dz,
                         SIZE_UNIT, SIZE_UNIT, SIZE_UNIT,
                         90.0f, color);
            }
        }
    }

    glPopMatrix();
}

void 
draw_head(float x, float y, float z)
{
    /* no call to alloca(3) necessary if it's in .data! */
    static const Color face_rows[4][S_HEAD_SIZE] = {
        /* row 5: eyes */
        {COLOR_SKIN, {255, 255, 255}, {82, 61, 136}, COLOR_SKIN, COLOR_SKIN, {82, 61, 136}, {255, 255, 255}, COLOR_SKIN},
        /* 6: nose */
        {COLOR_SKIN, COLOR_SKIN, COLOR_SKIN, {175, 98, 55}, {175, 98, 55}, COLOR_SKIN, COLOR_SKIN, COLOR_SKIN},
        /* 7: mouth */
        {COLOR_SKIN, COLOR_SKIN, {83, 33, 5}, COLOR_SKIN, COLOR_SKIN, {83, 33, 5}, COLOR_SKIN, COLOR_SKIN},
        /* 8: mouth */
        {COLOR_SKIN, COLOR_SKIN, {83, 33, 5}, {83, 33, 5}, {83, 33, 5}, {83, 33, 5}, COLOR_SKIN, COLOR_SKIN}};

    glPushMatrix();
    glTranslatef(x, y, z);

    for (int row = 0; row < S_HEAD_SIZE; row++)
    {
        /* 1-based row from the top */
        int r = S_HEAD_SIZE - row;

        for (int col = 0; col < S_HEAD_SIZE; col++)
        {
            /* 1-based column from the left */
            int c = col + 1;

            for (int d = 0; d < S_HEAD_DEPTH; d++)
            {
                Color color = COLOR_SKIN;
                float eps = 0.0f; /* extra offset toward the viewer for face details */

                if (r <= 2 || (r == 3 && (c == 1 || c == S_HEAD_SIZE)))
                {
                    color = COLOR_HAIR;
                }
                else if (d == S_HEAD_DEPTH - 1) /* front face */
                {
                    if (r >= 5)
                    {
                        color = face_rows[r - 5][col];
                        eps = FACE_EPSILON;
                    }
                }

                /* d == 0 is the lowest z, so "toward the viewer" should be -z */
                float dz = (d - (S_HEAD_DEPTH - 1) / 2.0f) * OFFSET_UNIT - eps;

                drawCube(col * OFFSET_UNIT,
                         row * OFFSET_UNIT,
                         dz,
                         SIZE_UNIT, SIZE_UNIT, SIZE_UNIT,
                         90.0f, color);
            }
        }
    }

    glPopMatrix();
}