local gearbox = {} -- will be filled with public functions

local settings = require('common.settings')
local players  = require('common.players')
local colors = settings.colors
local fonts = settings.fonts


-- kmh and rpm number text is 17px
-- gear number text is 33px
-- kmh/rpm/FUEL/EST.LAP/fuel number/estimated lap number/ texts are 9px

-- gear green circle diameter is 87px
-- gear green circle padding is 6px
-- gear green circle width is 6px
-- green gots diameter is 10px (also the gray one at the bottom)
-- gray outline below is 2px
-- pillola with gray outline is 248x27 px

local function make_pillola_path(left_center, right_center, radius, segments)
    ui.pathClear()
    ui.pathArcTo(left_center, radius, math.pi/2, math.pi*3/2, segments)
    ui.pathArcTo(right_center, radius, -math.pi/2, math.pi/2, segments)
end

local on_show_animation_start = 0
local is_showing = true
local is_paused = false
function gearbox.on_open()
    if is_paused == false then
        on_show_animation_start = Time
        is_showing = true
    end
    is_paused = false
end

function gearbox.on_close()
    is_paused = ac.getSim().isPaused
    if is_paused == false then
        is_showing = false
    end
end

local previous_fuel_estimate = nil


local mod_element_state_timer = 0
local del_element_state_timer = 0
local rec_element_state_timer = 0

local mod_element_state = false
local del_element_state = false
local rec_element_state = false

local mod_anim_state = 0
local del_anim_state = 0
local rec_anim_state = 0

local del_input = false
local mod_input = false
local rec_input = false

local current_mod_data = ""
local current_del_data = ""
local current_rec_data = ""

local mguh_mode = false
local mguk_del = 0
local mguk_rec = 0

local battery_lap_percentage_old = 0

local p2p_time = -10
local p2p_old_status = 0

Gearbox_PeakRpmDetected = nil

local over_limit_audio = settings.get_looping_audio_asset("CMRT UI gearbox")
local drs_rise_audio = settings.get_looping_audio_asset("CMRT UI DRS rise")
local drs_gain_audio = nil -- filled later
local collision_time = -100
local in_collision = false

function gearbox.init()
    -- check if we have fuel estimate
    -- we have a proper estimate, save it so next time we can get it immediately
    local trackname = ac.getTrackFullID("|||")
    local carname = ac.getCarName(0)
    local access_string = trackname .. " - " .. carname
    local fuel_estimate = ac.storage(access_string .. " :> fuel estimate", nil)
    previous_fuel_estimate = fuel_estimate:get()

    
    local my_car = ac.getCar(0) -- in init it's always gonna be the player
    mguh_mode = my_car.mguhChargingBatteries
    mguk_del = my_car.mgukDelivery
    mguk_rec = my_car.mgukRecovery

    
    if Gearbox_ERSMode == true then
        if my_car.kersPresent then
            Gearbox_MGUKMode = true
            Gearbox_InfoMode = false
            Gearbox_MGUKInfoMode = true
        else
            Gearbox_MGUKMode = false 
            Gearbox_InfoMode = true
            Gearbox_MGUKInfoMode = false
        end
    else
        Gearbox_InfoMode = true
        Gearbox_MGUKInfoMode = false
    end
    
    current_mod_data = "BAT"
    if ac.getMGUKDeliveryName(0) ~= nil then
        current_del_data = string.upper(ac.getMGUKDeliveryName(0))
    end
    current_rec_data = tostring(my_car.mgukRecovery * 10)

    battery_lap_percentage_old = math.clamp(my_car.kersCurrentKJ / my_car.kersMaxKJ, 0, 1)

    local lut_data = ac.DataLUT11.carData(0, "power.lut")
    local min_lut, max_lut = ac.DataLUT11().bounds(lut_data)
    if max_lut.x > 100 then
        -- calculate the rpm for each point, get the peak to know when to shift
        local peak_hp = 0
        local current_lut_index = 0
        while true do
            local current_rpm = lut_data:getPointInput(current_lut_index)
            if math.isnan(current_rpm) then break end

            -- calculate current horse power
            -- values/formulas taken from this worksheet https://assettocorsamods.net/threads/ac-worksheet.806/
            local torque_kp = lut_data:getPointOutput(current_lut_index)
            local torque_ft_lb = torque_kp * 7.2330138512
            local torque_nm = torque_ft_lb * 1.35581794884
            local current_hp = torque_nm * current_rpm * 0.101972 / 725
            
            -- now find PEAK horse power
            if current_hp > peak_hp then
                peak_hp = current_hp
                Gearbox_PeakRpmDetected = current_rpm
            end

            current_lut_index = current_lut_index + 1
        end
    end
end

function gearbox.on_game_close()
    local estimated_fuel = ac.getCar(0).fuelPerLap
    if estimated_fuel > 0 then
        -- we have a proper estimate, save it so next time we can get it immediately
        local trackname = ac.getTrackFullID("|||")
        local carname = ac.getCarName(0)
        local access_string = trackname .. " - " .. carname
        local fuel_estimate = ac.storage(access_string .. " :> fuel estimate", nil)
        fuel_estimate:set(estimated_fuel)
    end
end

local function move_towards(source, dest, rate)
    local val = source
    
    if val <= dest then
        val = val + rate
        if val > dest then val = dest end
    else
        val  = val - rate
        if val < dest then val = dest end
    end
    return val
end


local current_arrow_value = 0
local function draw_arrows(center_up, center_down, arrow_width, current_derivative)
    local target = 0
    if current_derivative > 0 then target = 1 end
    if current_derivative < 0 then target = -1 end
    current_arrow_value = move_towards(current_arrow_value, target, 14 * Dt)
    local arrow_lerp_t = math.abs(current_arrow_value)

    if current_arrow_value > 0 then
        ui.pathLineTo(center_up + vec2(-arrow_width / 2, 0))
        ui.pathLineTo(center_up + vec2(0, -arrow_width)/2)
        ui.pathLineTo(center_up + vec2(arrow_width / 2, 0))
        ui.pathStroke(colors.GREEN:clone() * rgbm(1,1,1,arrow_lerp_t), false, 3 * GearboxScale)
    elseif current_arrow_value < 0 then
        ui.pathLineTo(center_down + vec2(-arrow_width / 2, 0))
        ui.pathLineTo(center_down + vec2(0, arrow_width)/2)
        ui.pathLineTo(center_down + vec2(arrow_width / 2, 0))
        ui.pathStroke(colors.YELLOW_BATTERY_CHARGE:clone() * rgbm(1,1,1,arrow_lerp_t), false, 3 * GearboxScale)
    end
end


local last_battery_value = 0
local current_derivative = 0

local kers_battery_anim_current_time = 0;
local kers_battery_active = false;

local mguk_elements_position_offset = vec2(0,0)
local gearbox_elemnts_position_offset = vec2(0,0)

local p2p_infos = table.new(10, 0)
local drs_start_time = -1
local drs_available_last_frame = false
local drs_activation_change_time = -1 -- starts from 0 every time the drs active changes state
local drs_activated_last_frame = false
local drs_current_width = 0
local drs_top_percentage = 0
local drs_bot_percentage = 0


local function check_changes_in_hybrind()
    local focused, changed = players.get_focused_car()
    local my_car = ac.getCar(focused)
    if changed then
        -- we changed which car we spectate, reset hybrid values so we don't animate incorrectly
        mguh_mode = my_car.mguhChargingBatteries
        mguk_del  = my_car.mgukDelivery
        mguk_rec  = my_car.mgukRecovery
    end

    if  mguh_mode ~= my_car.mguhChargingBatteries then
        mod_input = true
    end

    if  mguk_del ~= my_car.mgukDelivery then
        del_input = true
    end

    if  mguk_rec ~= my_car.mgukRecovery then
        rec_input = true
    end

    if mguh_mode ~= my_car.mguhChargingBatteries or mguk_del ~= my_car.mgukDelivery or mguk_rec ~= my_car.mgukRecovery then
        mguh_mode = my_car.mguhChargingBatteries
        mguk_del = my_car.mgukDelivery
        mguk_rec = my_car.mgukRecovery
        return true
    end

    return false
end

local function get_curve_percentages(has_battery, current_percentage, circle_radius, entire_app_size)
    local angle_start = math.pi / 2
    local angle_end = math.pi * 3 / 2
    local r = circle_radius + drs_current_width / 2

    local initial_length = 35 * GearboxScale
    if not has_battery then initial_length = 18 * GearboxScale end
    local arc_length = (angle_end - angle_start) * r -- by definition of radians
    local horizontal_length = entire_app_size.x / 2 - circle_radius
    local total_length = arc_length + horizontal_length + initial_length

    local percentage_initial    = initial_length    / total_length
    local percentage_arc        = arc_length        / total_length
    local percentage_horizontal = horizontal_length / total_length

    local current_initial = math.clamp(settings.remap(current_percentage, 0, percentage_initial, 0, 1), 0, 1)
    local current_angle = math.clamp(settings.remap(current_percentage, percentage_initial, percentage_initial + percentage_arc, 0, 1), 0, 1)
    local current_horiz = math.clamp(settings.remap(current_percentage, percentage_initial + percentage_arc, 1, 0, 1), 0, 1)
    
    return current_initial, current_angle, current_horiz
end


function gearbox.main()

    local focused, changed = players.get_focused_car()
    local my_car = ac.getCar(focused)
    if my_car == nil then return end -- should never happen
    local rpm_percentage_limiter = my_car.rpm / my_car.rpmLimiter -- fallback, if we don't have the lut table use the rpm limiter
    local has_battery = my_car.kersPresent

    local top_bar_height = 22
    local draw_top_left = vec2(0, top_bar_height)
    
    local region_width = 260 * GearboxScale
    local region_radius = 10 * GearboxScale
    local bg_size = vec2(365, 100) * GearboxScale
    if has_battery or my_car.p2pStatus > 0 then bg_size = vec2(435, 100) * GearboxScale end
    local region_border = 3 * GearboxScale
    local circle_radius = bg_size.y / 2
    
    local bg_top = draw_top_left + vec2(bg_size.x / 2, 0)
    local bg_center = bg_top + vec2(0, circle_radius)
    
    -- setup for later since we use the colors
    local drs_max_border_width = 8 * GearboxScale
    local drs_min_border_width = 5 * GearboxScale
    local max_dots = 14
    local window_top = bg_center - vec2(0, bg_size.y) / 2

    local dots_offset = vec2(14, 0) * GearboxScale
    local dot_radius = 5 * GearboxScale

    local dots_start = window_top + vec2(-68, 20) * GearboxScale

    if has_battery or my_car.p2pStatus > 0 then
        local dot_line_size = (dots_offset.x * 13.0 + dot_radius * 2)
        dots_start = window_top - vec2(dot_line_size / 2, -20 * GearboxScale) + vec2(dot_radius,0)
    end

    if my_car.drsPresent then dots_start = dots_start + vec2(0, drs_max_border_width) end
    local green_to_yellow = 12
    local light_higher_bound = my_car.rpmLimiter
    local light_dot_count = settings.remap(my_car.rpm, 0, my_car.rpmLimiter, 0, max_dots) -- 0 dots = 0 rpm, full dots = rpm limiter
    if Gearbox_DotsWindow then
        light_higher_bound = math.clamp(Gearbox_WantedRpm, 300, my_car.rpmLimiter - 250)
        light_dot_count = settings.remap(my_car.rpm, Gearbox_WantedRpm - 1200, light_higher_bound, 0, max_dots) -- 0 dots = limiter - 1500 rpm, full dots = rpm limiter
    end
    
    if Gearbox_PeakRpmDetected ~= nil and Gearbox_ShowPeakShift then
        light_higher_bound = math.clamp(Gearbox_PeakRpmDetected, 300, my_car.rpmLimiter - 250)
        light_dot_count = settings.remap(my_car.rpm, Gearbox_PeakRpmDetected - 1200, light_higher_bound, 0, max_dots) -- 0 dots = limiter - 1500 rpm, full dots = rpm limiter
    end

    local dot_color = colors.LIGHT_GREEN
    if my_car.rpm >= light_higher_bound * green_to_yellow / max_dots then
        dot_color = colors.YELLOW
    end
    if my_car.rpm >= light_higher_bound then
        dot_color = colors.RED
    end

    local entire_app_size = bg_size + vec2(0, region_border + region_radius)
    if my_car.drsPresent then
        draw_top_left = draw_top_left + vec2(drs_max_border_width, drs_max_border_width)
        entire_app_size = entire_app_size + vec2(drs_max_border_width * 2, drs_max_border_width)
    end
    local entire_app_center = draw_top_left + entire_app_size / 2
    players.play_intro_anim_setup(entire_app_center, entire_app_size, on_show_animation_start, is_showing)
    
    --
    -- background
    --
    local segments = 30
    local left_circle_center = draw_top_left + vec2(circle_radius, circle_radius)
    local right_circle_center = left_circle_center + vec2(bg_size.x - circle_radius * 2, 0)
    
    -- we change background color when it's time to switch
    local new_range = math.clamp(settings.remap(my_car.rpm, light_higher_bound - 1200, light_higher_bound, 0, 1), 0, 1)
    local bg_color_norm = colors.BG
    local bg_color_red = colors.RED:clone()
    bg_color_red.mult = bg_color_norm.mult
    local dots_bg_color = settings.color_lerp(colors.GRAY, rgbm(27 / 255, 27 / 255, 27 / 255, 0.55), new_range)

    over_limit_audio.volume = 5 * Audio_GearboxBlinkingVolume * Audio_MasterVolume
    local bg_color = settings.color_lerp(bg_color_norm, bg_color_red, new_range)
    if my_car.rpm >= light_higher_bound then
        local blah = 10 * Time
        if blah % 2 >= 1 then bg_color = bg_color_norm
        else bg_color = bg_color_red end
        
        if Audio_MasterEnabled and Audio_GearboxBlinkingEnabled then
            if my_car.gear < my_car.gearCount then -- don't play sound the final gear, it's useless!
                local audio_time = over_limit_audio:getTimelinePosition()
                if audio_time <= 0 then over_limit_audio:start() end
            else
                over_limit_audio:stop()
            end
        end
    else
        over_limit_audio:stop() -- even when audio is disabled so we don't have it infinitely playing by mystake
    end
    
    if my_car.drsPresent then
        if my_car.drsAvailable and not drs_available_last_frame then drs_start_time = Time end
        if my_car.drsActive ~= drs_activated_last_frame then drs_activation_change_time = Time end

        local drop_to_peak_time = 0.14
        local elapsed = Time - drs_start_time
        local total_time = drop_to_peak_time * 4
        if elapsed <= total_time then -- so we don't take up too much in useless stuff
            local current_t = settings.interpolate_table({[0]=0, 0.6, 0}, elapsed, drop_to_peak_time)
            local real_t = settings.ease_in_out_sine(current_t)
            bg_color = settings.color_lerp(bg_color_norm, colors.LIGHT_GREEN, real_t)
        end

        drs_available_last_frame = my_car.drsAvailable
        drs_activated_last_frame = my_car.drsActive
    end

    local end_width = dots_start + dots_offset * (max_dots - 1) + 22 * GearboxScale
    local bottom_center = bg_center + vec2(0, circle_radius)
    local right_region_center = vec2(end_width.x + 2 * GearboxScale, bottom_center.y)
    local left_region_center = right_region_center - vec2(region_width - region_radius * 2, 0)
    if has_battery or my_car.p2pStatus > 0 then
        right_region_center = bottom_center + vec2(region_width / 2 - region_radius)
        left_region_center  = bottom_center - vec2(region_width / 2 - region_radius)
    end
    if my_car.drsPresent then
        left_region_center = left_region_center + vec2(drs_max_border_width, drs_max_border_width)
        right_region_center = right_region_center + vec2(drs_max_border_width, drs_max_border_width)
        bg_center = bg_center + vec2(drs_max_border_width, drs_max_border_width)
    end

    local warning_liter_level = my_car.maxFuel * 0.1
    local mega_warning_liter_level = my_car.maxFuel * 0.05
    local fuel_indicator_color = colors.LIGHT_GRAY
    local area_border_color = rgbm(90 / 255, 90 / 255, 90 / 255, 0.75)
    local draw_fuel_bloom = false
    if my_car.fuel < warning_liter_level then
        fuel_indicator_color = colors.WARNING_RED
        area_border_color = colors.WARNING_RED
        draw_fuel_bloom = true
    end
    if my_car.fuel < mega_warning_liter_level then
        if (3 * Time) % 2 <= 1 then
            fuel_indicator_color = colors.WARNING_RED
            area_border_color = colors.WARNING_RED
            draw_fuel_bloom = true
        else
            fuel_indicator_color = colors.LIGHT_GRAY
            area_border_color = rgbm(65 / 255, 65 / 255, 65 / 255, 0.8)
            draw_fuel_bloom = false
        end
    end

    local drs_zones = players.get_drs_zones()
    local zones_count = table.nkeys(drs_zones)
    local has_zones = zones_count > 0
    if my_car.drsPresent then
        local car_spline = my_car.splinePosition
        local sim_info = ac.getSim()
        local drs_window = 100 / sim_info.trackLengthM -- 100 meters away
        local in_window = false
        for i=0, zones_count-1 do
            local drs_data = drs_zones[i]
            if car_spline >= drs_data.zstart - drs_window and car_spline <= drs_data.zstart then
                drs_top_percentage = math.clamp(settings.remap(car_spline, drs_data.zstart - drs_window, drs_data.zstart, 0, 1), 0, 1)
                drs_bot_percentage = 0
                in_window = true
                break
            end
        end
        if my_car.drsAvailable then
            drs_top_percentage = 1
            drs_bot_percentage = 0
        elseif not in_window then
            drs_bot_percentage = move_towards(drs_bot_percentage, 1, 2 * Dt)
            drs_top_percentage = 1
        end
        if my_car.isInPitlane or not has_zones then
            drs_top_percentage = 0
            drs_bot_percentage = 0
        end

        if in_window and Audio_MasterEnabled and Audio_DRSEnabled then
            local volume_down_t = 1
            if my_car.speedKmh < 80 and not in_collision then
                in_collision = true
                collision_time = Time
            end
            if in_collision then
                volume_down_t = settings.anim_lerp(Time - collision_time, false, 0.5)
                if my_car.speedKmh > 100 then in_collision = false end
            end
            local drs_remap = math.clamp(settings.remap(drs_top_percentage, 0.66, 1, 0, 1), 0, 1)
            drs_rise_audio.volume = 8 * Audio_DRSVolume * Audio_MasterVolume * drs_remap * volume_down_t
            if Audio_DRSEnabled then
                local audio_time = drs_rise_audio:getTimelinePosition()
                if audio_time <= 0 then drs_rise_audio:start() end
            end
        else
            drs_rise_audio:stop()
        end

        if Time - drs_start_time < 0.01 and Audio_MasterEnabled and Audio_DRSEnabled then
            if drs_gain_audio ~= nil then drs_gain_audio:dispose() end
            drs_gain_audio = settings.get_single_audio_asset("CMRT UI DRS gained", 5.6 * Audio_DRSVolume * Audio_MasterVolume)
            drs_gain_audio:start()
        end

        -- when we gain drs the outer bar will get bigger than smaller
        local bar_t = settings.interpolate_table({[0]=0, 1, 0}, Time - drs_start_time, 0.1)
        local smooth_t = settings.ease_in_out_sine(bar_t)
        drs_current_width = settings.remap(smooth_t, 0, 1, drs_min_border_width, drs_max_border_width)
        
        -- NOTE(cogno): since we can't draw the lower horizontal portion, because it will be
        -- visible behind the panel with fuel and stuff, we need to divide the path into 2 portions
        -- and draw each one of them

        if has_battery or my_car.p2pStatus > 0 then
            local angle_start = math.pi / 2
            local angle_end = math.pi * 3 / 2
            local r = circle_radius + drs_current_width / 2
            local horizontal_length = entire_app_size.x / 2 - circle_radius - drs_current_width
            local initial_length = 35 * GearboxScale

            -- pt = percentage top, pb = percentage bottom, ct = current top, cb = current bottom
            local pt_initial, pt_angle, pt_horiz = get_curve_percentages(has_battery or my_car.p2pStatus > 0, drs_top_percentage, circle_radius, entire_app_size)
            local pb_initial, pb_angle, pb_horiz = get_curve_percentages(has_battery or my_car.p2pStatus > 0, drs_bot_percentage, circle_radius, entire_app_size)
            local ct_initial = settings.remap(pt_initial, 0, 1, 0, initial_length)
            local ct_angle   = settings.remap(pt_angle,   0, 1, 0, angle_end - angle_start)
            local ct_horiz   = settings.remap(pt_horiz,   0, 1, 0, horizontal_length)
            local cb_initial = settings.remap(pb_initial, 0, 1, 0, initial_length)
            local cb_angle   = settings.remap(pb_angle,   0, 1, 0, angle_end - angle_start)
            local cb_horiz   = settings.remap(pb_horiz,   0, 1, 0, horizontal_length)

            -- left path
            ui.pathClear()
            local initial_point_left = draw_top_left + vec2(circle_radius + initial_length, circle_radius * 2 + drs_current_width / 2)
            if pt_initial > 0 and pb_initial < 1 then
                ui.pathLineTo(initial_point_left - vec2(cb_initial, 0))
                ui.pathLineTo(initial_point_left - vec2(ct_initial, 0))
            end
            if pt_angle > 0 and pb_angle < 1 then
                ui.pathArcTo(left_circle_center, r, angle_start + cb_angle, angle_start + ct_angle, 20)
            end
            if pt_horiz > 0 and pb_horiz < 1 then
                ui.pathLineTo(draw_top_left + vec2(circle_radius + cb_horiz + drs_max_border_width, -drs_current_width / 2))
                ui.pathLineTo(draw_top_left + vec2(circle_radius + ct_horiz + drs_max_border_width, -drs_current_width / 2))
            end
            ui.pathStroke(colors.LIGHT_GREEN, false, drs_current_width)
            
            -- right path
            -- with battery the path is simmetric (it's not when battery is missing)
            ui.pathClear()
            local initial_point_right = initial_point_left + vec2(region_width + region_border * 2 - 1 * GearboxScale)
            if pt_initial > 0 and pb_initial < 1 then
                ui.pathLineTo(initial_point_right + vec2(cb_initial, 0))
                ui.pathLineTo(initial_point_right + vec2(ct_initial, 0))
            end
            if pt_angle > 0 and pb_angle < 1 then
                ui.pathArcTo(right_circle_center, r, angle_start - cb_angle, angle_start - ct_angle, 20)
            end
            if pt_horiz > 0 and pb_horiz < 1 then
                ui.pathLineTo(draw_top_left + vec2(entire_app_size.x - circle_radius - cb_horiz - drs_max_border_width * 2, -drs_current_width / 2))
                ui.pathLineTo(draw_top_left + vec2(entire_app_size.x - circle_radius - ct_horiz - drs_max_border_width * 2, -drs_current_width / 2))
            end
            ui.pathStroke(colors.LIGHT_GREEN, false, drs_current_width)
        else
            local left_angle_start = math.pi / 2
            local angle_end = math.pi * 3 / 2
            local r = circle_radius + drs_current_width / 2
            local horizontal_length = entire_app_size.x / 2 - circle_radius
            local initial_length = 18 * GearboxScale
            
            -- pt = percentage top, pb = percentage bottom, ct = current top, cb = current bottom
            local pt_initial, pt_angle, pt_horiz = get_curve_percentages(has_battery, drs_top_percentage, circle_radius, entire_app_size)
            local pb_initial, pb_angle, pb_horiz = get_curve_percentages(has_battery, drs_bot_percentage, circle_radius, entire_app_size)
            local ct_initial = settings.remap(pt_initial, 0, 1, 0, initial_length)
            local ct_angle   = settings.remap(pt_angle,   0, 1, 0, angle_end - left_angle_start)
            local ct_horiz   = settings.remap(pt_horiz,   0, 1, 0, horizontal_length)
            local cb_initial = settings.remap(pb_initial, 0, 1, 0, initial_length)
            local cb_angle   = settings.remap(pb_angle,   0, 1, 0, angle_end - left_angle_start)
            local cb_horiz   = settings.remap(pb_horiz,   0, 1, 0, horizontal_length)

            -- left path
            ui.pathClear()
            local initial_point = draw_top_left + vec2(circle_radius + initial_length, circle_radius * 2 + drs_current_width / 2)
            if pt_initial > 0 and pb_initial < 1 then
                ui.pathLineTo(initial_point - vec2(cb_initial, 0))
                ui.pathLineTo(initial_point - vec2(ct_initial, 0))
            end
            if pt_angle > 0 and pb_angle < 1 then
                ui.pathArcTo(left_circle_center, r, left_angle_start + cb_angle, left_angle_start + ct_angle, 20)
            end
            if pt_horiz > 0 and pb_horiz < 1 then
                ui.pathLineTo(draw_top_left + vec2(circle_radius + cb_horiz + drs_max_border_width, -drs_current_width / 2))
                ui.pathLineTo(draw_top_left + vec2(circle_radius + ct_horiz + drs_max_border_width, -drs_current_width / 2))
            end
            ui.pathStroke(colors.LIGHT_GREEN, false, drs_current_width)
            
            -- right path
            -- NOTE(cogno): when no battery the right side of the drs lines start from a particular point of the arc, since
            -- we want the effect to be simmetry we skip the initial length and a portion of the arc
            ui.pathClear()
            if pt_angle > 0 and pb_angle < 1 then
                cb_angle = math.clamp(cb_angle, 0.33, math.pi)
                ui.pathArcTo(right_circle_center, r, left_angle_start - cb_angle, left_angle_start - ct_angle, 20)
            end
            if pt_horiz > 0 and pb_horiz < 1 then
                ui.pathLineTo(draw_top_left + vec2(entire_app_size.x - circle_radius - cb_horiz - drs_max_border_width * 2, -drs_current_width / 2))
                ui.pathLineTo(draw_top_left + vec2(entire_app_size.x - circle_radius - ct_horiz - drs_max_border_width * 2, -drs_current_width / 2))
            end
            ui.pathStroke(colors.LIGHT_GREEN, false, drs_current_width)
        end
    end


    if has_battery or my_car.p2pStatus > 0 then
        -- TEMP(cogno): replace with proper texture
        --make_pillola_path(left_circle_center, right_circle_center, circle_radius, segments)
        --ui.pathFillConvex(bg_color)

        ui.drawImage(
            settings.get_asset("GEARBOX_ERS"),
            bg_center - bg_size / 2,
            bg_center + bg_size / 2,
            bg_color
        )
    else
        ui.drawImage(
            settings.get_asset("GEARBOX"),
            bg_center - bg_size / 2,
            bg_center + bg_size / 2,
            bg_color
        )
    end
    ui.pushDWriteFont(fonts.opti_edgar)

    if my_car.drsPresent and my_car.drsAvailable and not my_car.isInPitlane and has_zones then
        local elapsed = Time - drs_activation_change_time
        local drs_active_lerp = math.clamp(settings.remap(elapsed, 0, 0.3, 0, 1), 0, 1)
        local curve_val = 0
        if my_car.drsActive then
            curve_val = settings.ease_out_back(drs_active_lerp) -- we want to give a good feedback only when we turn it on
        else
            curve_val = 1 - settings.ease_out_cubic(drs_active_lerp)
        end
        local drs_light_alpha = curve_val * 0.3;
        
        local drs_gradient = ""
        if has_battery or my_car.p2pStatus > 0 then drs_gradient = settings.get_asset("drs_gradient_ext")
        else                drs_gradient = settings.get_asset("drs_gradient")
        end
        ui.drawImage(
            drs_gradient,
            bg_center - bg_size / 2,
            bg_center + bg_size / 2,
            rgbm(colors.LIGHT_GREEN.r, colors.LIGHT_GREEN.g, colors.LIGHT_GREEN.b, drs_light_alpha)
        )
    end

    --
    -- right battery region
    --
    local green_circle_padding = 6 * GearboxScale
    local green_circle_width = 6 * GearboxScale
    local green_circle_radius = bg_size.y / 2 - green_circle_padding - green_circle_width / 2
    local battery_lap_percentage = math.clamp(my_car.kersCurrentKJ / my_car.kersMaxKJ, 0, 1)
    battery_lap_percentage_old = settings.damp(battery_lap_percentage_old, battery_lap_percentage, 50)

    local battery_total_percentage = my_car.kersCharge
    local kers_derivative = (battery_total_percentage - last_battery_value) * 60 -- times 1 / dt => 1 / (1 / 60fps) => 60 fps
    current_derivative = move_towards(current_derivative, kers_derivative, Dt * 0.15)
    
    local empty_angle = math.pi * 0.06
    if has_battery or my_car.p2pStatus > 0 then
        if my_car.p2pStatus > 0 then
            if p2p_infos[focused] == nil then
                p2p_infos[focused] = ac.INIConfig.carData(focused, "engine.ini")
            end -- so we don't check it every frame which is SLOW

            local disc_conf = p2p_infos[focused]
            local p2p_duration = 0
            local p2p_cooldown = 0
            if disc_conf ~= nil then
                local sections = disc_conf['sections']
                local p2p_sec = sections['PUSH_TO_PASS']
                if p2p_sec ~= nil then
                    p2p_duration = p2p_sec['TIME_SECONDS'][1]
                    p2p_cooldown = p2p_sec['COOLDOWN_SECONDS'][1]
                end
            end


            local bat_angle_position = -math.pi
            local bat_start_angle = bat_angle_position + empty_angle / 2
            local bat_end_angle = bat_angle_position - empty_angle / 2 + math.pi * 2
            
            local p2p_text_color = colors.WHITE
            local battery_bar_color = colors.BLUE_BATTERY

            -- bars bg
            local bat_circle_radius = green_circle_radius
            local bat_circle_width = green_circle_width

            if p2p_old_status ~= my_car.p2pStatus then
                p2p_old_status = my_car.p2pStatus
                p2p_time = Time
            end

            -- when p2p status 2 is available, 3 is using, 1 is charging, 0 is never used
            local p2p_lerp = 1
            local lerp_t = 0
            local bloom_lerp = 0
            local bloom_color = nil
            if my_car.p2pStatus == 3 then -- using
                p2p_lerp = 1 - math.clamp((Time - p2p_time) / p2p_duration, 0, 1)
                battery_bar_color = colors.GREEN
                lerp_t = math.clamp((Time - p2p_time) / 0.2, 0, 1)
                bloom_lerp = settings.interpolate_table({[0]=0, 1, 0}, Time - p2p_time, 0.15)
                bloom_color = colors.GREEN
            end
            
            if my_car.p2pStatus == 2 then -- available
                bloom_lerp = settings.interpolate_table({[0]=0, 1, 0}, Time - p2p_time, 0.15)
                bloom_color = colors.BLUE_BATTERY
            end

            if my_car.p2pStatus == 3 and (p2p_duration == 0 or p2p_cooldown == 0) then
                p2p_text_color = colors.GREEN -- green text when no data is present
            end

            if my_car.p2pStatus == 1 and my_car.p2pActivations ~= 0 then -- charging
                p2p_text_color = colors.LIGHT_GRAY
                p2p_lerp = math.clamp((Time - p2p_time) / p2p_cooldown, 0, 1)
                lerp_t = 1 - math.clamp((Time - p2p_time) / 0.2, 0, 1)
                battery_bar_color = colors.YELLOW_BATTERY_CHARGE
            end

            if my_car.p2pActivations == 0 and my_car.p2pStatus == 1 and p2p_lerp >= 1    then
                battery_bar_color = colors.GRAY
                p2p_text_color = colors.GRAY
            end

            if p2p_duration == 0 or p2p_cooldown == 0 then
                lerp_t = 0
                battery_bar_color = colors.GRAY
            end

            local kers_angle = settings.remap(p2p_lerp, 0, 1, bat_start_angle, bat_end_angle)

            local new_width = bat_circle_width * 1.5
            local extended_bat_circle_radius = (bat_circle_radius - (new_width - bat_circle_width) / 2)

            local extended_bat_circle_width = new_width
            lerp_t = settings.ease_out_cubic(lerp_t)
            local final_bat_circle_radius = math.lerp(bat_circle_radius, extended_bat_circle_radius, lerp_t)
            local final_bat_circle_width  = math.lerp(bat_circle_width, extended_bat_circle_width, lerp_t)

            if bloom_color ~= nil then
                ui.drawImage(
                    settings.get_asset("bloom2"),
                    right_circle_center - vec2(60, 60) * GearboxScale,
                    right_circle_center + vec2(60, 60) * GearboxScale,
                    rgbm(bloom_color.r, bloom_color.g, bloom_color.b, bloom_lerp)
                )
            end

            ui.pathArcTo(right_circle_center, final_bat_circle_radius, bat_start_angle, bat_end_angle, 60)
            ui.pathStroke(dots_bg_color, false, final_bat_circle_width)

            ui.pathArcTo(right_circle_center, final_bat_circle_radius, bat_start_angle, kers_angle, 60)
            ui.pathStroke(battery_bar_color, false, final_bat_circle_width)

            -- p2p activations text in the middle of the circle
            local p2p_fontsize = settings.fontsize(14 * GearboxScale)
            ui.pushDWriteFont(fonts.opti_edgar)
            local p2p_string = string.format("%d", my_car.p2pActivations)
            local p2p_percentage_size = ui.measureDWriteText(p2p_string, p2p_fontsize)
            local p2p_textpos = right_circle_center - p2p_percentage_size / 2
            -- battery_total_percentage is both kers and p2p
            if battery_total_percentage == 1 then p2p_textpos.x = p2p_textpos.x - 2 * GearboxScale end
            p2p_textpos.x = p2p_textpos.x - GearboxScale -- adjust font positioning

            ui.dwriteDrawText(p2p_string, p2p_fontsize, p2p_textpos, p2p_text_color)
            ui.popDWriteFont()
        else
            local bat_angle_position = -math.pi
            local bat_start_angle = bat_angle_position + empty_angle / 2
            local bat_end_angle = bat_angle_position - empty_angle / 2 + math.pi * 2
            local kers_angle = settings.remap(1 - battery_lap_percentage_old, 0, 1, bat_start_angle, bat_end_angle)
            local battery_bar_color = colors.BLUE_BATTERY
            if current_derivative < 0 then battery_bar_color = colors.YELLOW_BATTERY_CHARGE end
        
            -- bars bg
            local bat_circle_radius = green_circle_radius
            local bat_circle_width = green_circle_width
    
            if my_car.kersButtonPressed then
                if kers_battery_active ~= true then
                    kers_battery_active = not kers_battery_active
                    kers_battery_anim_current_time = Time
                end
            end
    
            if my_car.kersButtonPressed == true then
                if kers_battery_active then
                    kers_battery_active = not kers_battery_active
                    kers_battery_anim_current_time = Time
                end
            end
    
            local lerp_t = settings.anim_lerp(Time - kers_battery_anim_current_time, kers_battery_active, 0.2)
            local new_width = bat_circle_width * 1.5
            local extended_bat_circle_radius = (bat_circle_radius - (new_width - bat_circle_width) / 2)
    
            local extended_bat_circle_width = new_width
            local final_bat_ricle_radius = math.lerp(bat_circle_radius, extended_bat_circle_radius, lerp_t)
            local final_bat_ricle_width = math.lerp(bat_circle_width, extended_bat_circle_width, lerp_t)
    
            ui.pathArcTo(right_circle_center, final_bat_ricle_radius, bat_start_angle, bat_end_angle, 60)
            ui.pathStroke(dots_bg_color, false, final_bat_ricle_width)
            
            ui.pathArcTo(right_circle_center, final_bat_ricle_radius, bat_start_angle, kers_angle, 60)
            ui.pathStroke(battery_bar_color, false, final_bat_ricle_width)
        
            -- battery percentage text in the middle of the circle
            local bat_fontsize = settings.fontsize(12 * GearboxScale)
            ui.pushDWriteFont(fonts.opti_edgar)
            local bat_string = string.format("%d", battery_total_percentage * 100)
            local bat_percentage_size = ui.measureDWriteText(bat_string, bat_fontsize)
            local bat_textpos = right_circle_center - bat_percentage_size / 2
            if battery_total_percentage == 1 then bat_textpos.x = bat_textpos.x - 2 * GearboxScale end
            local bat_text_color = colors.WHITE
            if my_car.kersButtonPressed then bat_text_color = colors.YELLOW_BATTERY_CHARGE end
             
            if battery_total_percentage * 100 < 20 then
                bat_text_color = colors.WARNING_RED
            end
    
            ui.dwriteDrawText(bat_string, bat_fontsize, bat_textpos, bat_text_color)
            ui.popDWriteFont()
        end
    end

    local arrow_width = 15 * GearboxScale
    local arrow_dist = 16 * GearboxScale
    local arrow_up_center = right_circle_center + vec2(0, -arrow_dist)
    local arrow_down_center = right_circle_center + vec2(0,  arrow_dist)
    draw_arrows(arrow_up_center, arrow_down_center, arrow_width, current_derivative)
    
    local kmh_fontsize = settings.fontsize(16 * GearboxScale)

    if has_battery or my_car.p2pStatus > 0 then
        ui.drawImageQuad(
            settings.get_asset("GEARBOX_ERS_dot"),
            bg_center + vec2(-bg_size.x, -bg_size.y) / 2,
            bg_center + vec2(bg_size.x, -bg_size.y) / 2,
            bg_center + vec2(bg_size.x, bg_size.y) / 2,
            bg_center + vec2(-bg_size.x, bg_size.y) / 2
        )
    else
        ui.drawImageQuad(
            settings.get_asset("rpm_gauge_gradient"),
            bg_center + vec2(-bg_size.x, -bg_size.y) / 2,
            bg_center + vec2(bg_size.x, -bg_size.y) / 2,
            bg_center + vec2(bg_size.x, bg_size.y) / 2,
            bg_center + vec2(-bg_size.x, bg_size.y) / 2
        )
    end
    
    local car_gear = my_car.gear
    local gear_string = string.format("%d", car_gear)
    if car_gear == 0 then gear_string = 'N' end
    if car_gear == -1 then gear_string = 'R' end
    
    local speed = my_car.speedKmh
    if Gearbox_ShowMph then speed = speed * 0.621371 end
    local kmh_string = string.format("%.0f", speed)
    local rpm_string = string.format("%.0f", my_car.rpm)

    --
    -- green circle
    --
    local top_angle_gear = 0 + empty_angle / 2
    local end_angle_gear = math.lerp(0, top_angle_gear + math.pi * 2 - empty_angle / 2, math.clamp(rpm_percentage_limiter, 0, 1))
    ui.pathClear()
    ui.pathArcTo(left_circle_center, green_circle_radius, top_angle_gear, top_angle_gear + math.pi * 2 - empty_angle, 60)
    ui.pathStroke(dots_bg_color, false, green_circle_width)
    ui.pathClear()
    ui.pathArcTo(left_circle_center, green_circle_radius, top_angle_gear, end_angle_gear, 60)
    ui.pathStroke(dot_color, false, green_circle_width)
    
    --
    -- gear kmh and rpm texts
    --
    local gear_fontsize = settings.fontsize(30 * GearboxScale)
    local text_fontsize = settings.fontsize(10 * GearboxScale)
    local gear_textsize = ui.measureDWriteText(gear_string, gear_fontsize)
    ui.dwriteDrawText(gear_string, gear_fontsize, left_circle_center - gear_textsize / 2)
    
    --
    -- dots
    --
    local color_of_dots = dot_color
    local bloom_texture = settings.get_asset("bloom")
    for i = 0, max_dots - 1 do
        local dot_pos = dots_start + dots_offset * i
        local t = math.clamp(i - light_dot_count, 0, 1)
        t = 1 - math.pow(t, 4)
        color_of_dots = nil
        
        -- we don't always need to go through the lerp, it's faster to avoid it
        if t == 0 then color_of_dots = dots_bg_color
        elseif t == 1 then color_of_dots = dot_color
        else color_of_dots = settings.color_lerp(dots_bg_color, dot_color, t)
        end
        if i == 0 and my_car.drsPresent and my_car.drsAvailable and not my_car.isInPitlane and has_zones then
            color_of_dots = colors.BLUE_DRS
            t = 1
        end
        if i == 1 and my_car.drsPresent and my_car.drsAvailable and not my_car.isInPitlane and has_zones then
            t = math.clamp(settings.remap(Time - drs_activation_change_time, 0, 0.1, 0, 1), 0, 1)
            if not my_car.drsActive then t = 1 - t end -- we want to turn it OFF
            color_of_dots = settings.color_lerp(dots_bg_color, colors.BLUE_DRS, t)
        end
        ui.drawCircleFilled(dot_pos, dot_radius, color_of_dots)
        ui.drawImage(
            bloom_texture,
            dot_pos + -4 * dot_radius,
            dot_pos +  4 * dot_radius,
            rgbm(color_of_dots.r, color_of_dots.g, color_of_dots.b, t)
        )
    end


    --
    -- START HYBRID PART AND GEARBOX DATA
    --

    -- API(cogno): why do we have them global if we just set them to false every frame?
    mod_input = false
    rec_input = false
    del_input = false


    local rec_string = string.format("%.0f%%", current_rec_data)

    if check_changes_in_hybrind() then
        --RESET AND START THE ANIMATION CHANGE
        Gearbox_MGUKModeStartTime = Time
    end

    --animation here
    -- if enough time has passed automatically switch from ers to normal mode (if we can)
    local hybrid_mode_switch_timer = 3
    if Time - Gearbox_MGUKModeStartTime > hybrid_mode_switch_timer and Gearbox_MGUKMode == false then
        if Gearbox_InfoMode == false then
            Gearbox_InfoOpacityTime = Time
            Gearbox_MGUKInfoOpacityTime = Time
            Gearbox_InfoMode = true
            Gearbox_MGUKInfoMode = false
            Gearbox_ElementsOffsetTime = Time
        end
    else
        if Gearbox_InfoMode == true then
            Gearbox_InfoOpacityTime = Time
            Gearbox_MGUKInfoOpacityTime = Time
            Gearbox_InfoMode = false
            Gearbox_MGUKInfoMode = true
            Gearbox_ElementsOffsetTime = Time
        end
    end

    local gearbox_elemnts_lerp_t = settings.anim_lerp(Time - Gearbox_InfoOpacityTime, Gearbox_InfoMode, 0.3)
    local mguk_elemnts_lerp_t = settings.anim_lerp(Time - Gearbox_MGUKInfoOpacityTime, Gearbox_MGUKInfoMode, 0.3)

    local gearbox_elements_offset_lerp_t = settings.ease_cubic_remap(Time - Gearbox_ElementsOffsetTime, 0.3)

    local kmh_x_offset = 60 * GearboxScale
    local rpm_x_offset = 150 * GearboxScale
    local text_vertical_offset = 3 * GearboxScale
    local kmh_number_pos = left_circle_center + vec2(kmh_x_offset, -text_vertical_offset)
    local rpm_number_pos = left_circle_center + vec2(rpm_x_offset, -text_vertical_offset)

    local mod_exist_offset = 0
    if my_car.hasCockpitMGUHMode == false then
        mod_exist_offset = 60
    end

    local mode_x_offset = 60 * GearboxScale
    local del_x_offset = (124 - mod_exist_offset) * GearboxScale
    local rec_x_offset = (234 - mod_exist_offset) * GearboxScale
    local mode_number_pos = left_circle_center + vec2(mode_x_offset, -text_vertical_offset)
    local del_number_pos = left_circle_center + vec2(del_x_offset, -text_vertical_offset)
    local rec_number_pos = left_circle_center + vec2(rec_x_offset, -text_vertical_offset)

    if Time - Gearbox_MGUKModeStartTime > hybrid_mode_switch_timer and not Gearbox_MGUKMode then
        gearbox_elemnts_position_offset = math.lerp(vec2(5,0), vec2(0,0), gearbox_elements_offset_lerp_t) * GearboxScale
        mguk_elements_position_offset = math.lerp(vec2(0,0), vec2(-5,0), gearbox_elements_offset_lerp_t) * GearboxScale
    else
        gearbox_elemnts_position_offset = math.lerp(vec2(0,0), vec2(-5,0), gearbox_elements_offset_lerp_t) * GearboxScale
        mguk_elements_position_offset = math.lerp(vec2(5,0), vec2(0,0), gearbox_elements_offset_lerp_t) * GearboxScale
    end


    local mod_element_lerp_t = settings.anim_lerp(Time - mod_element_state_timer, mod_element_state, 0.3)
    local del_element_lerp_t = settings.anim_lerp(Time - del_element_state_timer, del_element_state, 0.3)
    local rec_element_lerp_t = settings.anim_lerp(Time - rec_element_state_timer, rec_element_state, 0.3)


    local mod_element_offset = vec2(0,0)
    local rec_element_offset = vec2(0,0)
    local del_element_offset = vec2(0,0)
    if mod_input and gearbox_elements_offset_lerp_t == 1 then
        mod_element_state_timer = Time
        mod_anim_state = 1
        mod_element_state = false
        if mod_element_lerp_t < 1 then
            mod_element_lerp_t = 0
        end
    end

    if del_input and gearbox_elements_offset_lerp_t == 1 then
        del_element_state_timer = Time
        del_anim_state = 1
        del_element_state = false
        if del_element_lerp_t < 1 and del_anim_state == 1 then
            del_element_lerp_t = 0
        end
    end

    if rec_input and gearbox_elements_offset_lerp_t == 1 then
        rec_element_state_timer = Time
        rec_anim_state = 1
        rec_element_state = false
        if rec_element_lerp_t < 1 then
            rec_element_lerp_t = 0
        end
    end


    if mod_element_lerp_t == 0 and mod_anim_state == 1 then
        mod_element_state_timer = Time
        mod_element_state = true
        mod_anim_state = 2
        if my_car.mguhChargingBatteries then
            current_mod_data = "BAT"
        else
            current_mod_data = "MOT"
        end
    end

    local mguk_delivery_name = ac.getMGUKDeliveryName(focused)
    if del_element_lerp_t == 0 and del_anim_state == 1 then
        del_element_state_timer = Time
        del_element_state = true
        del_anim_state = 2
        if mguk_delivery_name ~= nil then
            current_del_data = string.upper(mguk_delivery_name)
        end
    end

    if rec_element_lerp_t == 0 and rec_anim_state == 1 then
        rec_element_state_timer = Time
        rec_element_state = true
        rec_anim_state = 2
        current_rec_data = tostring(my_car.mgukRecovery * 10)
    end

    local mod_element_offset_lerp_t = settings.ease_cubic_remap(Time - mod_element_state_timer, 0.3)
    local del_element_offset_lerp_t = settings.ease_cubic_remap(Time - del_element_state_timer, 0.3)
    local rec_element_offset_lerp_t = settings.ease_cubic_remap(Time - rec_element_state_timer, 0.3)


    if mod_element_state == false then
        mod_element_offset = math.lerp(vec2(0,0), vec2(-5,0), mod_element_offset_lerp_t) * GearboxScale
    else
        mod_element_offset = math.lerp(vec2(5,0), vec2(0,0), mod_element_offset_lerp_t) * GearboxScale
    end

    if del_element_state == false then
        del_element_offset = math.lerp(vec2(0,0), vec2(-5,0),del_element_offset_lerp_t) * GearboxScale
    else
        del_element_offset = math.lerp(vec2(5,0), vec2(0,0),del_element_offset_lerp_t) * GearboxScale
    end

    if rec_element_state == false then
        rec_element_offset = math.lerp(vec2(0,0), vec2(-5,0),rec_element_offset_lerp_t) * GearboxScale
    else
        rec_element_offset = math.lerp(vec2(5,0), vec2(0,0),rec_element_offset_lerp_t) * GearboxScale
    end

    if mod_anim_state ~= 0 then
        if mod_anim_state == 2 and mod_element_lerp_t == 1 then
            mod_anim_state = 0
        end
    end

    if del_anim_state ~= 0 then
        if del_anim_state == 2 and del_element_lerp_t == 1 then
           del_anim_state = 0
        end
    end

    if rec_anim_state ~= 0 then
        if rec_anim_state == 2 and rec_element_lerp_t == 1 then
           rec_anim_state = 0
        end
    end

    local mod_element_alpha = mguk_elemnts_lerp_t
    local current_mod_element_offset = vec2(0,0)

    local del_element_alpha = mguk_elemnts_lerp_t
    local current_del_element_offset = vec2(0,0)

    local rec_element_alpha = mguk_elemnts_lerp_t
    local current_rec_element_offset = vec2(0,0)
    
    if gearbox_elements_offset_lerp_t < 1 then
        if my_car.mguhChargingBatteries then
            current_mod_data = "BAT"
        else
            current_mod_data = "MOT"
        end

        if mguk_delivery_name ~= nil then
            current_del_data = string.upper(mguk_delivery_name)
        end

        current_rec_data = tostring(my_car.mgukRecovery * 10)
    end

    if mod_anim_state ~= 0 and gearbox_elements_offset_lerp_t == 1 then
        mod_element_alpha = gearbox_elements_offset_lerp_t == 1 and mod_element_lerp_t or mguk_elemnts_lerp_t
        current_mod_element_offset = mod_element_offset
    end

    if del_anim_state ~= 0 and gearbox_elements_offset_lerp_t == 1 then
        del_element_alpha = gearbox_elements_offset_lerp_t == 1 and del_element_lerp_t or mguk_elemnts_lerp_t
        current_del_element_offset = del_element_offset
    end

    if rec_anim_state ~= 0 and gearbox_elements_offset_lerp_t == 1 then
        rec_element_alpha = gearbox_elements_offset_lerp_t == 1 and rec_element_lerp_t or mguk_elemnts_lerp_t
        current_rec_element_offset = rec_element_offset
    end

    
    if gearbox_elemnts_lerp_t >= 1 then
        mod_element_alpha = 0
        del_element_alpha = 0
        rec_element_alpha = 0
    end
    -- ANIMATION OF THE SINGLE ELEMENT OF THE HYBRID PART



    ----- GEARBOX RPG AND KMH INFO ------------------------
        -- KMH and RPM texts
        -- we also keep the font for later
        -- top of number text is aligned with center line of shape
    ui.dwriteDrawText(kmh_string, kmh_fontsize, kmh_number_pos - gearbox_elemnts_position_offset, colors.WHITE * rgbm(1,1,1,gearbox_elemnts_lerp_t))
    ui.dwriteDrawText(rpm_string, kmh_fontsize, rpm_number_pos - gearbox_elemnts_position_offset, colors.WHITE * rgbm(1,1,1,gearbox_elemnts_lerp_t))
    ui.popDWriteFont()

    local text_offset = vec2(0, 10) * GearboxScale
    ui.pushDWriteFont(fonts.archivo_medium)
    local kmh_text = "KMH"
    if Gearbox_ShowMph then kmh_text = "MPH" end
    ui.dwriteDrawText(kmh_text, text_fontsize, (kmh_number_pos - text_offset) - gearbox_elemnts_position_offset, colors.TEXT_GRAY * rgbm(1,1,1,gearbox_elemnts_lerp_t))
    ui.dwriteDrawText("RPM", text_fontsize, (rpm_number_pos - text_offset) - gearbox_elemnts_position_offset, colors.TEXT_GRAY * rgbm(1,1,1,gearbox_elemnts_lerp_t))
    --end

    local delivery_font_size = settings.fontsize(11 * GearboxScale)
    local additional_offset = vec2(0,05) * GearboxScale

    ------- HYBRID INFO -----------------
    ui.popDWriteFont()
    ui.pushDWriteFont(fonts.archivo_bold)
    

    local delivery_split = string.split(current_del_data, " ", 2)
    ui.dwriteDrawText(delivery_split[1], delivery_font_size, del_number_pos + additional_offset - mguk_elements_position_offset - current_del_element_offset, colors.WHITE * rgbm(1,1,1,del_element_alpha))
    ui.dwriteDrawText(delivery_split[2], delivery_font_size, del_number_pos + additional_offset - mguk_elements_position_offset - current_del_element_offset + (vec2(0,15) * GearboxScale), colors.WHITE * rgbm(1,1,1,del_element_alpha))
    

    ui.dwriteDrawText(rec_string, delivery_font_size, rec_number_pos + additional_offset - mguk_elements_position_offset - current_rec_element_offset, colors.WHITE * rgbm(1,1,1,rec_element_alpha))
    if my_car.hasCockpitMGUHMode then
        ui.dwriteDrawText(current_mod_data, delivery_font_size, (mode_number_pos + additional_offset) - mguk_elements_position_offset - current_mod_element_offset, colors.WHITE * rgbm(1,1,1, mod_element_alpha))
    end
    ui.popDWriteFont()
    ui.pushDWriteFont(fonts.archivo_medium)

    if my_car.hasCockpitMGUHMode then
        ui.dwriteDrawText("MODE", text_fontsize,(mode_number_pos -  text_offset) - mguk_elements_position_offset, colors.TEXT_GRAY * rgbm(1,1,1, mguk_elemnts_lerp_t))
    end
    ui.dwriteDrawText("DEL", text_fontsize, (del_number_pos - text_offset) - mguk_elements_position_offset, colors.TEXT_GRAY * rgbm(1,1,1, mguk_elemnts_lerp_t))
    ui.dwriteDrawText("REC", text_fontsize, (rec_number_pos - text_offset) - mguk_elements_position_offset, colors.TEXT_GRAY *  rgbm(1,1,1, mguk_elemnts_lerp_t))


    
    --
    -- bottom region background
    --
    make_pillola_path(left_region_center, right_region_center, region_radius + region_border / 2, segments)
    ui.pathStroke(area_border_color, true, region_border)
    make_pillola_path(left_region_center, right_region_center, region_radius, segments)
    ui.pathFillConvex(colors.BG)
    
    --
    -- fuel dot
    --
    local dot_offset = vec2(2, 0) * GearboxScale
    local dot_radius = 6 * GearboxScale
    local dot_center = dot_offset + left_region_center
    ui.drawCircleFilled(dot_center, dot_radius, fuel_indicator_color)
    if draw_fuel_bloom then
        ui.drawImage(
            settings.get_asset("bloom"),
            dot_center - 5 * vec2(dot_radius, dot_radius),
            dot_center + 5 * vec2(dot_radius, dot_radius),
            colors.WARNING_RED
        )
    end
    
    --
    -- fuel stuff
    --
    local lowtext_fontsize = settings.fontsize(10) * GearboxScale
    left_region_center.x = math.round(left_region_center.x) -- moves fuzzy text
    local region_text_size = ui.measureDWriteText("FUEL", lowtext_fontsize)
    region_text_size.y = math.floor(region_text_size.y) -- removes fuzzy text
    local fuel_text_offset = vec2(15 * GearboxScale, -region_text_size.y/2)
    local fuel_text = string.format("%.1fL", my_car.fuel)
    local my_sim = ac.getSim()
    if Gearbox_ShowGal then
        fuel_text = string.format("%.1fgal", my_car.fuel / 3.785)
    end
    if my_sim.isOnlineRace and focused ~= 0 then fuel_text = "-" end
    ui.setCursor(left_region_center + fuel_text_offset)
    ui.dwriteText("FUEL", lowtext_fontsize, colors.TEXT_GRAY)
    ui.sameLine()
    ui.pushDWriteFont(fonts.archivo_bold)
    ui.dwriteText(fuel_text, lowtext_fontsize, colors.WHITE)
    ui.popDWriteFont()
    
    --
    -- estimated stuff
    --
    local estimated_fuel = my_car.fuelPerLap
    local lap_text = ''
    if estimated_fuel <= 0.01 then
        lap_text = '-'
        if focused == 0 and previous_fuel_estimate ~= nil then -- recover fuel estimate from old session so we have a jumping off point.
            lap_text = string.format("%.1f", my_car.fuel / previous_fuel_estimate)
        end
    else
        lap_text = string.format("%.1f", my_car.fuel / estimated_fuel)
    end
    local lap_text_offset = vec2(125 * GearboxScale, -region_text_size.y/2)
    ui.setCursor(left_region_center + lap_text_offset)
    ui.dwriteText("EST. LAP", lowtext_fontsize, colors.TEXT_GRAY)
    ui.sameLine()
    ui.pushDWriteFont(fonts.archivo_bold)
    ui.dwriteText(lap_text, lowtext_fontsize, colors.WHITE)
    ui.popDWriteFont()

    ui.popDWriteFont()
    last_battery_value = my_car.kersCharge
    players.play_intro_anim(entire_app_center, entire_app_size, on_show_animation_start, GearboxScale)
    settings.lock_app(entire_app_center, entire_app_size, APPNAMES.gearbox, GearboxScale)
    settings.auto_scale_window(entire_app_size * 1.01, APPNAMES.gearbox)
    settings.auto_place_once(entire_app_size, APPNAMES.gearbox)
end

return gearbox -- expose functions to the outside
