-- SlotMachine.lua – Slot gép FS22/25 tartás funkcióval + megállás hang + Dupla vagy Semmi játék

SlotMachine = {}
SlotMachine.MOD_NAME = g_currentModName

local STEPS, STEP_DEG = 12, 30
local EPS = 1e-4

local function easeOutCubic(t) local i=1-t; return 1-i*i*i end

local REEL_MAP = {
    {"Hetes","Citrom","Szőlő","Szilva","Cseresznye","Bonus","Óra","Harang","Narancs","Dinnye","Csillag","Dobókocka"},
    {"Cseresznye","Óra","Szőlő","Narancs","Bonus","Hetes","Szilva","Citrom","Dobókocka","Harang","Dinnye","Csillag"},
    {"Cseresznye","Citrom","Csillag","Dobókocka","Szőlő","Dinnye","Szilva","Óra","Harang","Hetes","Bonus","Narancs"}
}

-- Szimbólum → nyeremény érték €
local PAYOUT = {
    ["Cseresznye"]=20, ["Citrom"]=30, ["Narancs"]=40, ["Harang"]=65,
    ["Szőlő"]=80, ["Hetes"]=100, ["Dinnye"]=110, ["Csillag"]=120,
    ["Szilva"]=50, ["Bonus"]=130, ["Dobókocka"]=140, ["Óra"]=150
}

local function angleToIndexZNeg(r) local d=math.deg(r); return (math.floor(((-d)+15)/30)%12)+1 end
local function indexToAngleZNeg(i) return math.rad(-((i-1)*STEP_DEG)) end

local function sm_createAndLoadSample(absPath, sampleName)
    local s = createSample(sampleName or "slotSample")
    if not loadSample(s, absPath, false) then
        print(("[SlotMachine] Hang betöltés hiba: %s"):format(tostring(absPath)))
        delete(s); return nil
    end
    return s
end

-- ========= PENZ változtatás EVENT =========
SlotMachinePenzEvent = {}
local SlotMachinePenzEvent_mt = Class(SlotMachinePenzEvent, Event)
InitEventClass(SlotMachinePenzEvent, "SlotMachinePenzEvent")

function SlotMachinePenzEvent.emptyNew() return Event.new(SlotMachinePenzEvent_mt) end
function SlotMachinePenzEvent.new(vehicle, delta)
    local self=SlotMachinePenzEvent.emptyNew(); self.vehicle=vehicle; self.delta=delta; return self
end
function SlotMachinePenzEvent:writeStream(s, c)
    NetworkUtil.writeNodeObject(s, self.vehicle); streamWriteFloat32(s, self.delta)
end
function SlotMachinePenzEvent:readStream(s, c)
    self.vehicle=NetworkUtil.readNodeObject(s); self.delta=streamReadFloat32(s); self:run(c)
end
function SlotMachinePenzEvent:run(connection)
    if self.vehicle ~= nil and self.vehicle.addFillUnitFillLevel ~= nil then
        local spec = self.vehicle.spec_slotMachine
        if spec ~= nil and spec.fillTypePENZ ~= nil then
            local ft = spec.fillTypePENZ
            local idx = 1
            local farmId = self.vehicle:getOwnerFarmId() or g_currentMission:getFarmId() or 1
            if self.vehicle:getFillUnitFillType(idx) ~= ft then
                self.vehicle:setFillUnitFillType(idx, ft)
            end
            self.vehicle:addFillUnitFillLevel(farmId, idx, self.delta, ft, ToolType.UNDEFINED, nil)
        end
    end
end

local function changePENZ(self, delta)
    if delta == 0 then return end
    if self.isServer then
        SlotMachinePenzEvent.run(SlotMachinePenzEvent.new(self, delta))
    else
        g_client:getServerConnection():sendEvent(SlotMachinePenzEvent.new(self, delta))
    end
end

-- ========= spec / hooks =========
function SlotMachine.prerequisitesPresent(specializations) return true end

function SlotMachine.initSpecialization()
    local schema = Vehicle.xmlSchema
    schema:setXMLSpecializationType("slotMachine")
    schema:setXMLSpecializationType()
end

function SlotMachine.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "startSpin",           SlotMachine.startSpin)
    SpecializationUtil.registerFunction(vehicleType, "checkWinGrid",        SlotMachine.checkWinGrid)
    SpecializationUtil.registerFunction(vehicleType, "requestWithdraw",     SlotMachine.requestWithdraw)
    SpecializationUtil.registerFunction(vehicleType, "performWithdrawPENZ", SlotMachine.performWithdrawPENZ)
    SpecializationUtil.registerFunction(vehicleType, "toggleHold",          SlotMachine.toggleHold)
    -- új függvények dupla vagy semmihez
    SpecializationUtil.registerFunction(vehicleType, "donChoose",           SlotMachine.donChoose)
    SpecializationUtil.registerFunction(vehicleType, "donBank",             SlotMachine.donBank)
end

function SlotMachine.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad",                 SlotMachine)
    SpecializationUtil.registerEventListener(vehicleType, "onDelete",               SlotMachine)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdate",               SlotMachine)
    SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", SlotMachine)
end

function SlotMachine:onLoad(savegame)
    self.spec_slotMachine = {}
    local spec = self.spec_slotMachine

    spec.reelNodes = {
        I3DUtil.indexToObject(self.components, "0>8|0"),
        I3DUtil.indexToObject(self.components, "0>8|1"),
        I3DUtil.indexToObject(self.components, "0>8|2")
    }
    spec.isSpinning=false; spec.startZ={0,0,0}; spec.targetZ={0,0,0}; spec.targetIdx={1,1,1}
    spec.durationMs={1500,1500,1500}; spec.elapsedMs={0,0,0}; spec.done={false,false,false}; spec.grid={{},{},{}}

    spec.hold = {false,false,false}
    spec.holdUsed = false

    local base = self.baseDirectory or ""
    spec.sampleSpin = sm_createAndLoadSample(base .. "sounds/push.wav", "slot_spin")
    spec.sampleWin  = sm_createAndLoadSample(base .. "sounds/win.wav",  "slot_win")
    spec.sampleStop = sm_createAndLoadSample(base .. "sounds/megall.wav", "slot_stop")
    spec.sampleYes  = sm_createAndLoadSample(base .. "sounds/yes.wav", "slot_yes")
    spec.sampleNo   = sm_createAndLoadSample(base .. "sounds/no.wav",  "slot_no")

    spec.fillTypePENZ  = g_fillTypeManager:getFillTypeIndexByName("PENZ")
    spec.fillUnitIndex = 1 -- fallback
    if spec.fillTypePENZ == nil then print("[SlotMachine] FIGYELEM: 'PENZ' fillType nem talált.") end

    spec.actionEvents = {}

    -- dupla vagy semmi state
    spec.doubleOrNothingActive = false
    spec.currentWin = 0
    spec.doubleSide = nil
end
function SlotMachine:onDelete()
    local spec=self.spec_slotMachine
    if spec.sampleSpin~=nil then delete(spec.sampleSpin); spec.sampleSpin=nil end
    if spec.sampleWin ~=nil then delete(spec.sampleWin);  spec.sampleWin =nil end
    if spec.sampleStop~=nil then delete(spec.sampleStop); spec.sampleStop=nil end
    if spec.sampleYes ~=nil then delete(spec.sampleYes);  spec.sampleYes =nil end
    if spec.sampleNo  ~=nil then delete(spec.sampleNo);   spec.sampleNo  =nil end
end

function SlotMachine:onRegisterActionEvents(isActiveForInput, isActiveForInputIgnoreSelection)
    if self.isClient then
        local spec = self.spec_slotMachine
        self:clearActionEventsTable(spec.actionEvents)
        if isActiveForInput then
            -- Pörgetés
            local spinAction = InputAction.SLOTSPIN or InputAction.IMPLEMENT_EXTRA
            local _, eventId = self:addActionEvent(spec.actionEvents, spinAction, self, SlotMachine.startSpin,
                false,true,false,true,nil)
            g_inputBinding:setActionEventText(eventId, g_i18n:getText("input_SLOTSPIN") or "Pörgetés (B)")

            -- Kifizetés
            if InputAction.SLOT_WITHDRAW ~= nil then
                local _, evId2 = self:addActionEvent(spec.actionEvents, InputAction.SLOT_WITHDRAW, self, SlotMachine.requestWithdraw,
                    false,true,false,true,nil)
                g_inputBinding:setActionEventText(evId2, g_i18n:getText("input_SLOT_WITHDRAW") or "Kifizetés (Ctrl+Alt+Y)")
            end

            -- Tartás gombok
            if InputAction.SLOT_HOLD_LEFT ~= nil then
                local _, evL = self:addActionEvent(spec.actionEvents, InputAction.SLOT_HOLD_LEFT, self,
                    function() self:toggleHold(1) end, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evL, "Bal tárcsa tart (A)")
            end
            if InputAction.SLOT_HOLD_MID ~= nil then
                local _, evM = self:addActionEvent(spec.actionEvents, InputAction.SLOT_HOLD_MID, self,
                    function() self:toggleHold(2) end, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evM, "Középső tárcsa tart (S)")
            end
            if InputAction.SLOT_HOLD_RIGHT ~= nil then
                local _, evR = self:addActionEvent(spec.actionEvents, InputAction.SLOT_HOLD_RIGHT, self,
                    function() self:toggleHold(3) end, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evR, "Jobb tárcsa tart (D)")
            end

            -- Dupla vagy Semmi gombok
            if InputAction.SLOT_DON_LEFT ~= nil then
                local _, evDL = self:addActionEvent(spec.actionEvents, InputAction.SLOT_DON_LEFT, self,
                    function() self:donChoose("left") end, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evDL, "Dupla vagy Semmi: BAL")
            end
            if InputAction.SLOT_DON_RIGHT ~= nil then
                local _, evDR = self:addActionEvent(spec.actionEvents, InputAction.SLOT_DON_RIGHT, self,
                    function() self:donChoose("right") end, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evDR, "Dupla vagy Semmi: JOBB")
            end
            if InputAction.SLOT_DON_BANK ~= nil then
                local _, evDB = self:addActionEvent(spec.actionEvents, InputAction.SLOT_DON_BANK, self,
                    SlotMachine.donBank, false,true,false,true,nil)
                g_inputBinding:setActionEventText(evDB, "Dupla vagy Semmi: BANKBA")
            end
        end
    end
end

-- ========= Tartás kapcsoló =========
function SlotMachine:toggleHold(idx)
    local spec = self.spec_slotMachine
    if spec.isSpinning then return end
    if spec.holdUsed then
        g_currentMission:showBlinkingWarning("Tartás már felhasználva!", 2000)
        return
    end
    spec.hold[idx] = not spec.hold[idx]
    local state = spec.hold[idx] and "BE" or "KI"
    g_currentMission:showBlinkingWarning(("Tárcsa %d tartás: %s"):format(idx, state), 1500)
end

-- ========= Pörgetés =========
function SlotMachine:startSpin()
    local spec = self.spec_slotMachine
    if spec.isSpinning or spec.doubleOrNothingActive then return end -- ne induljon, ha DON játék aktív
    if spec.fillTypePENZ ~= nil then
        local idx = spec.fillUnitIndex or 1
        local lvl = self:getFillUnitFillLevel(idx) or 0

        local cost = spec.holdUsed and 5 or 2
        if lvl < cost then
            g_currentMission:showBlinkingWarning("Nincs elég PÉNZ a pörgetéshez! (min. "..cost..")", 2500)
            return
        end

        changePENZ(self, -cost)
    end

    spec.isSpinning = true

    for i=1,3 do
        if not spec.hold[i] then
            spec.done[i] = false
            spec.elapsedMs[i] = 0
            local _,_,rz = getRotation(spec.reelNodes[i])
            spec.startZ[i] = rz

            local minDeg, maxDeg, step = 60, 2160, 30
            local rndSteps = math.random(minDeg/step, maxDeg/step)
            local targetDeltaDeg = -(rndSteps * step)

            spec.targetZ[i] = rz + math.rad(targetDeltaDeg)

            local finalRz = rz + math.rad(targetDeltaDeg)
            spec.targetIdx[i] = angleToIndexZNeg(finalRz)
        else
            spec.done[i] = true
        end
    end

    if spec.hold[1] or spec.hold[2] or spec.hold[3] then
        spec.holdUsed = true
        spec.hold = {false,false,false}
    else
        spec.holdUsed = false
    end

    local baseL = math.random(1200,1500)
    local addM  = math.random(180,420)
    local addR  = math.random(180,420)
    spec.durationMs[1] = baseL
    spec.durationMs[2] = baseL + addM
    spec.durationMs[3] = baseL + addR + addM

    if spec.sampleSpin ~= nil then
        playSample(spec.sampleSpin, 1, 1, 0, 0, 0)
    end
end

-- ========= Update =========
function SlotMachine:onUpdate(dt)
    local spec=self.spec_slotMachine
    if not spec.isSpinning then return end
    local allDone=true
    for i=1,3 do
        if not spec.done[i] then
            allDone=false
            spec.elapsedMs[i]=math.min(spec.durationMs[i], spec.elapsedMs[i]+dt)
            local t=spec.elapsedMs[i]/spec.durationMs[i]; local e=easeOutCubic(t)
            local z=MathUtil.lerp(spec.startZ[i], spec.targetZ[i], e); setRotation(spec.reelNodes[i],0,0,z)
            if spec.elapsedMs[i]>=spec.durationMs[i] then
                setRotation(spec.reelNodes[i],0,0,indexToAngleZNeg(spec.targetIdx[i]))
                spec.done[i]=true
                if spec.sampleStop ~= nil then
                    playSample(spec.sampleStop,1,1,0,0,0)
                end
            end
        end
    end
    if allDone then
        spec.isSpinning=false
        for r=1,3 do spec.grid[r]={} end
        for k=1,3 do
            local _,_,rz = getRotation(spec.reelNodes[k])
            local mid=angleToIndexZNeg(rz); local prev=((mid-2+STEPS)%STEPS)+1; local nxt=((mid)%STEPS)+1
            spec.grid[1][k]=REEL_MAP[k][prev]; spec.grid[2][k]=REEL_MAP[k][mid]; spec.grid[3][k]=REEL_MAP[k][nxt]
        end
        local won,sym = self:checkWinGrid(spec.grid)
        if won then
            local value=PAYOUT[sym] or 0
            if value > 0 then
                -- itt indítjuk a dupla vagy semmit
                spec.currentWin = value
                spec.doubleOrNothingActive = true
                spec.doubleSide = (math.random(0,1)==0) and "left" or "right"
                if spec.sampleWin ~= nil then playSample(spec.sampleWin,1,1,0,0,0) end
                g_currentMission:showBlinkingWarning("NYERTÉL! Dupla vagy Semmi? Válassz BAL / JOBB, vagy BANK!", 4000)
            end
        else
            g_currentMission:showBlinkingWarning("Nem nyertél!",1500)
        end
    end
end

-- ========= Nyerés ellenőrzés =========
local function triple(a,b,c) return (a~=nil and b~=nil and c~=nil) and (a==b and b==c) end
function SlotMachine:checkWinGrid(g)
    if triple(g[1][1],g[1][2],g[1][3]) then return true,g[1][1] end
    if triple(g[2][1],g[2][2],g[2][3]) then return true,g[2][1] end
    if triple(g[3][1],g[3][2],g[3][3]) then return true,g[3][1] end
    if triple(g[1][1],g[2][2],g[3][3]) then return true,g[1][1] end
    if triple(g[1][3],g[2][2],g[3][1]) then return true,g[1][3] end
    return false,nil
end

-- ========= Dupla vagy Semmi logika =========
function SlotMachine:donChoose(side)
    local spec = self.spec_slotMachine
    if not spec.doubleOrNothingActive then return end
    if side == spec.doubleSide then
        spec.currentWin = spec.currentWin * 2
        changePENZ(self, spec.currentWin)
        if spec.sampleYes ~= nil then playSample(spec.sampleYes,1,1,0,0,0) end
        g_currentMission:showBlinkingWarning("Dupláztál! +"..spec.currentWin.." €", 4000)
    else
        spec.currentWin = 0
        if spec.sampleNo ~= nil then playSample(spec.sampleNo,1,1,0,0,0) end
        g_currentMission:showBlinkingWarning("Elbuktad a nyereményt!", 3500)
    end
    spec.doubleOrNothingActive = false
end

function SlotMachine:donBank()
    local spec = self.spec_slotMachine
    if not spec.doubleOrNothingActive then return end
    changePENZ(self, spec.currentWin)
    g_currentMission:showBlinkingWarning("Bankba tetted: +"..spec.currentWin.." €", 3500)
    spec.doubleOrNothingActive = false
end

-- ========= Kifizetés EVENT =========
SlotMachineWithdrawEvent = {}
local SlotMachineWithdrawEvent_mt = Class(SlotMachineWithdrawEvent, Event)
InitEventClass(SlotMachineWithdrawEvent, "SlotMachineWithdrawEvent")
function SlotMachineWithdrawEvent.emptyNew() return Event.new(SlotMachineWithdrawEvent_mt) end
function SlotMachineWithdrawEvent.new(vehicle)
    local self=SlotMachineWithdrawEvent.emptyNew(); self.vehicle=vehicle; return self
end
function SlotMachineWithdrawEvent:writeStream(s,c) NetworkUtil.writeNodeObject(s,self.vehicle) end
function SlotMachineWithdrawEvent:readStream(s,c) self.vehicle=NetworkUtil.readNodeObject(s); self:run(c) end
function SlotMachineWithdrawEvent:run(c)
    if self.vehicle~=nil and self.vehicle.performWithdrawPENZ~=nil then
        self.vehicle:performWithdrawPENZ()
    end
end

function SlotMachine:requestWithdraw()
    if self.isServer then
        self:performWithdrawPENZ()
    else
        g_client:getServerConnection():sendEvent(SlotMachineWithdrawEvent.new(self))
    end
end

-- ========= Kifizetés: a gépből → farm pénz =========
function SlotMachine:performWithdrawPENZ()
    if not self.isServer then return end
    local spec=self.spec_slotMachine
    if spec==nil or spec.fillTypePENZ==nil then
        g_currentMission:showBlinkingWarning("Nincs beállítva a PENZ fillType.", 2000)
        return
    end
    local ft   = spec.fillTypePENZ
    local srcI = 1
    if self:getFillUnitFillType(srcI) ~= ft then
        self:setFillUnitFillType(srcI, ft)
    end
    local farmId = self:getOwnerFarmId() or g_currentMission:getFarmId() or 1
    local amount = self:getFillUnitFillLevel(srcI) or 0
    if amount <= 0 then
        g_currentMission:showBlinkingWarning("Üres a gép – nincs kifizetés.", 2000)
        return
    end
    self:addFillUnitFillLevel(farmId, srcI, -amount, ft, ToolType.UNDEFINED, nil)
    g_currentMission:addMoney(amount, farmId, MoneyType.OTHER, true, true)
    g_currentMission:showBlinkingWarning(("Kifizetés kész! +%d €"):format(amount), 3500)
end
