#include "ui/ScreenSaver.h"
#include "data/roads.h"
#include "data/vehicles.h"
#include <cstdlib>

ScreenSaver::ScreenSaver(menu_options_t *options)
    : Widget(options->u8g2), m_options(options), m_animationCounter(0), m_lastSpawnTime(0), m_leftVehicleCount(0),
      m_rightVehicleCount(0)
{
    initVehicles();
}

void ScreenSaver::initVehicles()
{
    m_vehicles.resize(MAX_VEHICLES);

    for (auto &vehicle : m_vehicles)
    {
        vehicle.active = false;
    }
}

void ScreenSaver::Update(const uint64_t dt)
{
    m_animationCounter += dt;
    m_lastSpawnTime += dt;
    m_sceneShiftTimer += dt;

    // Shift entire scene every 30 seconds
    if (m_sceneShiftTimer > 30000)
    {
        m_sceneOffsetX = (random() % 7) - 3; // -3 to +3 pixels
        m_sceneOffsetY = (random() % 7) - 3; // -3 to +3 pixels
        m_sceneShiftTimer = 0;
    }

    // Try to spawn a new vehicle every few seconds
    if (m_lastSpawnTime > VEHICLE_SPAWN_DELAY)
    {
        trySpawnVehicle();
        m_lastSpawnTime = 0;
    }

    // Update vehicle positions
    if (m_animationCounter > 16) // ~60 FPS
    {
        m_animationCounter = 0;

        for (auto &vehicle : m_vehicles)
        {
            if (!vehicle.active)
                continue;

            // Move vehicle
            if (vehicle.direction == Direction::LEFT)
            {
                vehicle.x -= static_cast<int>(vehicle.speed);

                // Remove the vehicle if it goes off-screen
                if (vehicle.x <= -32) // Allow for largest vehicle width
                {
                    vehicle.active = false;
                    m_leftVehicleCount--;
                }
            }
            else // Direction::RIGHT
            {
                vehicle.x += static_cast<int>(vehicle.speed);

                // Remove the vehicle if it goes off-screen
                if (vehicle.x >= (u8g2->width + 32)) // Allow for largest vehicle width
                {
                    vehicle.active = false;
                    m_rightVehicleCount--;
                }
            }
        }
    }
}

bool ScreenSaver::canSpawnInDirection(Direction direction) const
{
    // Minimalen Abstand zwischen 48 und 64 Pixel zufällig wählen
    int requiredDistance =
        MIN_SAME_DIRECTION_DISTANCE + (random() % (MAX_SAME_DIRECTION_DISTANCE - MIN_SAME_DIRECTION_DISTANCE + 1));

    for (const auto &vehicle : m_vehicles)
    {
        if (!vehicle.active || vehicle.direction != direction)
            continue;

        // Abstand zum nächsten Fahrzeug in gleicher Richtung prüfen
        if (direction == Direction::LEFT)
        {
            // Fahrzeuge fahren von rechts nach links
            // Neues Fahrzeug würde bei u8g2->width + 16 starten
            int newVehicleX = u8g2->width + 16;

            // Prüfen ob genug Abstand zum existierenden Fahrzeug
            if (newVehicleX - vehicle.x < requiredDistance)
                return false;
        }
        else // Direction::RIGHT
        {
            // Fahrzeuge fahren von links nach rechts
            // Neues Fahrzeug würde bei -32 starten
            int newVehicleX = -32;

            // Prüfen ob genug Abstand zum existierenden Fahrzeug
            if (vehicle.x - newVehicleX < requiredDistance)
                return false;
        }
    }

    return true;
}

void ScreenSaver::trySpawnVehicle()
{
    // Check if we can spawn a new vehicle
    int activeVehicles = 0;
    int availableSlot = -1;

    for (int i = 0; i < MAX_VEHICLES; i++)
    {
        if (m_vehicles[i].active)
        {
            activeVehicles++;
        }
        else if (availableSlot == -1)
        {
            availableSlot = i;
        }
    }

    // Don't spawn if we're at max capacity or no slot available
    if (activeVehicles >= MAX_VEHICLES || availableSlot == -1)
    {
        return;
    }

    Direction direction = getRandomDirection();

    // Check direction constraints
    if ((direction == Direction::LEFT && m_leftVehicleCount >= MAX_LEFT_VEHICLES) ||
        (direction == Direction::RIGHT && m_rightVehicleCount >= MAX_RIGHT_VEHICLES))
    {
        return;
    }

    if (!canSpawnInDirection(direction))
    {
        return;
    }

    // Create new vehicle
    Vehicle &newVehicle = m_vehicles[availableSlot];
    newVehicle.type = getRandomVehicleType();
    newVehicle.direction = direction;
    newVehicle.speed = MIN_SPEED + (static_cast<float>(random()) / RAND_MAX) * (MAX_SPEED - MIN_SPEED);

    // Set Y position based on a direction (simulate opposing traffic lanes)
    const int halfHeight = u8g2->height / 2;
    if (direction == Direction::RIGHT)
    {
        // Vehicles going LEFT use bottom half of screen
        newVehicle.y = halfHeight + 8 + (random() % (halfHeight - 24));
        m_rightVehicleCount++;
    }
    else // Direction::RIGHT
    {
        // Vehicles going RIGHT use top half of screen
        newVehicle.y = 8 + (random() % (halfHeight - 24));
        m_leftVehicleCount++;
    }

    // Set the starting X position based on the direction
    if (direction == Direction::LEFT)
    {
        // Vehicles going LEFT (from right to left) start from RIGHT side of screen
        newVehicle.x = u8g2->width + 16;
    }
    else // Direction::RIGHT
    {
        // Vehicles going RIGHT (from left to right) start from LEFT side of screen
        newVehicle.x = -32; // Account for the largest vehicle width
    }

    newVehicle.active = true;
}

ScreenSaver::VehicleType ScreenSaver::getRandomVehicleType()
{
    switch (random() % 5)
    {
    case 0:
        return VehicleType::CAR;
    case 1:
        return VehicleType::CONVERTABLE;
    case 2:
        return VehicleType::SUV;
    case 3:
        return VehicleType::LORRY;
    case 4:
        return VehicleType::TRUCK;
    default:
        return VehicleType::CAR;
    }
}

ScreenSaver::Direction ScreenSaver::getRandomDirection()
{
    // Simple 50/50 chance for each direction
    return (random() % 2 == 0) ? Direction::LEFT : Direction::RIGHT;
}

void ScreenSaver::Render()
{
    // Clear screen with a black background
    u8g2_SetDrawColor(u8g2, 0);
    u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
    u8g2_SetDrawColor(u8g2, 1);

    // Calculate offsets
    const int roadOffset = (m_animationCounter / 100) % road_horizontal_width;

    // Draw all active vehicles with a scene offset
    for (const auto &vehicle : m_vehicles)
    {
        if (vehicle.active)
        {
            Vehicle offsetVehicle = vehicle;
            offsetVehicle.x += m_sceneOffsetX;
            offsetVehicle.y += m_sceneOffsetY;
            drawVehicle(offsetVehicle);
        }
    }

    // Draw road with offsets
    const int y = u8g2->height / 2 - road_horizontal_height / 2 + m_sceneOffsetY;
    for (int x = -road_horizontal_width + roadOffset + m_sceneOffsetX; x <= u8g2->width; x += road_horizontal_width)
    {
        drawTransparentBitmap(x, y, road_horizontal_width, road_horizontal_height, road_horizontal_bits);
    }
}

void ScreenSaver::drawVehicle(const Vehicle &vehicle) const
{
    int width, height;

    if (const unsigned char *bitmap = getVehicleBitmap(vehicle.type, vehicle.direction, width, height))
    {
        drawTransparentBitmap(vehicle.x, vehicle.y, width, height, bitmap);
        // u8g2_DrawXBM(u8g2, vehicle.x, vehicle.y, width, height, bitmap);
    }
}

void ScreenSaver::drawTransparentBitmap(const int x, const int y, const int width, const int height,
                                        const unsigned char *bitmap) const
{
    for (int py = 0; py < height; py++)
    {
        for (int px = 0; px < width; px++)
        {
            // Calculate byte and a bit of position in bitmap
            const int byteIndex = (py * ((width + 7) / 8)) + (px / 8);

            // Check if the pixel is set (white)
            if (const int bitIndex = px % 8; bitmap[byteIndex] & (1 << bitIndex))
            {
                // Only draw white pixels, skip black (transparent) pixels
                const int screenX = x + px;

                // Bounds checking
                if (const int screenY = y + py;
                    screenX >= 0 && screenX < u8g2->width && screenY >= 0 && screenY < u8g2->height)
                {
                    u8g2_DrawPixel(u8g2, screenX, screenY);
                }
            }
            // Black pixels are simply not drawn (transparent)
        }
    }
}

const unsigned char *ScreenSaver::getVehicleBitmap(const VehicleType type, const Direction direction, int &width,
                                                   int &height)
{
    switch (type)
    {
    case VehicleType::CAR:
        width = car_width;
        height = car_height;
        return (direction == Direction::LEFT) ? car_left_bits : car_right_bits;

    case VehicleType::CONVERTABLE:
        width = convertable_width;
        height = convertable_height;
        return (direction == Direction::LEFT) ? convertable_left_bits : convertable_right_bits;

    case VehicleType::SUV:
        width = suv_width;
        height = suv_height;
        return (direction == Direction::LEFT) ? suv_left_bits : suv_right_bits;

    case VehicleType::LORRY:
        width = lorry_width;
        height = lorry_height;
        return (direction == Direction::LEFT) ? lorry_left_bits : lorry_right_bits;

    case VehicleType::TRUCK:
        width = truck_width;
        height = truck_height;
        return (direction == Direction::LEFT) ? truck_left_bits : truck_right_bits;

    default:
        width = car_width;
        height = car_height;
        return car_left_bits;
    }
}

void ScreenSaver::OnButtonClicked(ButtonType button)
{
    if (m_options && m_options->popScreen)
    {
        m_options->popScreen();
    }
}