local RS = require 'map_gen.shared.redmew_surface'
local Debug = require 'utils.debug'
local Rendering = require 'utils.rendering'
local Vertices = require 'resources.vertices'
local insert = table.insert

local toggle_debug = false --Set to true if you wish to get spammed with debug messages from the rendering module (Requires _DEBUG = true)

local function debug_print(message, trace_levels)
    if toggle_debug then
        Debug.print(message, trace_levels)
    end
end

local Public = {}

--At zoom level 1 a tile is 32x32 pixels
--tile size is calculated by 32 * zoom level.

local function text_height_in_tiles(scale, zoom)
    -- Default (count-font) at scale 10 is 125 pixels for lower letters and 109 for capital letters
    -- At scale 5 this is 64 or very close to half of the size at scale 10 (62.5)
    -- Therefore size hsa been determined to be (12.5 * scale) + 1
    local size = (12.5 * scale) + 1
    local pixel_per_tile = zoom * 32
    return size / pixel_per_tile, size
end

local function calculate_percentages(settings, player_resolution)
    local original_resolution = settings.original_resolution
    return {
        height = player_resolution.height / original_resolution.height,
        width = player_resolution.width / original_resolution.width,
        tile = (settings.original_zoom * 32) / (settings.player_zoom * 32)
    }
end

local function text_scale(percentage, scale)
    return scale * (percentage.height + percentage.width) * 0.5
end

local function fit_to_screen(percentage, coordinates)
    if not coordinates.fitted then
        local height = percentage.height
        local width = percentage.width
        local tile = percentage.tile
        for _, pos in pairs(coordinates) do
            if type(pos) == 'number' then
                coordinates.x = coordinates.x * width
                coordinates.y = coordinates.y * height
                break
            else
                pos.x = pos.x * width
                pos.y = pos.y * height
            end
        end
        for _, pos in pairs(coordinates) do
            if type(pos) == 'number' then
                coordinates.x = coordinates.x * tile
                coordinates.y = coordinates.y * tile
                break
            else
                pos.x = pos.x * tile
                pos.y = pos.y * tile
            end
        end
        coordinates.fitted = true
    end
    return coordinates
end

local function fit_to_screen_edges(settings, player_resolution, coordinates)
    if not coordinates.fitted then
        local tile = settings.original_zoom * 32
        local player_tile = settings.player_zoom * 32
        local display_scale = player_resolution.scale or 1

        local player_height = (player_resolution.height / player_tile) * 0.5
        local player_width = (player_resolution.width / player_tile) * 0.5

        for _, pos in pairs(coordinates) do
            if type(pos) == 'number' then
                coordinates.y = -player_height + (((coordinates.y * tile) / player_tile) * display_scale)
                coordinates.x = -player_width + (((coordinates.x * tile) / player_tile) * display_scale)
                break
            else
                pos.y = -player_height + (((pos.y * tile) / player_tile) * display_scale)
                pos.x = -player_width + (((pos.x * tile) / player_tile) * display_scale)
            end
        end
        coordinates.fitted = true
    end
    return coordinates
end

local function create_background_params(params)
    local background_params = params.background
    if background_params then
        for k, v in pairs(params) do
            if k ~= 'background' then
                if not background_params[k] then
                    background_params[k] = v
                end
            end
        end
    else
        background_params = params
    end
    return background_params
end

local function text_background(settings, offset, player, percentages, size, number_text, params)
    local margin = 0.01 / params.scale
    local left_top = fit_to_screen(percentages, {x = -40, y = 0})
    local right_bottom = fit_to_screen(percentages, {x = 40, y = 0})
    left_top.y = -size * margin * 0.875
    right_bottom.y = size * (1.5 + (margin * 1.125)) * number_text
    local background_params = create_background_params(params)
    return Public.draw_rectangle(settings, offset, left_top, right_bottom, player, background_params)
end

function Public.draw_text(settings, offset, text, player, params, draw_background, fit_to_edge)
    local list = {}
    local player_resolution = player.display_resolution
    player_resolution.scale = player.display_scale
    local percentages = calculate_percentages(settings, player_resolution)
    local scale = params.scale

    if draw_background ~= -1 then
        scale = text_scale(percentages, scale)
        local size = text_height_in_tiles(scale, settings.player_zoom)
        if fit_to_edge then
            offset = fit_to_screen_edges(settings, player_resolution, offset)
        else
            offset = fit_to_screen(percentages, offset)
        end
        offset.y = offset.y - size * 0.5
    end
    local size = text_height_in_tiles(scale, settings.player_zoom)

    if draw_background == true then
        insert(list, text_background(settings, offset, player, percentages, size, 1, params, fit_to_edge))
    end

    local target = {x = player.physical_position.x + offset.x, y = player.physical_position.y + offset.y}

    local color = params.color
    color = color and color or {r = 255, g = 255, b = 255}

    local font = params.font

    local surface = params.surface
    surface = surface or RS.get_surface()

    local ttl = params.time_to_live
    ttl = ttl and ttl or -1

    local forces = params.forces

    local players = params.players
    players = players or {}

    table.insert(players, player)

    local visible = params.visible
    visible = visible or true

    local dog = params.draw_on_ground
    dog = dog or false

    local orientation = params.orientation
    orientation = orientation or 0

    local alignment = params.alignment
    alignment = alignment or 'center'

    --local swz = params.scale_with_zoom
    local swz = true

    local oiam = params.only_in_alt_mode
    oiam = oiam or false

    local rendering_params = {
        text = {'', text},
        color = color,
        target = target,
        scale_with_zoom = swz,
        surface = surface,
        time_to_live = ttl,
        alignment = alignment,
        players = players,
        scale = scale,
        forces = forces,
        visible = visible,
        draw_on_ground = dog,
        only_in_alt_mode = oiam,
        orientation = orientation,
        font = font
    }

    debug_print(rendering_params)

    insert(list, rendering.draw_text(rendering_params))
    return list
end

function Public.draw_multi_line_text(settings, offset, texts, player, params, draw_background, fit_to_edge)
    local list = {}
    local player_resolution = player.display_resolution
    player_resolution.scale = player.display_scale
    local percentages = calculate_percentages(settings, player_resolution)
    local scale = params.scale

    scale = text_scale(percentages, scale)
    local size = text_height_in_tiles(scale, settings.player_zoom)

    if fit_to_edge then
        offset = fit_to_screen_edges(settings, player_resolution, offset)
    else
        offset = fit_to_screen(percentages, offset)
    end

    offset.y = offset.y - size * 0.5

    if draw_background then
        insert(list, text_background(settings, offset, player, percentages, size, #texts, params, fit_to_edge))
        draw_background = -1
    end

    for i = 1, #texts do
        insert(list, Public.draw_text(settings, offset, texts[i], player, params, draw_background, fit_to_edge)[1])
        offset.y = offset.y + (size * 1.5)
    end
    return list
end

function Public.draw_rectangle(settings, offset, left_top, right_bottom, player, params, fit_to_edge)
    local player_resolution = player.display_resolution
    player_resolution.scale = player.display_scale
    local percentages = calculate_percentages(settings, player_resolution)
    if fit_to_edge then
        offset = fit_to_screen_edges(settings, player_resolution, offset)
        left_top = fit_to_screen_edges(settings, player_resolution, left_top)
        right_bottom = fit_to_screen_edges(settings, player_resolution, right_bottom)
    else
        offset = fit_to_screen(percentages, offset)
        left_top = fit_to_screen(percentages, left_top)
        right_bottom = fit_to_screen(percentages, right_bottom)
    end

    local target_left = {x = player.physical_position.x + left_top.x + offset.x, y = player.physical_position.y + left_top.y + offset.y}
    local target_right = {x = player.physical_position.x + right_bottom.x + offset.x, y = player.physical_position.y + right_bottom.y + offset.y}

    local color = params.color
    color = color and color or {}

    local width = params.width
    width = width and width or 0

    local filled = params.filled
    filled = filled and filled or true

    local surface = params.surface
    surface = surface or RS.get_surface()

    local ttl = params.time_to_live
    ttl = ttl and ttl or -1

    local forces = params.forces

    local players = params.players
    players = players or {}

    table.insert(players, player)

    local visible = params.visible
    visible = visible or true

    local dog = params.draw_on_ground
    dog = dog or false

    local oiam = params.only_in_alt_mode
    oiam = oiam or false

    local rendering_params = {
        color = color,
        width = width,
        filled = filled,
        left_top = target_left,
        right_bottom = target_right,
        surface = surface,
        time_to_live = ttl,
        forces = forces,
        players = players,
        visible = visible,
        draw_on_ground = dog,
        only_in_alt_mode = oiam
    }

    debug_print(rendering_params)

    return rendering.draw_rectangle(rendering_params)
end

local blackout_settings = {original_resolution = {height = 1440, width = 2560}, original_zoom = 1, player_zoom = 1}
function Public.blackout(player, zoom, ttl, color)
    local left_top = {x = -40, y = -22.5}
    local right_bottom = {x = 40, y = 22.5}
    blackout_settings.player_zoom = zoom
    return Public.draw_rectangle(blackout_settings, {x = 0, y = 0}, left_top, right_bottom, player, {color = color, time_to_live = ttl})
end

function Public.draw_arrow(settings, offset, player, params, fit_to_edge)
    local player_resolution = player.display_resolution
    player_resolution.scale = player.display_scale
    local percentages = calculate_percentages(settings, player_resolution)
    if fit_to_edge then
        offset = fit_to_screen_edges(settings, player_resolution, offset)
    else
        offset = fit_to_screen(percentages, offset)
    end

    local vertices = Rendering.scale(Vertices.arrow, percentages.tile, percentages.tile)
    vertices = Rendering.rotate(vertices, params.rotation)
    vertices = Rendering.translate(vertices, offset.x, offset.y)

    local color = params.color or {1, 1, 1, 1}
    params.color = color

    local players = params.players
    players = players or {}

    table.insert(players, player)
    params.players = players

    params.surface = RS.get_surface()
    --Debug.print(vertices)
    return Rendering.draw_polygon(vertices, params)
end

return Public
