-- view, backgrounds, tiles, sprites
require("util")

class("Point")
function Point:init(x,y)
    self.x = x
    self.y = y
end

class("Rect")

function Rect:init(x,y,w,h)
    if w == nil or h == nil then
        self.w = x
        self.h = y
        self.x = 0
        self.y = 0
        return
    end
    self.x = x
    self.y = y
    self.w = w
    self.h = h
end

function Rect:top(v) if v ~= nil then self.y = v end return self.y end
function Rect:right(v) if v ~= nil then self.x = v-self.w end return self.x+self.w end
function Rect:bottom(v) if v ~= nil then self.y = v-self.h end return self.y+self.h end
function Rect:left(v) if v ~= nil then self.x = v end return self.x end
function Rect:centerx(v) if v ~= nil then self.x = v-self.w/2 end return self.x+self.w/2 end
function Rect:centery(v) if v ~= nil then self.y = v-self.h/2 end return self.y+self.h/2 end
function Rect:center(v) if v ~= nil then self.x = v.x-self.w/2; self.y = v.y-self.h/2; end return {x=self.x+self.w/2,y=self.y+self.h/2} end
    
function Rect:clamp(r)
    local tmp
    self.x = max(self.x,r.x)
    self.y = max(self.y,r.y)
    tmp = min(self.x+self.w,r.x+r.w)
    self.x = tmp-self.w
    tmp = min(self.y+self.h,r.y+r.h)
    self.y = tmp-self.h
    tmp = self.w - r.w
    if tmp > 0 then self.x = self.x+tmp/2 end
    tmp = self.h - r.h
    if tmp > 0 then self.y = self.y+tmp/2 end
end

function Rect:collidepoint(p)
    return (self.x <= p.x and self.y <= p.y and (self.x+self.w) > p.x and (self.y+self.h) > p.y)
end

-- function Rect:colliderect(r)
--     return (not (r:right() <= self:left() or self:right() <= r:left() or r:bottom() <= self:top() or self:bottom() <= r:top()))
-- end
function Rect:colliderect(r)
    return ((r.x+r.w) > (self.x) and (self.x+self.w) > (r.x) and (r.y+r.h) > (self.y) and (self.y+self.h) > (r.y))
end

function Rect:copy()
    return Rect(self.x,self.y,self.w,self.h)
end

class("Sprite")

function Sprite:init(image,rect,shape)
    self.image = image
    self.shape = shape or Rect(0,0,rect.w,rect.h)
    self.rect = rect
    self.angle = 0.0
    self.color = 0xffffff
    self.alpha = 1.0
    self.mode = 0
end

function Sprite:draw()
    cuz.image_draw(self.image,
        round(self.rect.x+self.shape.x),round(self.rect.y+self.shape.y),
        self.shape.w,self.shape.h)
end
function Sprite:draw_shadow()
    cuz.image_draw(self.shadow_image,
        round(self.rect.x+self.shape.x),round(self.rect.y+self.shape.y),
        self.shape.w,self.shape.h)
end

class("Parts")

function Parts:init()
    self.items = {}
end

function Parts:draw()
    for _,e in ipairs(self.items) do
        cuz.rect_draw(e.color,e.x,e.y,e.w,e.h)
    end
end

function Part(color,x,y,size,vx,vy,t)
    return {color=color,x=x-size/2,y=y-size/2,w=size,h=size,vx=vx,vy=vy,t=t}
end

function Parts:add(e)
    self.items[#self.items+1] = e
end

function Parts:loop(scene,t)
    local r = {}
    for _,e in ipairs(self.items) do
        e.x = e.x + e.vx * t
        e.y = e.y + e.vy * t
        e.t = e.t - t
        if e.t > 0 then
            r[#r+1] = e
        end
    end
    self.items = r
end

function Parts:draw_shadow()
end


class("Tile")

function Tile:init(image)
    self.image = image
end

class("Scene")

function Scene:init(view,bounds)
    self.items = {}
    self.view = view
    self.bounds = bounds
    self._dirty = false
end

function Scene:draw()
    self.view.x = round(self.view.x)
    self.view.y = round(self.view.y)
    if self.bounds ~= nil then self.view:clamp(self.bounds) end
    cuz.view_set(self.view.x,self.view.y,self.view.w,self.view.h)
    table.sort(self.items,function (a,b) return a.zindex<b.zindex end)
    for n,obj in ipairs(self.items) do
        if obj._live then obj:draw(self) end
    end
    self:_letterbox()
end

function Scene:draw_shadow()
    self.view.x = round(self.view.x)
    self.view.y = round(self.view.y)
    if self.bounds ~= nil then self.view:clamp(self.bounds) end
    local z = 5
    cuz.view_set(self.view.x-z,self.view.y-z,self.view.w,self.view.h)
    cuz.image_draw('shadow',self.view.x-z,self.view.y-z-9999,z,self.view.h+9999*2)
    table.sort(self.items,function (a,b) return a.zindex<b.zindex end)
    for n,obj in ipairs(self.items) do
        if obj._live then obj:draw_shadow(self) end
    end
end

function Scene:_letterbox()
	local c = 0x222222 -- 0x333333
    local z = 99999
    local x = self.view.x
    local y = self.view.y
     -- cuz.rect_draw(c,x+-z,y+-z,z+self.view.w+z,z)
    cuz.rect_draw(0,x+self.view.w,y+-z,z,z+self.view.h+z)
    cuz.rect_draw(c,2+x+self.view.w,y+-z,z,z+self.view.h+z)
 --    cuz.rect_draw(c,x+-z,y+self.view.h,z+self.view.w+z,z)
    cuz.rect_draw(0,x+-z,y+-z,z,z+self.view.h+z)
    cuz.rect_draw(c,-2+x+-z,y+-z,z,z+self.view.h+z)
end

function Scene:add(obj,name)
    obj.name = name
    if obj.zindex == nil then obj.zindex = 0 end
    obj._live = true
    self.items[#self.items+1] = obj
end

function Scene:remove(obj)
    obj._live = false
    self._dirty = true
end

function Scene:_sweep()
    if not self._dirty then return end
    local r = {}
    for n,obj in ipairs(self.items) do
        if obj._live then r[#r+1] = obj end
    end
    self.items = r
    self._dirty = false
end

function Scene:find(name)
    for n,obj in ipairs(self.items) do
        if obj._live and obj.name == name then return obj end
    end
end

function Scene:loop(t)
    for n,obj in ipairs(self.items) do
        if obj._live and obj:method("loop") then obj:loop(self,t) end
    end
    self:_sweep()
end

function Scene:event(e)
    for n,obj in ipairs(self.items) do
        if obj._live and obj:method("event") then obj:event(self,e) end
    end
    self:_sweep()
end

class("Tilemap")

function Tilemap:init(tiles,data,tile_size,rect)
    self.tiles = tiles
    self.data = data
    self.tile_size = tile_size
    self.rect = rect or Rect(0,0,0,0)
--     for y=1,rect.h do
--         local row = {}
--         self.data[y] = row
--         for x=1,rect.w do
--             row[x] = 0
--         end
--     end
end

function Tilemap:set(x,y,v)
    x = round(x)
    y = round(y)
    local w = #self.data[1]
    local h = #self.data
    if x < 1 or y < 1 or x > w or y > h then return end
    self.data[y][x] = v
end

function Tilemap:get(x,y)
    x = round(x)
    y = round(y)
    local w = #self.data[1]
    local h = #self.data
    if x < 1 or y < 1 or x > w or y > h then return -1 end
    return self.data[y][x]
end

function Tilemap:draw(scene)
    local tw = self.tile_size.w
    local th = self.tile_size.h
    local rect = self.rect
    local x_min = max(1,min(#self.data[1],math.floor((scene.view.x-rect.x) / tw)))
    local x_max = max(1,min(#self.data[1],math.ceil((scene.view:right()-rect.x) / tw)))
    local y_min = max(1,min(#self.data,math.floor((scene.view.y-rect.y) / th)))
    local y_max = max(1,min(#self.data,math.ceil((scene.view:bottom()-rect.y) / th)))
--     print("view: x="..x_min.."..."..x_max.." y="..y_min.."..."..y_max)

    if GAME.scene.wtv == self then 
        x_max = max(2,min(#self.data[1]-1,x_max))
        x_min = max(2,min(#self.data[1]-1,x_min))
    end


    for y=y_min,y_max do
        local row = self.data[y]
        for x=x_min,x_max do
            local n = row[x]
            if n ~= 0 then
                local tile = self.tiles[n]
                -- print('y:'..y.." x:"..x)
                local z = GAME.pulse[y][x]
                if z > 1 then z = math.sqrt(z) end
                -- cuz.image_draw(tile.image,x*tw+rect.x,y*th+rect.y,tw,th)
                cuz.image_draw(tile.image,x*tw+rect.x-z,y*th+rect.y-z,tw+z*2,th+z*2)
            end
        end
    end

    if GAME.scene.wtv == self then return end

    if ((GAME.map) % 5) == 0 then
        local z = -2
        local t1 = GAME.TILE_SILVER
        local t2 = GAME.TILE_SILVER+1
        local t3 = GAME.TILE_GEM
        local t4 = GAME.TILE_GEM+1
        local t5 = GAME.TILE_GEM+2
        for y=y_min,y_max do
            local row = self.data[y]
            for x=x_min,x_max do
                local n = row[x]
                if n ~= 0 and n ~= t1 and n ~= t2 and n ~= t3 and n~= t4 and n~= t5 then
                    cuz.image_draw("shadow",x*tw+rect.x-z,y*th+rect.y-z,tw+z*2,th+z*2)
                    -- cuz.rect_draw(0,x*tw+rect.x-z,y*th+rect.y-z,tw+z*2,th+z*2)
                end
            end
        end
    end


end

function Tilemap:draw_shadow(scene)
    local tw = self.tile_size.w
    local th = self.tile_size.h
    local rect = self.rect
    local x_min = max(1,min(#self.data[1],math.floor((scene.view.x-rect.x) / tw)))
    local x_max = max(1,min(#self.data[1],math.ceil((scene.view:right()-rect.x) / tw)))
    local y_min = max(1,min(#self.data,math.floor((scene.view.y-rect.y) / th)))
    local y_max = max(1,min(#self.data,math.ceil((scene.view:bottom()-rect.y) / th)))
--     print("view: x="..x_min.."..."..x_max.." y="..y_min.."..."..y_max)

    if GAME.scene.wtv == self then 
        x_max = max(2,min(#self.data[1]-1,x_max))
        x_min = max(2,min(#self.data[1]-1,x_min))
    end
    
    for y=y_min,y_max do
        local row = self.data[y]
        for x=x_min,x_max do
            local n = row[x]
            if n ~= 0 then
                local tile = self.tiles[n]
                cuz.image_draw('shadow',x*tw+rect.x,y*th+rect.y,tw,th)
            end
        end
    end
end

class("Background")

function Background:init(image,rect)
    self.image = image
    self.rect = rect
end

function Background:draw(scene)
    -- background size
    local w = self.rect.w
    local h = self.rect.h
    -- background
    local bw = min(scene.bounds.w,w)
    local bh = min(scene.bounds.h,h)
    -- background shift
    local sw = max(0,bw-scene.view.w)
    local sh = max(0,bh-scene.view.h)
    -- view corner
    local vx = scene.view.x - scene.bounds.x
    local vy = scene.view.y - scene.bounds.y
    -- view shift
    local zw = max(0,scene.bounds.w-scene.view.w)
    local zh = max(0,scene.bounds.h-scene.view.h)
    -- figure offsets
    local ox = (bw-w)/2
    local oy = (bh-h)/2
    -- figure x,y
    local x = scene.view.x - ((vx * sw) / max(1,zw)) + ox
    local y = scene.view.y - ((vy * sh) / max(1,zh)) + oy
    -- re-center for small backgrounds
    if w < scene.view.w then x = scene.view.x + (scene.view.w-w) / 2 end
    if h < scene.view.h then y = scene.view.y + (scene.view.h-h) / 2 end
    cuz.image_draw(self.image,
        round(x),round(y),
        w,h)
end

-- TEST = false
-- if TEST then
--     r = Rect(1,2,3,4)
--     print(r.x)
--     print(r:top())
--     print(r:bottom())
--     print(r:left())
--     print(r:right())
-- end
