/*
 * face_animation.c — Face Emotion Renderer for LCD Display
 *
 * Implements expressive face animations with smooth transitions between emotions.
 * Supports idle blinking and parameterized eye/mouth shapes for each emotion.
 */

#include "face_animation.h"
#include "face_lcd.h"
#include <math.h>
#include <string.h>

/* === Configuration === */
#define TRANSITION_FRAMES   15      /* ~0.5s at 30Hz */
#define BLINK_DURATION_MS   120     /* ~4 frames at 30Hz */
#define BLINK_INTERVAL_MS   4000    /* ~120 frames at 30Hz */

/* === Display Dimensions (centered face layout) === */
#define FACE_CENTER_X       (LCD_WIDTH / 2)
#define FACE_CENTER_Y       (LCD_HEIGHT / 2)
#define EYE_RADIUS          5
#define EYE_SPACING         20      /* Distance between eyes */
#define BROW_LENGTH         12
#define MOUTH_WIDTH         16

/* === Emotion Parameter Sets === */
static const face_params_t emotion_params[6] = {
    /* FACE_HAPPY */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -10,
        .eye_open_y = 5,           .eye_close_y = 0,
        .brow_angle = 15,          .brow_y_offset = -6,
        .mouth_x = 0,              .mouth_y = 10,
        .mouth_width = MOUTH_WIDTH, .mouth_curve = 4,  /* Upturned smile */
        .blink_interval_ms = 120,
    },
    /* FACE_SAD */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -8,
        .eye_open_y = 5,           .eye_close_y = 0,
        .brow_angle = -15,         .brow_y_offset = -8,
        .mouth_x = 0,              .mouth_y = 12,
        .mouth_width = MOUTH_WIDTH, .mouth_curve = -3,  /* Downturned frown */
        .blink_interval_ms = 180,  /* Slower blink when sad */
    },
    /* FACE_CURIOUS */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -12,
        .eye_open_y = 7,           .eye_close_y = 0,     /* Wide eyes */
        .brow_angle = 20,          .brow_y_offset = -10, /* Raised brows */
        .mouth_x = 2,              .mouth_y = 10,
        .mouth_width = 12,         .mouth_curve = 1,     /* Slight smile */
        .blink_interval_ms = 150,
    },
    /* FACE_ANGRY */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -6,
        .eye_open_y = 3,           .eye_close_y = 0,     /* Narrowed eyes */
        .brow_angle = -20,         .brow_y_offset = -5,
        .mouth_x = 0,              .mouth_y = 11,
        .mouth_width = 14,         .mouth_curve = -5,    /* Strong frown */
        .blink_interval_ms = 90,   /* Angry blinks faster */
    },
    /* FACE_SLEEPING */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -8,
        .eye_open_y = 0,           .eye_close_y = -2,    /* Closed/squinted */
        .brow_angle = 5,           .brow_y_offset = -4,
        .mouth_x = 0,              .mouth_y = 10,
        .mouth_width = 10,         .mouth_curve = 2,     /* Peaceful smile */
        .blink_interval_ms = 60,   /* Not used when sleeping */
    },
    /* FACE_NEUTRAL */
    {
        .eye_x = -EYE_SPACING/2,   .eye_y = -8,
        .eye_open_y = 5,           .eye_close_y = 0,
        .brow_angle = 0,           .brow_y_offset = -6,
        .mouth_x = 0,              .mouth_y = 10,
        .mouth_width = 12,         .mouth_curve = 0,     /* Straight line */
        .blink_interval_ms = 120,
    },
};

/* === Animation State === */
static struct {
    face_emotion_t current_emotion;
    face_emotion_t target_emotion;
    uint16_t frame;                 /* Current frame in animation */
    uint16_t transition_frame;      /* Frame counter for transition */
    bool is_transitioning;          /* True if mid-transition */

    uint16_t blink_timer;           /* Frames until next blink */
    uint16_t blink_frame;           /* Current frame in blink animation */
    bool is_blinking;               /* True if mid-blink */
} anim_state = {
    .current_emotion = FACE_NEUTRAL,
    .target_emotion = FACE_NEUTRAL,
    .frame = 0,
    .transition_frame = 0,
    .is_transitioning = false,
    .blink_timer = BLINK_INTERVAL_MS / 33,  /* ~120 frames */
    .blink_frame = 0,
    .is_blinking = false,
};

/* === Easing Functions === */

/**
 * Ease-in-out cubic interpolation [0, 1].
 * Smooth acceleration/deceleration for transitions.
 */
static float ease_in_out_cubic(float t) {
    if (t < 0.5f)
        return 4.0f * t * t * t;
    else {
        float f = 2.0f * t - 2.0f;
        return 0.5f * f * f * f + 1.0f;
    }
}

/**
 * Interpolate two emotion parameters by factor [0, 1].
 */
static face_params_t interpolate_params(const face_params_t *a,
                                        const face_params_t *b,
                                        float t) {
    face_params_t result;
    result.eye_x = (int16_t)(a->eye_x + (b->eye_x - a->eye_x) * t);
    result.eye_y = (int16_t)(a->eye_y + (b->eye_y - a->eye_y) * t);
    result.eye_open_y = (int16_t)(a->eye_open_y + (b->eye_open_y - a->eye_open_y) * t);
    result.eye_close_y = (int16_t)(a->eye_close_y + (b->eye_close_y - a->eye_close_y) * t);
    result.brow_angle = (int16_t)(a->brow_angle + (b->brow_angle - a->brow_angle) * t);
    result.brow_y_offset = (int16_t)(a->brow_y_offset + (b->brow_y_offset - a->brow_y_offset) * t);
    result.mouth_x = (int16_t)(a->mouth_x + (b->mouth_x - a->mouth_x) * t);
    result.mouth_y = (int16_t)(a->mouth_y + (b->mouth_y - a->mouth_y) * t);
    result.mouth_width = (int16_t)(a->mouth_width + (b->mouth_width - a->mouth_width) * t);
    result.mouth_curve = (int16_t)(a->mouth_curve + (b->mouth_curve - a->mouth_curve) * t);
    return result;
}

/* === Drawing Functions === */

/**
 * Draw an eye (circle) with optional closure (eyelid).
 */
static void draw_eye(int16_t x, int16_t y, int16_t open_y, int16_t close_y,
                     bool is_blinking) {
    lcd_color_t color = LCD_WHITE;

    /* Eye position accounts for blink closure */
    int16_t eye_h = is_blinking ? close_y : open_y;
    if (eye_h <= 0) {
        /* Closed: draw horizontal line instead */
        face_lcd_line(x - EYE_RADIUS, y, x + EYE_RADIUS, y, color);
    } else {
        /* Open: draw circle (simplified ellipse) */
        face_lcd_circle(x, y, EYE_RADIUS, color);
        /* Fill iris pupil */
        face_lcd_fill_rect(x - 2, y - 1, 4, 2, color);
    }
}

/**
 * Draw an eyebrow with angle and offset.
 */
static void draw_brow(int16_t x, int16_t y, int16_t angle, int16_t y_offset) {
    /* Approximate angled line by adjusting endpoints */
    int16_t brow_y = y + y_offset;
    int16_t angle_offset = (angle * BROW_LENGTH) / 45;  /* ~1 pixel per 45 degrees */

    face_lcd_line(x - BROW_LENGTH/2 - angle_offset, brow_y,
                  x + BROW_LENGTH/2 + angle_offset, brow_y,
                  LCD_WHITE);
}

/**
 * Draw mouth (curved line or bezier approximation).
 */
static void draw_mouth(int16_t x, int16_t y, int16_t width, int16_t curve) {
    /* Simplified mouth: two diagonal lines forming a V or inverted V */
    int16_t mouth_left = x - width / 2;
    int16_t mouth_right = x + width / 2;
    int16_t mouth_bottom = y + (curve > 0 ? 3 : 0);

    if (curve > 0) {
        /* Smile: V shape upturned */
        face_lcd_line(mouth_left, y + 2, x, mouth_bottom, LCD_WHITE);
        face_lcd_line(x, mouth_bottom, mouth_right, y + 2, LCD_WHITE);
    } else if (curve < 0) {
        /* Frown: ^ shape downturned */
        face_lcd_line(mouth_left, y - 2, x, y + 2, LCD_WHITE);
        face_lcd_line(x, y + 2, mouth_right, y - 2, LCD_WHITE);
    } else {
        /* Neutral: straight line */
        face_lcd_line(mouth_left, y, mouth_right, y, LCD_WHITE);
    }
}

/* === Public API Implementation === */

void face_animation_init(void) {
    anim_state.current_emotion = FACE_NEUTRAL;
    anim_state.target_emotion = FACE_NEUTRAL;
    anim_state.frame = 0;
    anim_state.transition_frame = 0;
    anim_state.is_transitioning = false;
    anim_state.blink_timer = BLINK_INTERVAL_MS / 33;
    anim_state.blink_frame = 0;
    anim_state.is_blinking = false;
}

void face_animation_set_emotion(face_emotion_t emotion) {
    if (emotion < 6) {
        anim_state.target_emotion = emotion;
        anim_state.is_transitioning = true;
        anim_state.transition_frame = 0;
    }
}

void face_animation_tick(void) {
    anim_state.frame++;

    /* Handle transition */
    if (anim_state.is_transitioning) {
        anim_state.transition_frame++;
        if (anim_state.transition_frame >= TRANSITION_FRAMES) {
            /* Transition complete */
            anim_state.current_emotion = anim_state.target_emotion;
            anim_state.is_transitioning = false;
        }
    }

    /* Handle idle blink */
    if (!anim_state.is_blinking) {
        anim_state.blink_timer--;
        if (anim_state.blink_timer == 0) {
            anim_state.is_blinking = true;
            anim_state.blink_frame = 0;
            /* Reset timer for next blink */
            anim_state.blink_timer = BLINK_INTERVAL_MS / 33;
        }
    } else {
        /* In blink */
        anim_state.blink_frame++;
        if (anim_state.blink_frame >= BLINK_DURATION_MS / 33) {
            /* Blink complete */
            anim_state.is_blinking = false;
            anim_state.blink_frame = 0;
        }
    }
}

void face_animation_render(void) {
    /* Clear display */
    face_lcd_clear();

    /* Get current emotion parameters (interpolated if transitioning) */
    face_params_t params;
    if (anim_state.is_transitioning) {
        float t = ease_in_out_cubic((float)anim_state.transition_frame /
                                    TRANSITION_FRAMES);
        params = interpolate_params(
            &emotion_params[anim_state.current_emotion],
            &emotion_params[anim_state.target_emotion],
            t);
    } else {
        params = emotion_params[anim_state.current_emotion];
    }

    /* Draw left eye */
    draw_eye(FACE_CENTER_X + params.eye_x, FACE_CENTER_Y + params.eye_y,
             params.eye_open_y, params.eye_close_y, anim_state.is_blinking);

    /* Draw right eye */
    draw_eye(FACE_CENTER_X - params.eye_x, FACE_CENTER_Y + params.eye_y,
             params.eye_open_y, params.eye_close_y, anim_state.is_blinking);

    /* Draw left brow */
    draw_brow(FACE_CENTER_X + params.eye_x, FACE_CENTER_Y + params.brow_y_offset,
              params.brow_angle, 0);

    /* Draw right brow (mirrored) */
    draw_brow(FACE_CENTER_X - params.eye_x, FACE_CENTER_Y + params.brow_y_offset,
              -params.brow_angle, 0);

    /* Draw mouth */
    draw_mouth(FACE_CENTER_X + params.mouth_x, FACE_CENTER_Y + params.mouth_y,
               params.mouth_width, params.mouth_curve);

    /* Push framebuffer to display */
    face_lcd_flush();
}

face_emotion_t face_animation_get_emotion(void) {
    return anim_state.is_transitioning ? anim_state.target_emotion
                                       : anim_state.current_emotion;
}

void face_animation_blink_now(void) {
    anim_state.is_blinking = true;
    anim_state.blink_frame = 0;
    anim_state.blink_timer = BLINK_INTERVAL_MS / 33;
}

bool face_animation_is_idle(void) {
    return !anim_state.is_transitioning && !anim_state.is_blinking;
}
