#include "ClockWidget.hpp"
#include <cmath>
#include <initializer_list>

ClockWidget::ClockWidget(const WidgetConfig& config)
    : Widget(config)
{
}

lv_obj_t* ClockWidget::create(lv_obj_t* parent) {
    obj_ = lv_obj_create(parent);
    if (!obj_) return nullptr;

    lv_obj_remove_style_all(obj_);
    lv_obj_set_pos(obj_, config_.x, config_.y);
    lv_obj_set_size(obj_, 
        config_.width > 0 ? config_.width : 200, 
        config_.height > 0 ? config_.height : 200);
    lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_clear_flag(obj_, LV_OBJ_FLAG_CLICKABLE);

    // Create hands as rectangles
    auto createHand = [&](lv_obj_t*& hand, const char* name) {
        hand = lv_obj_create(obj_);
        lv_obj_remove_style_all(hand);
        lv_obj_add_flag(hand, LV_OBJ_FLAG_IGNORE_LAYOUT);
        lv_obj_clear_flag(hand, LV_OBJ_FLAG_CLICKABLE);
        // Set pivot to bottom center (will be set in applyStyle implicitly by size?)
        // We will set alignment there.
    };

    createHand(hourHand_, "Hour");
    createHand(minuteHand_, "Minute");
    createHand(secondHand_, "Second");

    centerDot_ = lv_obj_create(obj_);
    lv_obj_remove_style_all(centerDot_);
    lv_obj_add_flag(centerDot_, LV_OBJ_FLAG_IGNORE_LAYOUT);
    lv_obj_clear_flag(centerDot_, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_set_size(centerDot_, 12, 12);
    lv_obj_set_style_radius(centerDot_, LV_RADIUS_CIRCLE, 0);
    lv_obj_center(centerDot_);

    // Initial update
    time_t now;
    time(&now);
    struct tm t;
    localtime_r(&now, &t);
    updateHands(t);

    return obj_;
}

void ClockWidget::applyStyle() {
    if (!obj_) return;

    // Face style
    if (config_.bgOpacity > 0) {
        lv_obj_set_style_bg_color(obj_, lv_color_make(
            config_.bgColor.r, config_.bgColor.g, config_.bgColor.b), 0);
        lv_obj_set_style_bg_opa(obj_, config_.bgOpacity, 0);
    }
    
    if (config_.borderRadius > 0) {
        lv_obj_set_style_radius(obj_, config_.borderRadius, 0);
    } else {
        lv_obj_set_style_radius(obj_, LV_RADIUS_CIRCLE, 0);
    }

    if (config_.borderWidth > 0 && config_.borderOpacity > 0) {
        lv_obj_set_style_border_width(obj_, config_.borderWidth, 0);
        lv_obj_set_style_border_color(obj_, lv_color_make(
            config_.borderColor.r, config_.borderColor.g, config_.borderColor.b), 0);
        lv_obj_set_style_border_opa(obj_, config_.borderOpacity, 0);
    } else {
        lv_obj_set_style_border_width(obj_, 2, 0);
        lv_obj_set_style_border_color(obj_, lv_color_make(
            config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
        lv_obj_set_style_border_opa(obj_, LV_OPA_COVER, 0);
    }

    lv_color_t handColor = lv_color_make(
        config_.textColor.r, config_.textColor.g, config_.textColor.b);
    lv_color_t secColor = lv_color_make(200, 50, 50);

    int32_t w = config_.width > 0 ? config_.width : 200;
    int32_t h = config_.height > 0 ? config_.height : 200;
    int32_t radius = (w < h ? w : h) / 2;

    auto configHand = [&](lv_obj_t* hand, int32_t width, int32_t length, lv_color_t color) {
        if (!hand) return;
        lv_obj_set_size(hand, width, length);
        lv_obj_set_style_bg_color(hand, color, 0);
        lv_obj_set_style_bg_opa(hand, LV_OPA_COVER, 0);
        lv_obj_set_style_radius(hand, width / 2, 0);
        
        // Align to center-bottom of the hand to the center of the clock
        // For rotation, we position the hand such that its rotation point is at the clock center.
        // We put the hand's bottom-center at the clock's center.
        // LVGL Rotation pivot is relative to the object's top-left (0,0) by default?
        // No, default pivot is center.
        // We want pivot at (width/2, length).
        // And we want that pivot point to be at clock center (w/2, h/2).
        
        // Position: 
        // x = (ClockW - HandW) / 2
        // y = (ClockH/2) - HandLength
        // This puts the bottom of the hand at the center.
        
        lv_obj_align(hand, LV_ALIGN_CENTER, 0, -length/2);
        // Wait, align center puts the *center* of hand at center of clock.
        // Hand center is at (w/2, length/2).
        // We want hand bottom (w/2, length) to be at clock center.
        // So we shift y by -length/2.
        
        // Pivot:
        // Set pivot to (width/2, length).
        // lv_obj_set_style_transform_pivot_x(hand, width/2, 0);
        // lv_obj_set_style_transform_pivot_y(hand, length, 0); // Bottom
        
        // BUT: lv_obj_align(..., 0, -length/2) moves it up.
        // Let's verify.
        
        lv_obj_set_style_transform_pivot_x(hand, width / 2, 0);
        lv_obj_set_style_transform_pivot_y(hand, length - 2, 0); // Slightly above bottom for overlap
    };

    configHand(hourHand_, 6, radius * 0.6, handColor);
    configHand(minuteHand_, 4, radius * 0.85, handColor);
    configHand(secondHand_, 2, radius * 0.9, secColor);

    if (centerDot_) {
        lv_obj_set_style_bg_color(centerDot_, handColor, 0);
        lv_obj_set_style_bg_opa(centerDot_, LV_OPA_COVER, 0);
    }

    // Force update
    lastSecond_ = -1;
    time_t now;
    time(&now);
    struct tm t;
    localtime_r(&now, &t);
    updateHands(t);
}

void ClockWidget::updateHands(const struct tm& t) {
    if (t.tm_sec == lastSecond_ && t.tm_min == lastMinute_ && t.tm_hour == lastHour_) return;
    
    lastSecond_ = t.tm_sec;
    lastMinute_ = t.tm_min;
    lastHour_ = t.tm_hour;

    // Calculate angles in 0.1 degree units (LVGL 9)
    // Second: 6 deg * 10 = 60 per sec
    int32_t angleSec = t.tm_sec * 60;
    
    // Minute: (6 deg * min + 0.1 deg * sec) * 10 = 60 * min + sec
    int32_t angleMin = t.tm_min * 60 + t.tm_sec;
    
    // Hour: (30 deg * hr + 0.5 deg * min) * 10 = 300 * (hr%12) + 5 * min
    int32_t angleHour = (t.tm_hour % 12) * 300 + t.tm_min * 5;

    if (secondHand_) lv_obj_set_style_transform_rotation(secondHand_, angleSec, 0);
    if (minuteHand_) lv_obj_set_style_transform_rotation(minuteHand_, angleMin, 0);
    if (hourHand_) lv_obj_set_style_transform_rotation(hourHand_, angleHour, 0);
}

void ClockWidget::onKnxTime(const struct tm& value, TextSource source) {
    updateHands(value);
}
