local Gui = require 'utils.gui'
local Event = require 'utils.event'
local Global = require 'utils.global'
local PlayerStats = require 'player_stats'

local Public = {}

local markets = {}

Global.register(
    {markets = markets},
    function(tbl)
        markets = tbl.markets
    end
)

local market_frame_name = Gui.uid_name()
local item_button_name = Gui.uid_name()
local count_slider_name = Gui.uid_name()
local count_text_name = Gui.uid_name()

local function redraw_market_items(data)
    local grid = data.grid

    Gui.clear(grid)

    local count = data.count
    local market_items = data.market_items

    for i, item in ipairs(market_items) do
        local name = item.name
        local price = item.price

        local price_per_item = string.format('%.2f', price)

        local button =
            grid.add({type = 'flow'}).add {
            type = 'sprite-button',
            name = item_button_name,
            sprite = 'item/' .. name,
            number = count,
            tooltip = table.concat {name, '  price: ', price_per_item}
        }
        button.style = 'slot_button'

        Gui.set_data(button, {index = i, data = data})

        local message = math.ceil(price * count)
        if message == 1 then
            message = message .. ' coin'
        else
            message = message .. ' coins'
        end

        local label = grid.add {type = 'label', caption = message}
        local label_style = label.style
        label_style.width = 80
        label_style.font = 'default-bold'
    end
end

local function do_coin_label(player, label)
    local coin_count = player.get_item_count('coin')

    if coin_count == 1 then
        label.caption = coin_count .. ' coin available'
    else
        label.caption = coin_count .. ' coins available'
    end
end

local function draw_market_frame(player, market_items)
    local frame =
        player.gui.center.add {type = 'frame', name = market_frame_name, caption = 'Market', direction = 'vertical'}

    local scroll_pane = frame.add {type = 'scroll-pane'}
    local scroll_style = scroll_pane.style
    scroll_style.maximal_height = 600

    local grid = scroll_pane.add {type = 'table', column_count = 10}

    local data = {
        grid = grid,
        count = 1,
        market_items = market_items
    }

    redraw_market_items(data)

    local coin_label = frame.add {type = 'label'}
    do_coin_label(player, coin_label)
    coin_label.style.font = 'default-bold'
    data.coin_label = coin_label

    local count_flow = frame.add {type = 'flow'}

    local count_slider =
        count_flow.add {type = 'slider', name = count_slider_name, minimum_value = 1, maximum_value = 7, value = 1}
    local count_text = count_flow.add {type = 'text-box', name = count_text_name, text = '1'}

    count_slider.style.width = 100
    count_text.style.width = 60

    local quantity_label = count_flow.add {type = 'label', caption = 'Quantity'}
    quantity_label.style.font = 'default-bold'

    data.slider = count_slider
    data.text = count_text

    Gui.set_data(count_slider, data)
    Gui.set_data(count_text, data)

    return frame
end

local function gui_opened(event)
    if not event.gui_type == defines.gui_type.entity then
        return
    end

    local player = game.players[event.player_index]
    if not player or not player.valid then
        return
    end

    local entity = event.entity
    if not entity or not entity.valid then
        return
    end

    local pos = entity.position
    local market_data = markets[pos.x .. ',' .. pos.y]
    if not market_data then
        return
    end

    local frame = draw_market_frame(player, market_data)

    player.opened = frame
end

function Public.add_market(position, data)
    markets[position.x .. ',' .. position.y] = data
end

Event.add(defines.events.on_gui_opened, gui_opened)

Gui.on_custom_close(
    market_frame_name,
    function(event)
        local element = event.element
        Gui.destroy(element)
    end
)

Event.add(
    defines.events.on_player_died,
    function(event)
        local player = game.players[event.player_index or 0]

        if not player or not player.valid then
            return
        end

        local element = player.gui.center

        if element and element.valid then
            element = element[market_frame_name]
            if element and element.valid then
                Gui.destroy(element)
            end
        end
    end
)

Gui.on_value_changed(
    count_slider_name,
    function(event)
        local element = event.element
        local data = Gui.get_data(element)

        local value = math.floor(element.slider_value)
        local count
        if value % 2 == 0 then
            count = 10 ^ (value * 0.5) * 0.5
        else
            count = 10 ^ ((value - 1) * 0.5)
        end

        data.count = count
        data.text.text = count

        redraw_market_items(data)
    end
)

Gui.on_text_changed(
    count_text_name,
    function(event)
        local element = event.element
        local data = Gui.get_data(element)

        local count = tonumber(element.text)

        if count then
            count = math.floor(count)
            count = math.clamp(count, 1, 1000)
            data.count = count
            data.text.text = count
        else
            data.count = 1
        end

        redraw_market_items(data)
    end
)

Gui.on_click(
    item_button_name,
    function(event)
        local player = event.player
        local element = event.element
        local button_data = Gui.get_data(element)
        local data = button_data.data

        local item = data.market_items[button_data.index]

        local name = item.name
        local price = item.price
        local count = data.count

        local cost = math.ceil(price * count)
        local coin_count = player.get_item_count('coin')

        if cost > coin_count then
            player.print('Insufficient coins')
        else
            local inserted = player.insert {name = name, count = count}
            if inserted < count then
                player.print('Insufficient inventory space')
                if inserted > 0 then
                    player.remove_item {name = name, count = inserted}
                end
            else
                player.remove_item {name = 'coin', count = cost}
                do_coin_label(player, data.coin_label)
                PlayerStats.change_coin_spent(player.index, cost)
            end
        end
    end
)

return Public
