function print_dbg(...)
    -- level = 2 → el llamador de imprimir_con_contexto
    local info = debug.getinfo(2, "n")
    local nombre = info.name or "<anónima>"
    print("[" .. nombre .. "]", ...)
end

hab1 = 1
tx1 = 1
ty1  = 1
hab2 = 1
tx2 = 1
ty2 = 1
 
require "pattern"

imp = {}

DEBUG_FN_NAME = false

function imp.new(_hab,_x,_y)
    local world_x, world_y = coords.room_to_world(_hab,_x,_y)
    return {
        name="imp",
        hab=_hab,
        x=world_x, y=world_y,
        x_old = 0, y_old = 0,
        w=32, h=32,
        bb={x=8,y=0,w=16,h=32},
        flip=true,
        frame=28,
        anim={28,29,28,30}, -- seqüencia de frames
        wait=0,
        step=0,
        moods={"stop",PATTERN_CHASE, PATTERN_AVOID, PATTERN_RANDOM }, -- deu coincidir en els modes de pattern
        mood="stop", --anterior fight_mode
        timers={mood=150,target=350, shot=100, super=2000, super_wait=200, flip_wait=50, stucked_locked=50}, --anterior *_cooldown
        move_types={free=0, pattern=1, super=2},
        move_type=1, -- anterior movement_type
        actions={no_action=0, up=1, down=2, left=4, right=8, jump=16, shot=32, super=64, jumpfwd=128},
        pattern= pattern.new(),
        analisis = {}, -- memoria per a guardar el resultat de l'analisis
        step_length=1,
        energia=21,
        max_energia=21,
        jump_height=0,
        max_jump_height=24,
        vmove_space = 1,
        falling = 0,
        action_event = {},
        action = 0,
        old_action = 0,
        stucked = {x=0, y=0, t=0 },
        invencible = false,
        zoom = 1,
        super_pal={[13]=13,[14]=14,[15]=15},
        super_fired = false,
        can_warp = false,
        invencible_time = 0,
        disappear_time=40,
        disappearing = false,
        --
        moure = noop,
        draw=imp.draw,
        update=imp.update_normal,
        reduce_timers = imp.reduce_timers,
        reset_timer = imp.reset_timer,
        analyze_env = imp.analyze_env,
        choose_action = imp.choose_action,
        controller_input = imp.controller_input,
        fight = imp.fight,
        create_hot_points = imp.create_hot_points,
        load_pattern_paths = imp.load_pattern_paths,
        pattern_movement = imp.pattern_movement,
        next_action = imp.next_action,
        do_jump = imp.do_jump,
        jump = imp.jump,
        advance = imp.advance,
        land = imp.land,
        next_frame = imp.staying_next_frame,
        do_flip = imp.do_flip,
        update_action_events = imp.update_action_events,
        check_action_event = imp.check_action_event, 
        add_action_event = imp.add_action_event,
        shot = imp.shot,
        pattern_recovery = imp.pattern_recovery,
        super = imp.super,
        super_movement = imp.super_movement,
        hit = imp.hit,
        warp_update = imp.warp_update,
        choose_mood = imp.choose_mood,
        -- debug
        _moure = imp._moure,
        _pause = false,
        _step = imp._step,
    }
end

function imp:draw() --OK
    self.pattern:draw()
    local scr_x, scr_y = viewp:screen_coords( self.x, self.y )
    
    if self.warping then
        actor_warp_draw(self)
    elseif self.moure==imp.state_super then
        -- Modo super
        -- rotar paleta
        for col=13,15 do
            local newc = self.super_pal[col]
            if self.timers.super_wait%6 == 0 then
                newc = newc+1
                if newc>15 then newc=13 end
            end
            pal.subpal(col,newc)
            self.super_pal[col]=newc
        end
        -- pintar
        draw.surf(96, 32, self.w, self.h, 
                  scr_x, scr_y-self.h*(self.zoom-1), 
                  self.w*self.zoom, self.h*self.zoom, 
                  self.flip)
        -- restaurar paleta
        for col=13,15 do pal.subpal(col) end
    else
    -- Modo normal
        if self.invencible then
            pal.subpal(5,1)
        end
        draw.surf((self.frame&7)*self.w, (self.frame>>cxr2)*self.h, self.w, self.h, scr_x, scr_y, self.w, self.h, self.flip)
        pal.subpal(5)
    end
    
    -- draw.rect(scr_x+self.bb.x,scr_y+self.bb.h, self.bb.w, 8,2)
    -- local wtx, wty = coords.room_to_world(hab1, tx1, ty1)
    -- local scr_x, scr_y = viewp:screen_coords( wtx, wty )
    -- draw.rect(scr_x, scr_y, 16, 16, 15)
    -- local wtx, wty = coords.room_to_world(hab2, tx2, ty2)
    -- local scr_x, scr_y = viewp:screen_coords( wtx, wty )
    -- draw.rect(scr_x, scr_y, 16, 16, 13)
end

function imp:hit() -- OK
    if self.energia <= 0 then return end
if DEBUG_FN_NAME then print_dbg("hit") end
    if not self.invencible then
        self.energia = self.energia -1
        if distancia(self, abad)<50 and self.invencible_time<=0 then
            self.invencible = true
            self.invencible_time = 50
        end
    end
    
    if self.energia<=1 then self.can_warp=true end
    
    if self.energia <= 0 then 
        self.energia = 0
        -- self.enabled = false
        if self.can_warp then self.warping=true end
        self.shrink=1
        self.angle=0
        self.disappearing=true
        -- calcular velocitat per al warp
        local warp_time = self.disappear_time/3
        self.d_angle = 720 / warp_time; -- 720 = 2 voltes
        self.d_shrink = self.shrink / warp_time
        
        self.update=imp.update_disappearing
    end
end

function imp:_step( value, foo )
    foo = foo or "anonymous"
    -- print_dbg(foo)
    self.step = value
end


function imp:warp_update()
print("warp_update")
    -- warp, wait, respawn
    if self.warping then
        if self.step<self.disappear_time then
            self.shrink=self.shrink-self.d_shrink
            self.angle=self.angle+self.d_angle
            if self.angle>=360 then self.angle = self.angle % 360 end
            if self.shrink<=0 then 
                -- Ha desaparegut
                self.shrink=1
                self.d_shrink=1
                self.angle=0
                self.d_angle=1
                self.warping = false
                self.frame = -1
                -- self.can_warp = false
                -- self.step = 0
            end
        end
    else
        self.can_warp = false
    end
end


function imp:update_disappearing()
-- print_dbg(">>>>>>>>>> update_disappearing "..self.wait)
    -- if not self.enabled then return end
    
    -- Que pasa quan "mor"
    self.enabled = false
    local step_time = self.disappear_time
    if self.can_warp then 
        step_time = self.disappear_time/2; -- warp, wait
    end
    
    self.wait=self.wait+1
    
    if self.wait>=6 then
        self.wait=0
        self.step=self.step+1
        -- self:_step(self.step+1,"update_hit")

        if self.can_warp then
            self:warp_update()
            self.moure = self.noop
        else
            if not self.warping then
                print("END BOSS")
                -- stages.stage1_boss_finished()
                states:executar("stage1_boss_outro", true)
            end
        end
    end
    
end

function imp:update_normal()
-- print("")
-- print("TARGET TIMER= "..self.timers.target)

    if not self._pause then 
-- print("-------------------------------------------------")
-- if self.mood~="stop" then print("TARGET= "..self.pattern:target()) end
-- if self.mood~="stop" then print("ACT/EV= "..self.pattern:action().." / "..self.pattern:event()) end
-- if self.mood~="stop" then print("ACTIONS= "..#self.pattern:actions()) end
        self:reduce_timers()
        
        -- Ajustar mood (emocions)
        self:choose_mood()
    
        -- self:choose_target()
        
        -- analisis
        self:analyze_env()
        self:update_action_events()
        if self.analisis.target_reached then self:reset_timer("target") end
-- print_analisis()
        
        -- decisio
        self:choose_action()
        -- local str_aev = ""
        -- for k,v in pairs(self.action_event) do str_aev=str_aev..", "..v end
-- if self.mood~="stop" then print("TARGET= "..self.pattern:target().." ACTION= "..self.pattern:action().." ACTION EVENT= "..self.pattern:event().." / "..str_aev) end
        -- moviment
        self:controller_input()
        self:moure()
        
        -- Que no conte si està en modo Super
        if self.move_type ~= self.move_types.super then 
            if self.stucked.x == self.x and self.stucked.y == self.y then
                self.stucked.t = self.stucked.t + 1
            else
                self.stucked.x = self.x
                self.stucked.y = self.y
                self.stucked.t = 0
            end
        end
        
        -- Logica per a selecció de frame
        self:next_frame()
        -- colisions en personatges
        -- self:colisions()
    end
        
end

require "imp3-functions"