-- global temp. variables for the polyfiller
local tmp_left1=  vec(0,0,0,0,0,0,0,0) -- x,y,z, u1,v1, u2,v2, dummy
local tmp_left2=  vec(0,0,0,0,0,0,0,0)
local tmp_right1= vec(0,0,0,0,0,0,0,0)
local tmp_right2= vec(0,0,0,0,0,0,0,0)
local tmp_vtx=    vec(0,0,0,0,0,0,0,0) 

local temp_u= userdata("i32", 256, 1)
local temp_v= userdata("i32", 256, 1)
local temp_ux= userdata("i16", 256, 1)
local temp_vx= userdata("i16", 256, 1)
local temp_z= userdata("i32", 256, 1)
local temp_index= userdata("i32", 256, 1)
local temp2x2= userdata("i16", 512, 1)
local temp_a= userdata("i16", 512, 1)
local temp_b= userdata("i16", 512, 1)
local temp_c= userdata("i16", 512, 1)

-- interpolate across scanlines:
--                   ? x1  y x2  y u1 v1 u2 v2
local tmp_span=  vec(1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0x400)
local tmp_delta= vec(0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0)

-- "vtx" is a table with vertices {x,y,u,v} starting at 1
function lighttunnel_draw_polygon(poly, startidx, vtxcount, texture)

    local vtxcount8= vtxcount<<3
--    local du, dv= calc_delta_uv(vtx[1], vtx[2], vtx[3]) 

    -- find bounding rect (min/max x/y)
--    local minx_vtx = 1
--    local maxx_vtx = 1

    local idx= startidx/(8*4)
    local min_y = poly:get(1,idx)
    local max_y = min_y
    local miny_idx = 0
    for n=8,vtxcount8-1,8 do
        local y= poly:get(n+1,idx)
        if (y < min_y) then 
            min_y,miny_idx= y,n
        elseif (y > max_y) then
            max_y= y 
        end
    end

    -- check if bounding rect is completely outside of the screen
--    if (min_x >= 128) then return end   -- right
--    if (max_x <= 0)    then return end   -- left
    if (min_y >= 270) then return end   -- bottom
    if (max_y <= 0)   then return end   -- top
    if (max_y > 270) max_y= 270


    -- top and bottom scanline (in pixel coordinates)
    -- round to next integer y
    min_y += (-min_y)%1.0 -- ceil( min_y )
    max_y += (-max_y)%1.0 -- ceil( max_y )

    -- left and right side start at the top vertex
    local left_idx, right_idx, left_height, right_height = miny_idx, miny_idx, 0, 0
    local left_vtx1,left_vtx2, right_vtx1, right_vtx2= tmp_left1, tmp_left2, tmp_right1, tmp_right2

    -- TODO: do we really need to copy?...
    right_vtx2:copy(poly, true, startidx+right_idx, 0, 8)
    left_vtx2:copy( poly, true, startidx+left_idx,  0, 8)

    local span,delta,tmp,_tline3d= tmp_span,tmp_delta,tmp_vtx,tline3d

    span:set( 2,  min_y,  0, min_y)

    -- without loop below we are at 29%

    -- scan left & right polygon edge from top to bottom
    while (min_y<max_y) do
        -- reached end of right slope -> calculate new delta
        -- there can be several vertices in the same scanline
        -- so loop until a segment with a non-zero height was reached
        while (right_height <= 0) do

            -- trace the next edge v1 .. v2
            right_vtx2,right_vtx1= right_vtx1,right_vtx2

            -- go to next vertex: right side is vtx-- because vertex winding is counterclockwise
--            if (right_idx > 0) then right_idx-=1 else right_idx= vtxcount-1 end
--            right_idx= right_idx > 0 and right_idx-1 or vtxcount-1
            right_idx= (right_idx+vtxcount8-8) % vtxcount8

            -- TODO: do we really need to copy?...
            right_vtx2:copy(poly, true, startidx+right_idx, 0, 8)    -- tmp= v2
            right_vtx2:sub(right_vtx1, tmp)    -- tmp= v2-v1

            right_height= tmp[7]    -- ceil(v2.y) - ceil(v1.y)
            tmp:div(tmp.y, true)   -- tmp/= v2.y - v1.y

            delta:set(3, tmp[0])         -- dx
            delta:set(7, tmp[3], tmp[4]) -- du,dv
            delta:set(10, tmp[2])        -- dz

            -- subpixel correction, prestep to next integer y
            tmp:mul( (-right_vtx1.y) % 1.0, true)
            tmp:add(right_vtx1, true)

            span:set(3, tmp[0])  -- right x
            span:set(7, tmp[3], tmp[4]) -- right u,v
            span:set(10, tmp[2])
        end

        -- reached end of left slope -> calculate new delta
        -- (same as above)
        while (left_height <= 0) do

            -- trace the next edge v1 .. v2
            left_vtx2,left_vtx1= left_vtx1,left_vtx2

            -- go to next vertex: left side is vtx++ because vertex winding is counterclockwise
--            if (left_idx < vtxcount-1) then left_idx+=1 else left_idx= 0 end
--            left_idx= left_idx < vtxcount-1 and left_idx+1 or 0
            left_idx= (left_idx+8) % vtxcount8

            left_vtx2:copy(poly, true, startidx+left_idx, 0, 8)    -- tmp= v2
            left_vtx2:sub(left_vtx1, tmp)  -- tmp= v2-v1

            left_height= tmp[7]      -- ceil(v2.y) - ceil(v1.y)
            tmp:div(tmp.y, true)

            delta:set(1, tmp[0])        -- dx
            delta:set(5, tmp[3],tmp[4]) -- du,dv
            delta:set(9, tmp[2])        -- dz

            -- prestep to next integer y (v1 + stepy*delta)
            tmp:mul((-left_vtx1.y) % 1.0, true) -- delta*= stepy
            tmp:add(left_vtx1, true)           -- +v1

            span:set(1, tmp[0])        -- left x
            span:set(5, tmp[3],tmp[4]) -- left u/v
            span:set(9, tmp[2])        -- ldeft z
        end

        local cnt= min(left_height, right_height)

--        if (cnt==1) return
        for y=1,cnt do
          local row= flr(span[2])
          if (row>=0) then
            local x1,x2= span[1], span[3]

            local ix1,ix2= ceil(x1),ceil(x2)
            if (ix1<0) ix1=0
            if (ix2>240) ix2=240
            
            local len= ix2-ix1
            if (len>0) then
                local w= x2-x1
                local du= (span[7]-span[5])/w
                local dv= (span[8]-span[6])/w
                local dz= (span[10]-span[9])/w

                local stepx= ix1-x1
                local u= span[5] + stepx*du
                local v= span[6] + stepx*dv
                local z= span[9] + stepx*dz

                local scale= (1<<27)
                temp_u[0]= u*scale
                temp_u[len]= (u+du*len)*scale
                temp_u:lerp(0,len)

                temp_v[0]= v*scale
                temp_v[len]= (v+dv*len)*scale
                temp_v:lerp(0,len)

                temp_z[0]= z*scale/256
                temp_z[len]= (z+dz*len)*scale/256
                temp_z:lerp(0,len)

--                temp_u:shl(3,      true, 0,0, len, 0,0,1)
                temp_u:div(temp_z, true, 0,0, len, 0,0,1)
--                temp_v:shl(3,      true, 0,0, len, 0,0,1)
                temp_v:div(temp_z, true, 0,0, len, 0,0,1)
                                
                temp_index:mutate("u8")
                temp_u:mutate("u8")
                temp_v:mutate("u8")
                temp_index:copy(temp_u, true, 1,0, 1, 4, 4, len)
                temp_index:copy(temp_v, true, 1,1, 1, 4, 4, len)
                temp_index:mutate("i32")
                temp_u:mutate("i32")
                temp_v:mutate("i32")

                temp_u:band(255, true, 0,0, len, 0,0,1)
                temp_v:band(255, true, 0,0, len, 0,0,1)

                texture:take(temp_index, temp2x2, 0,0, 2, 1,2, len)

--                temp_u:mutate("i16")
--                temp_v:mutate("i16")


--[[
 B1 A1 | B2 A2 || B2 A2 | B3 A3 || B3 A3 | B4 A4 || B4 A4 | B5 A5 || ...
    A1      A2 ||    A2 |    A3 ||    A3 |    A4 ||    A4 |    A5
 A1-A2 | 
]]

                temp2x2:mutate("u8")
                temp_a:mutate("u8")
                temp_b:mutate("u8")
                temp_a:copy(temp2x2, true, 0,0, 1, 4,4, len)
                temp_a:copy(temp2x2, true, 1,2, 1, 4,4, len)
                temp_b:copy(temp2x2, true, 2,0, 1, 4,4, len)
                temp_b:copy(temp2x2, true, 3,2, 1, 4,4, len)
                temp2x2:mutate("i16")
                temp_a:mutate("i16")
                temp_b:mutate("i16")
                temp_b:sub(temp_a, true, 0,0, len*2, 0,0,1)

                temp_b:mutate("i32")
                temp_b:mul(temp_u, true, 0,0, len)
                temp_b:mutate("i16")
                temp_b:shr(8,     true, 0,0, len*2)
                temp_b:add(temp_a, true, 0,0, len*2)

                
                temp_a:copy(temp_b, true, 0,0, 1, 2,1, len)
                temp_b:copy(temp_b, true, 1,0, 1, 2,1, len)

                -- filtering in y:
                -- temp2[x] = temp1[x] + ((temp2[x]-temp1[x]) * temp3[x] >> 8)

                temp_v:mutate("i16")
                temp_b:sub(temp_a, true, 0,0, len)
                temp_b:mul(temp_v, true, 0,0, 1, 2,1, len)
                temp_b:shr(8,      true, 0,0, len)
                temp_b:add(temp_a, true, 0,0, len)
                temp_v:mutate("i32")


                temp_b:mutate("u8")
                get_display():copy(temp_b, true, 0,row*240+ix1, 1, 2,1, len)
                temp_b:mutate("i16")

                temp_u:mutate("i32")
                temp_v:mutate("i32")
              end
          end

           span:add(delta, true)  -- 78% -> 109%
        end
--        count_tline+= cnt

        -- deltas for right side
        right_height-=cnt
        left_height-=cnt

        min_y+=cnt
    end
end



-- "vtx" is a table with vertices {x,y,u,v} starting at 1
function lighttunnel_draw_polygon2(poly, startidx, vtxcount, texture)

    local vtxcount8= vtxcount<<3
--    local du, dv= calc_delta_uv(vtx[1], vtx[2], vtx[3]) 

    -- find bounding rect (min/max x/y)
--    local minx_vtx = 1
--    local maxx_vtx = 1

    local idx= startidx/(8*4)
    local min_y = poly:get(1,idx)
    local max_y = min_y
    local miny_idx = 0
    for n=8,vtxcount8-1,8 do
        local y= poly:get(n+1,idx)
        if (y < min_y) then 
            min_y,miny_idx= y,n
        elseif (y > max_y) then
            max_y= y 
        end
    end

    -- check if bounding rect is completely outside of the screen
--    if (min_x >= 128) then return end   -- right
--    if (max_x <= 0)    then return end   -- left
    if (min_y >= 270) then return end   -- bottom
    if (max_y <= 0)   then return end   -- top
    if (max_y > 270) max_y= 270


    -- top and bottom scanline (in pixel coordinates)
    -- round to next integer y
    min_y += (-min_y)%1.0 -- ceil( min_y )
    max_y += (-max_y)%1.0 -- ceil( max_y )

    -- left and right side start at the top vertex
    local left_idx, right_idx, left_height, right_height = miny_idx, miny_idx, 0, 0
    local left_vtx1,left_vtx2, right_vtx1, right_vtx2= tmp_left1, tmp_left2, tmp_right1, tmp_right2

    -- TODO: do we really need to copy?...
    right_vtx2:copy(poly, true, startidx+right_idx, 0, 8)
    left_vtx2:copy( poly, true, startidx+left_idx,  0, 8)

    local span,delta,tmp,_tline3d= tmp_span,tmp_delta,tmp_vtx,tline3d

    span:set( 2,  min_y,  0, min_y)

    -- without loop below we are at 29%

    -- scan left & right polygon edge from top to bottom
    while (min_y<max_y) do
        -- reached end of right slope -> calculate new delta
        -- there can be several vertices in the same scanline
        -- so loop until a segment with a non-zero height was reached
        while (right_height <= 0) do

            -- trace the next edge v1 .. v2
            right_vtx2,right_vtx1= right_vtx1,right_vtx2

            -- go to next vertex: right side is vtx-- because vertex winding is counterclockwise
--            if (right_idx > 0) then right_idx-=1 else right_idx= vtxcount-1 end
--            right_idx= right_idx > 0 and right_idx-1 or vtxcount-1
            right_idx= (right_idx+vtxcount8-8) % vtxcount8

            -- TODO: do we really need to copy?...
            right_vtx2:copy(poly, true, startidx+right_idx, 0, 8)    -- tmp= v2
            right_vtx2:sub(right_vtx1, tmp)    -- tmp= v2-v1

            right_height= tmp[7]    -- ceil(v2.y) - ceil(v1.y)
            tmp:div(tmp.y, true)   -- tmp/= v2.y - v1.y

            delta:set(3, tmp[0])         -- dx
            delta:set(7, tmp[3], tmp[4]) -- du,dv
            delta:set(10, tmp[2])        -- dz

            -- subpixel correction, prestep to next integer y
            tmp:mul( (-right_vtx1.y) % 1.0, true)
            tmp:add(right_vtx1, true)

            span:set(3, tmp[0])  -- right x
            span:set(7, tmp[3], tmp[4]) -- right u,v
            span:set(10, tmp[2])
        end

        -- reached end of left slope -> calculate new delta
        -- (same as above)
        while (left_height <= 0) do

            -- trace the next edge v1 .. v2
            left_vtx2,left_vtx1= left_vtx1,left_vtx2

            -- go to next vertex: left side is vtx++ because vertex winding is counterclockwise
--            if (left_idx < vtxcount-1) then left_idx+=1 else left_idx= 0 end
--            left_idx= left_idx < vtxcount-1 and left_idx+1 or 0
            left_idx= (left_idx+8) % vtxcount8

            left_vtx2:copy(poly, true, startidx+left_idx, 0, 8)    -- tmp= v2
            left_vtx2:sub(left_vtx1, tmp)  -- tmp= v2-v1

            left_height= tmp[7]      -- ceil(v2.y) - ceil(v1.y)
            tmp:div(tmp.y, true)

            delta:set(1, tmp[0])        -- dx
            delta:set(5, tmp[3],tmp[4]) -- du,dv
            delta:set(9, tmp[2])        -- dz

            -- prestep to next integer y (v1 + stepy*delta)
            tmp:mul((-left_vtx1.y) % 1.0, true) -- delta*= stepy
            tmp:add(left_vtx1, true)           -- +v1

            span:set(1, tmp[0])        -- left x
            span:set(5, tmp[3],tmp[4]) -- left u/v
            span:set(9, tmp[2])        -- ldeft z
        end

        local cnt= min(left_height, right_height)

--        if (cnt==1) return
        for y=1,cnt do
          local row= flr(span[2])
          if (row>=0) then
            local x1,x2= span[1], span[3]

            local ix1,ix2= ceil(x1),ceil(x2)
            if (ix1<0) ix1=0
            if (ix2>240) ix2=240
            
            local len= ix2-ix1
            if (len>0) then
                local w= x2-x1
                local du= (span[7]-span[5])/w
                local dv= (span[8]-span[6])/w
                local dz= (span[10]-span[9])/w

                local stepx= ix1-x1
                local u= span[5] + stepx*du
                local v= span[6] + stepx*dv
                local z= span[9] + stepx*dz

                local scale= (1<<27)
                temp_u[0]= u*scale
                temp_u[len]= (u+du*len)*scale
                temp_u:lerp(0,len)

                temp_v[0]= v*scale
                temp_v[len]= (v+dv*len)*scale
                temp_v:lerp(0,len)

                temp_z[0]= z*scale/256
                temp_z[len]= (z+dz*len)*scale/256
                temp_z:lerp(0,len)

--                temp_u:shl(3,      true, 0,0, len, 0,0,1)
                temp_u:div(temp_z, true, 0,0, len, 0,0,1)
--                temp_v:shl(3,      true, 0,0, len, 0,0,1)
                temp_v:div(temp_z, true, 0,0, len, 0,0,1)
                                
                temp_index:mutate("u8")
                temp_u:mutate("u8")
                temp_v:mutate("u8")
                temp_index:copy(temp_u, true, 1,0, 1, 4, 4, len)
                temp_index:copy(temp_v, true, 1,1, 1, 4, 4, len)
                temp_index:mutate("i32")
                temp_u:mutate("i32")
                temp_v:mutate("i32")

                temp_u:band(255, true, 0,0, len, 0,0,1)
                temp_v:band(255, true, 0,0, len, 0,0,1)

                texture:take(temp_index, temp2x2, 0,0, 2, 1,2, len)

--                temp_u:mutate("i16")
--                temp_v:mutate("i16")


--[[
 B1 A1 | B2 A2 || B2 A2 | B3 A3 || B3 A3 | B4 A4 || B4 A4 | B5 A5 || ...
    A1      A2 ||    A2 |    A3 ||    A3 |    A4 ||    A4 |    A5
 A1-A2 | 
]]

                temp2x2:mutate("u8")
                temp_a:mutate("u8")
                temp_b:mutate("u8")
                temp_a:copy(temp2x2, true, 0,0, 1, 4,4, len)
                temp_a:copy(temp2x2, true, 1,2, 1, 4,4, len)
                temp_b:copy(temp2x2, true, 2,0, 1, 4,4, len)
                temp_b:copy(temp2x2, true, 3,2, 1, 4,4, len)
                temp2x2:mutate("i16")
                temp_a:mutate("i16")
                temp_b:mutate("i16")
                temp_b:sub(temp_a, true, 0,0, len*2, 0,0,1)

                temp_b:mutate("i32")
                temp_b:mul(temp_u, true, 0,0, len)
                temp_b:mutate("i16")
                temp_b:shr(8,     true, 0,0, len*2)
                temp_b:add(temp_a, true, 0,0, len*2)

                
                temp_a:copy(temp_b, true, 0,0, 1, 2,1, len)
                temp_b:copy(temp_b, true, 1,0, 1, 2,1, len)

                -- filtering in y:
                -- temp2[x] = temp1[x] + ((temp2[x]-temp1[x]) * temp3[x] >> 8)

                temp_v:mutate("i16")
                temp_b:sub(temp_a, true, 0,0, len)
                temp_b:mul(temp_v, true, 0,0, 1, 2,1, len)
                temp_b:shr(8,      true, 0,0, len)
                temp_b:add(temp_a, true, 0,0, len)
                temp_v:mutate("i32")


                temp_b:mutate("u8")
                get_display():shr(2, true, 0,row*240+ix1, len, 0,0,1)
                get_display():add(temp_b, true, 0,row*240+ix1, 1, 2,1, len)
                temp_b:mutate("i16")

                temp_u:mutate("i32")
                temp_v:mutate("i32")
              end
          end

           span:add(delta, true)  -- 78% -> 109%
        end
--        count_tline+= cnt

        -- deltas for right side
        right_height-=cnt
        left_height-=cnt

        min_y+=cnt
    end
end
