local math = math
local additiveColorMaterial = {materialType = 11, lighting = false}
local HELPER_SPRITES = {}

function video.initHelpers()
	local f = video.getSpriteStateNames("helper.dat")
	if f then
		for index, anim in pairs(f) do
			HELPER_SPRITES[anim] = video.createSpriteState(anim, "helper.dat")
		end
	end
end

function video.renderAveragedAdditiveSpriteState(sprite, x, y, scale, angle, r, g, b, flipped)
	if not video.disableAdditive then
		video.setUseMaterialFor2d(true)
		video.setMaterial(additiveColorMaterial)
		local avg = math.max(1,(r+g+b)/255)
		video.renderSpriteState(sprite, x, y, scale, angle, 255,  r/avg, g/avg, b/avg, flipped)		
		video.setUseMaterialFor2d(false)
	end
end

function video.renderAdditiveSpriteState(sprite, x, y, scale, angle, r, g, b, flipped)
	if not video.disableAdditive then
		video.setUseMaterialFor2d(true)
		video.setMaterial(additiveColorMaterial)
		video.renderSpriteState(sprite, x, y, scale, angle, 255,  r, g, b, flipped)		
		video.setUseMaterialFor2d(false)
	end
end

function video.renderLineWithOutline(x, y, x2, y2, alpha, r, g, b, outAlpha, outR, outG, outB)
	video.renderLine(x+1,y+1,x2+1,y2+1, outAlpha, outR, outG, outB)
	video.renderLine(x-1,y-1,x2-1,y2-1, outAlpha, outR, outG, outB)
	video.renderLine(x+1,y-1,x2+1,y2-1, outAlpha, outR, outG, outB)
	video.renderLine(x-1,y+1,x2-1,y2+1, outAlpha, outR, outG, outB)
	video.renderLine(x,y,x2,y2, alpha, r, g, b)
end

function video.renderLineArrow(x,y, x2, y2, arc, size, alpha, r, g, b)
	video.renderLine(x,y, x2, y2, alpha, r, g, b)
	local ang = math.angleBetweenPoints(x,y,x2,y2)
	video.renderLine(x2, y2, x2 + math.cos(ang+math.pi+arc*0.5)*size, y2 + math.sin(ang+math.pi+arc*0.5)*size , alpha, r, g, b)
	video.renderLine(x2, y2, x2 + math.cos(ang+math.pi-arc*0.5)*size, y2 + math.sin(ang+math.pi-arc*0.5)*size , alpha, r, g, b)
end


function video.renderLinePlus(x, y, size, alpha, r, g, b)
	video.renderLine(x-size,y,x+size,y, alpha, r, g, b)
	video.renderLine(x,y-size,x,y+size, alpha, r, g, b)
end

function video.renderRectangleOutline(x, y, x2, y2, alpha, r, g, b)
	video.renderLine(x,y,x2,y, alpha, r, g, b)
	video.renderLine(x,y2,x2,y2, alpha, r, g, b)
	video.renderLine(x,y,x,y2, alpha, r, g, b)
	video.renderLine(x2,y,x2,y2, alpha, r, g, b)
end

function video.renderTextWithOutline(text, x, y, alignment, font, alpha, r, g, b, outAlpha, outR, outG, outB)
	video.renderTextSprites(text, x-1, y, alignment, font, outAlpha, outR, outG, outB)		
	video.renderTextSprites(text, x+2, y, alignment, font, outAlpha, outR, outG, outB)		
	video.renderTextSprites(text, x, y-1, alignment, font, outAlpha, outR, outG, outB)		
	video.renderTextSprites(text, x, y+1, alignment, font, outAlpha, outR, outG, outB)		
	video.renderTextSprites(text, x, y, alignment, font, alpha, r, g, b)		
end

function video.renderBeamSprite(sprite, x1, y1, x2, y2, width, alpha, r, g, b)
	local realAngle = math.angleBetweenPoints(x1, y1, x2, y2)
	local sx1, sy1 = x1 + math.cos(realAngle-math.pi*0.5)*width, y1 + math.sin(realAngle-math.pi*0.5)*width
	local sx2, sy2 = x1 + math.cos(realAngle+math.pi*0.5)*width, y1 + math.sin(realAngle+math.pi*0.5)*width
	local tx1, ty1 = x2 + math.cos(realAngle-math.pi*0.5)*width, y2 + math.sin(realAngle-math.pi*0.5)*width
	local tx2, ty2 = x2 + math.cos(realAngle+math.pi*0.5)*width, y2 + math.sin(realAngle+math.pi*0.5)*width
	video.renderSpriteStateFreeShape(sprite, sx1, sy1, tx1, ty1, sx2, sy2, tx2, ty2, alpha, r, g, b)
end

function video.renderHudSpriteState(sprite, x, y, scale, angle, alpha, r, g, b)
	video.renderSpriteState(sprite, x , y , scale, angle , alpha, r, g, b)
end

function video.renderPlus(x,y, size, thickness, alpha, r, g, b)
	video.renderRectangle(x-size/2, y-size/2*thickness, size,  size*thickness, alpha, r, g, b)
	video.renderRectangle(x-size/2*thickness, y-size/2, size*thickness, size/2-size*thickness/2, alpha, r, g, b)
	video.renderRectangle(x-size/2*thickness, y+ size*thickness/2, size*thickness, size/2-size*thickness/2, alpha, r, g, b)
end

function video.renderFrameSprite(fs, x, y, w, h, a, r, g, b, widthborder, heightborder)
	local wi,hi = widthborder or fs.width, heightborder or fs.height
	video.renderSpriteStateFreeShape(fs.topLeft, x, y, math.min(x+wi, x+w/2), y, x, math.min(y+hi, y+h/2), math.min(x+wi, x+w/2), math.min(y+hi, y+h/2), a, r, g, b)
	video.renderSpriteStateFreeShape(fs.top, math.min(x+wi, x+w/2), y, math.max(x+w-wi, x+w/2), y, math.min(x+wi, x+w/2), math.min(y+hi, y+h/2), math.max(x+w-wi, x+w/2), math.min(y+hi, y+h/2), a, r, g, b)
	video.renderSpriteStateFreeShape(fs.topRight, math.max(x+w-wi, x+w/2), y, x+w, y, math.max(x+w-wi, x+w/2), math.min(y+hi, y+h/2), x+w, math.min(y+hi, y+h/2), a, r, g, b)
	video.renderSpriteStateFreeShape(fs.left, x, math.min(y+hi, y+h/2), math.min(x+wi, x+w/2), math.min(y+hi, y+h/2), x, math.max(y+h-hi, y+h/2), math.min(x+wi, x+w/2), math.max(y+h-hi, y+h/2), a, r, g, b)
	if fs.main then
		video.renderSpriteStateFreeShape(fs.main, math.min(x+wi, x+w/2), math.min(y+hi, y+h/2), math.max(x+w-wi, x+w/2), math.min(y+hi, y+h/2), math.min(x+wi, x+w/2), math.max(y+h-hi, y+h/2), math.max(x+w-wi, x+w/2), math.max(y+h-hi, y+h/2), a, r, g, b)
	end
	video.renderSpriteStateFreeShape(fs.right, math.max(x+w-wi, x+w/2), math.min(y+hi, y+h/2), x+w, math.min(y+hi, y+h/2), math.max(x+w-wi, x+w/2), math.max(y+h-hi, y+h/2), x+w, math.max(y+h-hi, y+h/2), a, r, g, b)
	video.renderSpriteStateFreeShape(fs.bottomLeft, x, math.max(y+h-hi, y+h/2), math.min(x+wi, x+w/2),math.max(y+h-hi, y+h/2), x, y+h, math.min(x+wi, x+w/2), y+h, a, r, g, b)
	video.renderSpriteStateFreeShape(fs.bottom, math.min(x+wi, x+w/2), math.max(y+h-hi, y+h/2), math.max(x+w-wi, x+w/2), math.max(y+h-hi, y+h/2), math.min(x+wi, x+w/2), y+h, math.max(x+w-wi, x+w/2), y+h, a, r, g, b)
	video.renderSpriteStateFreeShape(fs.bottomRight, math.max(x+w-wi, x+w/2), math.max(y+h-hi, y+h/2), x+w, math.max(y+h-hi, y+h/2), math.max(x+w-wi, x+w/2), y+h, x+w, y+h, a, r, g, b)
end

function video.renderGradientUp(left, top, right, bottom, alpha, r, g, b)
	video.renderSpriteStateFreeShape(HELPER_SPRITES.gradient, left, bottom, right, bottom, left, top, right, top, alpha, r, g, b)
end

function video.renderGradientLeft(left, top, right, bottom, alpha, r, g, b)
	video.renderSpriteStateFreeShape(HELPER_SPRITES.gradient, right, top, right, bottom, left, top, left, bottom, alpha, r, g, b)
end

function video.renderGradientRight(left, top, right, bottom, alpha, r, g, b)
	video.renderSpriteStateFreeShape(HELPER_SPRITES.gradient, left, bottom, left, top, right, bottom, right, top, alpha, r, g, b)
end

function video.renderGradientDown(left, top, right, bottom, alpha, r, g, b)
	video.renderSpriteStateFreeShape(HELPER_SPRITES.gradient, left, top, right, top, left, bottom, right, bottom, alpha, r, g, b)
end

function video.renderGradientBox(left, top, right, bottom, gradientStart, gradientLength, alpha, r, g, b)
	video.renderGradientUp(left,bottom - bottom*gradientLength-gradientStart*bottom, right, bottom-gradientStart*bottom, alpha, r, g, b)
	video.renderGradientDown(left,gradientStart*bottom, right, bottom*gradientLength +gradientStart*bottom, alpha, r, g, b)
	video.renderGradientRight(gradientStart*right,top, right*gradientLength+gradientStart*right, bottom, alpha, r, g, b)
	video.renderGradientLeft(right-gradientLength*right-gradientStart*right,top, right-gradientStart*right, bottom, alpha, r, g, b)
end

function video.renderProgressBar(val, step, x, y, scale, angle, alpha, r, g, b)
	for i=1,math.ceil(val) do
		local stepAlpha = math.min(1,math.max(0,val - (i-1)))
		video.renderSpriteState(HELPER_SPRITES.hudLine, x + math.cos(angle)*(i-1)*step, y+ math.sin(angle)*(i-1)*step, scale, angle-math.pi*0.5, alpha*stepAlpha, r*stepAlpha, g*stepAlpha, b*stepAlpha)
	end
end

function video.renderCenteredProgressBar(val, gapSize, step, x, y, scale, angle, alpha, r, g, b)
	for i=1,math.ceil(val) do
		local stepAlpha = math.min(1,math.max(0,val - (i-1)))
		video.renderSpriteState(HELPER_SPRITES.hudLine, x + gapSize/2 + i*step, y, scale, angle-math.pi*0.5, alpha*stepAlpha, r*stepAlpha, g*stepAlpha, b*stepAlpha)
		video.renderSpriteState(HELPER_SPRITES.hudLine, x - gapSize/2 - i*step, y, scale, angle-math.pi*0.5, alpha*stepAlpha, r*stepAlpha, g*stepAlpha, b*stepAlpha)
	end
end

function video.renderCircle(x, y, radius, alpha, r, g, b)
	video.renderSpriteState(HELPER_SPRITES.fontCircle, x, y, radius/50, 0, alpha, r, g, b)
end

function video.renderSpriteRectangle(x, y, w, h, alpha, r, g, b)
	video.renderSpriteStateFreeShape(HELPER_SPRITES.plainRectangle, x, y, x +w, y, x, y+h, x+w, y+h, alpha, r, g, b)
end


function video.renderCircleProgressBar(val, maxSlices, radius, x, y, scale, angle, alpha, r, g, b)
	for i=0, math.min(math.ceil(val), maxSlices) do
		local a = math.pi*2/maxSlices*i + angle - math.pi*0.5
		local nx,ny = x + math.cos(a)*radius, y + math.sin(a)*radius
		local stepAlpha = math.min(1,math.max(0,val - (i-1)))
		video.renderSpriteState(HELPER_SPRITES.hudLine, nx , ny, scale, a, alpha*stepAlpha,r*stepAlpha,g*stepAlpha,b*stepAlpha)
	end
end

function video.renderCircleNotches(maxSlices, radius, x, y, angle, alpha, r, g, b, width, length)
	for i=0, maxSlices do
		local a = math.pi*2/maxSlices*i + angle
		local nx,ny = x + math.cos(a)*radius, y + math.sin(a)*radius
		video.renderSpriteLine(nx, ny, nx + math.cos(a)*length, ny + math.sin(a)*length, alpha, r, g, b, nil, nil, width)
	end
end

function video.renderCircleProcess(val, maxSlices, radius, x, y, scale, angle, alpha, r, g, b)
	for i=0, maxSlices do
		local a = math.pi*2/maxSlices*i + angle - math.pi*0.5
		local nx,ny = x + math.cos(a)*radius, y + math.sin(a)*radius
		local diff = -math.angleDifference(a, math.pi*2/maxSlices*val*maxSlices + angle - math.pi*0.5)
		local stepAlpha = math.max(0,1- math.max(0,diff/math.pi))
		video.renderSpriteState(HELPER_SPRITES.hudLine, nx , ny, scale, a, alpha*stepAlpha,r*stepAlpha,g*stepAlpha,b*stepAlpha)
	end
end

function video.renderSpriteCircleLine(x,y, radius, w,h, steps, angle, alpha, r, g, b, width, animate)
	for i=0,steps-1 do
		local a1 = -math.pi*2/steps*i + angle
		local a2 = -math.pi*2/steps*(i+1) + angle
		local an = animate and math.max(0,(steps*animate-i)/steps) or 1
		video.renderSpriteLine(x+math.cos(a1)*radius*w,y+math.sin(a1)*radius*h,x+math.cos(a2)*radius*w,y+math.sin(a2)*radius*h, an*alpha, r, g, b, nil, nil, width, 1)
	end
end

function video.renderArcLineSprite(x,y, radius, arc, steps, angle, alpha, r, g, b, lineSprite, additive, width)
	local lineSprite = lineSprite or HELPER_SPRITES.line
	for i=0,steps-1 do
		local a1 = -arc/steps*i + angle + arc*0.5
		local a2 = -arc/steps*(i+1) + angle + arc*0.5
		
		local an1 = math.abs(math.angleDifference(angle, a1))/(arc*0.5)
		local an2 = math.abs(math.angleDifference(angle, a2))/(arc*0.5)
		local an = math.min(1,math.max(0,1-math.max(an1, an2)))
		
		video.renderSpriteStateFreeShape(lineSprite,
			x+math.cos(a1)*(radius+width*0.5),
			y+math.sin(a1)*(radius+width*0.5),
			x+math.cos(a2)*(radius+width*0.5),
			y+math.sin(a2)*(radius+width*0.5),
			x+math.cos(a1)*(radius-width*0.5),
			y+math.sin(a1)*(radius-width*0.5),
			x+math.cos(a2)*(radius-width*0.5),
			y+math.sin(a2)*(radius-width*0.5),
			alpha*an, r, g, b)
	end
end

function video.renderCircleArcSprite(sprite, x,y, radius, arc, steps, angle, alpha, r, g, b, width)
	for i=0,steps-1 do
		local a1 = -arc/steps*i + angle + arc*0.5
		local a2 = -arc/steps*(i+1) + angle + arc*0.5
		
		local an1 = math.abs(math.angleDifference(angle, a1))/(arc*0.5)
		local an2 = math.abs(math.angleDifference(angle, a2))/(arc*0.5)
		local an = math.min(1,math.max(0,1-math.max(an1, an2)))
		
		video.renderSpriteStateFreeShape(sprite,
			x+math.cos(a1)*(radius+width*0.5),
			y+math.sin(a1)*(radius+width*0.5),
			x+math.cos(a2)*(radius+width*0.5),
			y+math.sin(a2)*(radius+width*0.5),
			x+math.cos(a1)*(radius-width*0.5),
			y+math.sin(a1)*(radius-width*0.5),
			x+math.cos(a2)*(radius-width*0.5),
			y+math.sin(a2)*(radius-width*0.5),
			alpha*an, r, g, b)
	end
end

function video.renderCircleSprite(sprite, x,y, radius, steps, angle, alpha, r, g, b, width)
	for i=0,steps-1 do
		local a1 = -math.pi*2/steps*i + angle
		local a2 = -math.pi*2/steps*(i+1) + angle
		video.renderSpriteStateFreeShape(sprite,
			x+math.cos(a1)*(radius+width*0.5),
			y+math.sin(a1)*(radius+width*0.5),
			x+math.cos(a2)*(radius+width*0.5),
			y+math.sin(a2)*(radius+width*0.5),
			x+math.cos(a1)*(radius-width*0.5),
			y+math.sin(a1)*(radius-width*0.5),
			x+math.cos(a2)*(radius-width*0.5),
			y+math.sin(a2)*(radius-width*0.5),
			alpha, r, g, b)
	end
end

function video.renderSpriteRectangleOutline(x, y, x2, y2, alpha, r, g, b, lineSprite, additive, width)
	video.renderSpriteLine(x,y,x2,y, alpha, r, g, b, lineSprite, additive, width)
	video.renderSpriteLine(x,y2,x2,y2, alpha, r, g, b, lineSprite, additive, width)
	video.renderSpriteLine(x,y,x,y2, alpha, r, g, b, lineSprite, additive, width)
	video.renderSpriteLine(x2,y,x2,y2, alpha, r, g, b, lineSprite, additive, width)
end

function video.renderAngledSpriteLine(x1,y1,ang1, x2,y2, ang2, alpha, r, g, b, lineSprite, additive, width, animate)
	local lineSprite = lineSprite or HELPER_SPRITES.line
	local width = (width or 3)*0.5
	local animate = animate or 1
	if animate < 0 then
		animate = math.abs(animate)
		x1,y1,x2,y2 = math.reverseCoordinates(x1,y1,x2,y2)
	end
	local x2 = x1 + (x2-x1)*math.min(1,(animate or 1))
	local y2 = y1 + (y2-y1)*math.min(1,(animate or 1))
	if additive then
		video.setUseMaterialFor2d(true)
		video.setMaterial(additiveColorMaterial)
		video.renderSpriteStateFreeShape(lineSprite, x1+math.cos(ang1-math.pi*0.5)*width-math.cos(ang1)*width, y1+math.sin(ang1-math.pi*0.5)*width-math.sin(ang1)*width, x2+math.cos(ang2-math.pi*0.5)*width+math.cos(ang2)*width, y2+math.sin(ang2-math.pi*0.5)*width+math.sin(ang2)*width, x1+math.cos(ang1+math.pi*0.5)*width-math.cos(ang1)*width, y1+math.sin(ang1+math.pi*0.5)*width-math.sin(ang1)*width, x2+math.cos(ang2+math.pi*0.5)*width+math.cos(ang2)*width, y2+math.sin(ang2+math.pi*0.5)*width+math.sin(ang2)*width, alpha, r, g, b)
		video.setUseMaterialFor2d(false)
	else
		video.renderSpriteStateFreeShape(lineSprite, x1+math.cos(ang1-math.pi*0.5)*width, y1+math.sin(ang1-math.pi*0.5)*width, x2+math.cos(ang2-math.pi*0.5)*width, y2+math.sin(ang2-math.pi*0.5)*width, x1+math.cos(ang1+math.pi*0.5)*width, y1+math.sin(ang1+math.pi*0.5)*width, x2+math.cos(ang2+math.pi*0.5)*width, y2+math.sin(ang2+math.pi*0.5)*width, alpha, r, g, b)
	end
end

function video.renderSpriteLine(x1,y1,x2,y2,alpha, r, g, b, lineSprite, additive, width, animate)
	local lineSprite = lineSprite or HELPER_SPRITES.line
	local width = (width or 3)*0.5
	local animate = animate or 1
	if animate < 0 then
		animate = math.abs(animate)
		x1,y1,x2,y2 = math.reverseCoordinates(x1,y1,x2,y2)
	end
	local x2 = x1 + (x2-x1)*math.min(1,(animate or 1))
	local y2 = y1 + (y2-y1)*math.min(1,(animate or 1))
	local ang = math.angleBetweenPoints(x1,y1,x2,y2)
	if additive then
		video.setUseMaterialFor2d(true)
		video.setMaterial(additiveColorMaterial)
		video.renderSpriteStateFreeShape(lineSprite, x1+math.cos(ang-math.pi*0.5)*width-math.cos(ang)*width, y1+math.sin(ang-math.pi*0.5)*width-math.sin(ang)*width, x2+math.cos(ang-math.pi*0.5)*width+math.cos(ang)*width, y2+math.sin(ang-math.pi*0.5)*width+math.sin(ang)*width, x1+math.cos(ang+math.pi*0.5)*width-math.cos(ang)*width, y1+math.sin(ang+math.pi*0.5)*width-math.sin(ang)*width, x2+math.cos(ang+math.pi*0.5)*width+math.cos(ang)*width, y2+math.sin(ang+math.pi*0.5)*width+math.sin(ang)*width, alpha, r, g, b)
		video.setUseMaterialFor2d(false)
	else
		video.renderSpriteStateFreeShape(lineSprite, x1+math.cos(ang-math.pi*0.5)*width, y1+math.sin(ang-math.pi*0.5)*width, x2+math.cos(ang-math.pi*0.5)*width, y2+math.sin(ang-math.pi*0.5)*width, x1+math.cos(ang+math.pi*0.5)*width, y1+math.sin(ang+math.pi*0.5)*width, x2+math.cos(ang+math.pi*0.5)*width, y2+math.sin(ang+math.pi*0.5)*width, alpha, r, g, b)
	end
end


function video.renderSpriteLineArrow(x,y, x2, y2, arc, size, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x,y, x2, y2, alpha, r, g, b, lineSprite, additive, width, animate)
	local ang = math.angleBetweenPoints(x,y,x2,y2)
	video.renderSpriteLine(x2, y2, x2 + math.cos(ang+math.pi+arc*0.5)*size, y2 + math.sin(ang+math.pi+arc*0.5)*size , alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x2, y2, x2 + math.cos(ang+math.pi-arc*0.5)*size, y2 + math.sin(ang+math.pi-arc*0.5)*size , alpha, r, g, b, lineSprite, additive, width, animate)
end


function video.renderSpriteLineWithOutline(x1,y1,x2,y2,alpha, r, g, b, outAlpha, outR, outG, outB, lineSprite, additive, width, animate)
	video.renderSpriteLine(x1,y1,x2,y2,outAlpha, outR, outG, outB, lineSprite, additive, width*2, animate)
	video.renderSpriteLine(x1,y1,x2,y2,alpha, r, g, b, lineSprite, additive, width, animate)
end

local zBase = 2

function video.renderConnectionLines(s1, e1, s2, e2, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	for i=1, slices do
		local prg = i/(slices+1)
		local pprg1 = prg
		if s1.z then
			local bias1 = (zBase + s1.z)/(zBase + e1.z)
			pprg1 = math.pow(prg, bias1)
		end
		local pprg2 = prg
		if s2.z then
			local bias2 = (zBase + s2.z)/(zBase + e2.z)
			pprg2 = math.pow(prg, bias2)
		end
		
		local an = (animate or 1)*(slices/3+1)-i/3
		if an <= 0 then
			break
		else
		video.renderSpriteLine(x + s1.x * pprg1*w + e1.x * (1-pprg1)*w, y + s1.y * pprg1*h + e1.y * (1-pprg1)*h,
						 x + s2.x * pprg2*w + e2.x * (1-pprg2)*w, y + s2.y * pprg2*h + e2.y * (1-pprg2)*h,
						 alpha, r, g, b, lineSprite, additive, width, math.min(1,an))
		end
	end
end	

function video.renderLineGrid(tl, tr, bl, br, x, y, w, h, wslices, hslices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(tl, tr, bl, br, x, y, w, h, wslices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(tl, bl, tr, br, x, y, w, h, hslices, alpha, r, g, b, lineSprite, additive, width, animate)
end

function video.renderLineBox(tl, tr, bl, br, x, y, w, h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + tl.x*w, y + tl.y*h, x + tr.x*w, y + tr.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + tr.x*w, y + tr.y*h, x + br.x*w, y + br.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + br.x*w, y + br.y*h, x + bl.x*w, y + bl.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + bl.x*w, y + bl.y*h, x + tl.x*w, y + tl.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
end
		
		
function video.renderPerspectiveGridCube(cube, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	local animate = math.min(1,animate or 1)
	video.renderSpriteLine(x + cube.t.x*w, y + cube.t.y*h, x + cube.tr.x*w, y + cube.tr.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.t.x*w, y + cube.t.y*h, x + cube.tl.x*w, y + cube.tl.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.tr.x*w, y + cube.tr.y*h, x + cube.c.x*w, y + cube.c.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.tl.x*w, y + cube.tl.y*h, x + cube.c.x*w, y + cube.c.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.br.x*w, y + cube.br.y*h, x + cube.tr.x*w, y + cube.tr.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.bl.x*w, y + cube.bl.y*h, x + cube.tl.x*w, y + cube.tl.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.br.x*w, y + cube.br.y*h, x + cube.b.x*w, y + cube.b.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.bl.x*w, y + cube.bl.y*h, x + cube.b.x*w, y + cube.b.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderSpriteLine(x + cube.b.x*w, y + cube.b.y*h, x + cube.c.x*w, y + cube.c.y*h, alpha, r, g, b, lineSprite, additive, width, animate)
	
	video.renderConnectionLines(cube.tr, cube.t, cube.c, cube.tl, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(cube.tl, cube.t, cube.c, cube.tr, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(cube.c, cube.tr, cube.b, cube.br, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(cube.c, cube.tl, cube.b, cube.bl, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(cube.c, cube.b, cube.tl, cube.bl, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
	video.renderConnectionLines(cube.c, cube.b, cube.tr, cube.br, x, y, w, h, slices, alpha, r, g, b, lineSprite, additive, width, animate)
end

video.colorStyles = {}

local colorStyles = video.colorStyles
colorStyles.intro = {
	getColor = function(linePrg)
		local a = linePrg*255
		local redding = 1-0.1*math.max(0,math.min(1,linePrg*2-1))
		local r = (linePrg*linePrg*0.5 + 0.5*math.easeOut(linePrg)*0.5)*255
		local g = (linePrg*0.5 + 0.5*redding*linePrg)*255
		local b = 0.5*redding*math.easeOut(linePrg)*255
		return a,r,g,b
	end
	}

colorStyles.dashboard = {
	getColor = function(linePrg)
		local a = linePrg*255
		local r = (linePrg*linePrg*0.5 + math.easeOut(linePrg)*0.5)*255*0.35
		local g = (linePrg)*255
		local b = math.easeOut(linePrg)*255*0.3
		return a,r,g,b
	end
	}

colorStyles.master = {
	getColor = function(linePrg)
		local a = linePrg*255
		local r = (linePrg*linePrg*0.5 + math.easeOut(linePrg)*0.5)*255*0.35
		local g = (linePrg)*255
		local b = math.easeOut(linePrg)*255*0.3
		return a,r,g,b
	end
	}


local defaultFonts = {sizes = {"small", "medium", "big"}, heights = {23, 40, 83}}
function video.renderFormattedText(lines, x,y, progress, lineFactor, fonts, enableContinue, time, colorStyle)
	local lineWaitIndex = 1
	local defaultAlignment = 0
	local defaultSize = 0
	local fonts = fonts or defaultFonts
	local newY = y
	for index, line in ipairs(lines) do
		local lineProgress = 1
		if progress then
			lineProgress = math.clamp(1 * (progress-lineFactor*(lineWaitIndex)), 0, 1)
		end
		
		local alpha, r, g, b = colorStyles[colorStyle or "intro"].getColor(lineProgress)
		
		if alpha > 0 then
			local newX = x
			if (line.alignment or defaultAlignment) == 0 then
				newX = newX - lines.width/2
			elseif (line.alignment or defaultAlignment) == 1 then
				newX = newX
			elseif (line.alignment or defaultAlignment) == 2 then
				newX = newX + lines.width/2
			end
			video.renderTextSprites(line.text, newX, newY, line.alignment or defaultAlignment, "../"..fonts.sizes[(line.size or defaultSize) +1] , alpha, r, g, b)
		end
		lineWaitIndex = lineWaitIndex+string.len(line.text)/20+0.35
		
		if (line.size or defaultSize) == 0 then
			newY = newY + fonts.heights[1]
		elseif (line.size or defaultSize) == 1 then
			newY = newY +  fonts.heights[2]
		elseif (line.size or defaultSize) == 2 then
			newY = newY +  fonts.heights[3]
		end
		
		if enableContinue and index == #lines then
			newY = newY + fonts.heights[1]/2
			local continueAlpha = 255
			if progress then
				continueAlpha = math.clamp((128+127*math.easeOut(math.abs(math.sin((time or 2)*math.pi/2)))) * math.min(1,(progress-lineFactor*(lineWaitIndex+1))), 0, 255)
			end
			local redding = 1-0.1*math.max(0,math.min(1,alpha/255*2-1))
			if continueAlpha > 0 then
				video.renderTextSprites(localize.get("press_to_continue"), x, newY, 1,  "../"..fonts.sizes[1], continueAlpha, (continueAlpha/255)*(continueAlpha/255)*255*0.5 + 0.5*math.easeOut(continueAlpha/255)*255*0.5, continueAlpha*0.5 + 0.5*redding*continueAlpha, 0.5*redding*math.easeOut(continueAlpha/255)*255)
			end
		end
	end
end

local function findParam(text, start)
	local par = nil
	local leng = 0
	local paramFound = false
	for i=start,string.len(text) do
		if string.sub(text, i,i) == ">" then
			return par, leng
		else
			if string.sub(text, i,i) == "=" then
				paramFound = true
			else
				if paramFound and string.sub(text, i,i) ~= "=" then
					par = (par or "").. string.sub(text, i,i)
				end
			end
			leng = leng +1
		end
	end
	return nil, leng
end
local function findNextCode(text, start)
	local leng = 0
	for i=start,string.len(text) do
		if string.sub(text, i,i) == "<" then
			return leng
		else			
			leng = leng +1
		end
	end
	return leng
end

function video.renderShadowedSpriteState(sprite, x , y, scale, angle, alpha,r,g,b, flipped)
	video.renderSpriteState(sprite, x+1 , y+1, scale, angle, alpha,0,0,0, flipped)
	video.renderSpriteState(sprite, x , y, scale, angle, alpha,r,g,b, flipped)
end


function video.renderShadowedTextSprites(text, x, y, align, customFont, alpha, r, g, b, prg, scale)
	video.renderTextSprites(text, x+1, y+1, align, customFont, alpha, 0, 0, 0, prg, scale)
	video.renderTextSprites(text, x, y, align, customFont, alpha, r, g, b, prg, scale)
end

function video.renderControllerIcon(controller, x, y, scale, a, r, g, b)
	video.renderSpriteState(controller:getIconSprite(), x, y, scale, 0, a, r, g, b)
	local id = controller:getDeviceId()
	if id then
		video.renderTextSprites(id, x- 75*scale, y+fonts.alignY("medium"), 2, "medium", a, r,g,b)
	end
end

function video.renderAdvancedText(lines, x,y, progress, colorStyle, a, r, g, b)
	local lineWaitIndex = 1
	local defaultAlignment = 0
	local defaultSize = 0
	local newY = y
	local font = "small"
	
	for index, line in ipairs(lines) do
		local lineProgress = 1
		if progress then
			lineProgress = math.clamp(1 * (progress-(lineWaitIndex)), 0, 1)
		end
		local alpha, r, g, b = a or 255, r or 255, g or 255, b or 255
		if colorStyle then
			alpha, r, g, b = colorStyles[colorStyle or "intro"].getColor(lineProgress)
		end
		
		if alpha > 0 then
			local newX = x
			if (line.alignment or defaultAlignment) == 0 then
				newX = newX - lines.width/2
			elseif (line.alignment or defaultAlignment) == 1 then
				newX = newX - line.width/2
			elseif (line.alignment or defaultAlignment) == 2 then
				newX = newX + lines.width/2 - line.width
			end

			local moveX = 0
			for i,segment in ipairs(line.segments) do
				font = fonts.sizes[segment.size]
				video.renderTextSprites(segment.text, newX+moveX, newY + line.height - segment.height, 0, font , alpha, r, g, b, lineProgress)
				lineWaitIndex = lineWaitIndex+video.getTextSpritesSize(text, font)/20+0.35
				moveX = moveX + segment.width
			end
		end
		
		newY = newY + line.height
	end
end








local string_byte = string.byte
local string_len = string.len
local string_sub = string.sub
local string_getCodePoint = string.getCodePoint
local string_findParameter = string.findParameter

local byte_lookup = { c = string_byte("c"),
					  S = string_byte("S"),
					  s = string_byte("s"),
					  R = string_byte("R"),
					  I = string_byte("I"),
					  K = string_byte("K"),
					  B = string_byte("B"),
					  T = string_byte("T"),
					  A = string_byte("A") }


local getCodePoint = string_getCodePoint

function video.getTextScaleFit(text, font, current_scale, width, raw)
	local w = math.max(1,video.getTextSpritesSize(text, font, 1, raw)*current_scale)
	return math.min(1, width/w)*current_scale
end

local FONTS = FONTS
local GLOBAL_TEXT_WIDTH_FACTOR = 1
local math_min = math.min
local math_max = math.max
local math_cos = math.cos
local math_pi = math.pi
local math_sin = math.sin
local lt = string_byte("<")
local gt = string_byte(">")
local S = string_byte("S")
local RELEASE = true
local video_renderSpriteState = video.renderSpriteState
local video_renderSpriteStateFreeShape = video.renderSpriteStateFreeShape

function video.renderTextSprites(text, x, y, align, customFont, alpha, r, g, b, prg, scale, shadow, raw)
	local font = FONTS[customFont]
	if not font then
		video.renderText(text, x, y, align, customFont, alpha or 255, r or 255, g or 255, b or 255)
		return
	end

	-- should really assert on not text or not scale.
	--assert(type(text) == "string")
	assert(text)
	local text = tostring(text) or ""
	local scale = scale or 1
	local raw = raw or false
	local prg = prg or 1
	
	
	if align ~= 0 then
		local w,h = video.getTextSpritesSize(text, customFont, scale, raw)
		x = x - w*align/2
	end

	local texLen = string_len(text)
	local len = math_min(prg*texLen, texLen)
	local dr,dg,db = r,g,b

	local drawing = 1
	local inverse = false
	local button = false
	local trendered = false
	local scanning = false
	local scanDepth = 0
	local first = false
	local arrow = nil
	local stick = false
	local keyShape = false
	local extraScale = nil
	local noLetter = nil

	local lt = lt
	local gt = gt

	for i=1,len do

		local byte, stride = getCodePoint(text, drawing)
		drawing = drawing + stride

		--print("byte", byte)

		--if video.debug then
		--	print(i, drawing, scanning, byte, string.char(byte))
		--end

		if byte == lt and not raw then
			scanDepth = scanDepth +1
			scanning = true
			--drawing = drawing + stride
			--print("in", text, drawing)
			first = true
		elseif scanning then
			local cmd = byte--string_byte(text, drawing)
			--drawing = drawing + stride
			if first then
				if cmd == byte_lookup["c"] then
					local col, extra = string_findParameter(text, drawing)
					if not shadow then
						if col then
							local color = COLORS[col] or COLORS.white
							dr,dg,db = color.r,color.g,color.b
						else
							dr,dg,db = r,g,b
						end
					end
					drawing = drawing + extra
				elseif cmd == byte_lookup["S"] then
					local angle, extra = string_findParameter(text, drawing, tonumber)
					if angle then
						arrow = angle
						stick = true
						drawing = drawing + extra
					else
						stick = true
					end
				elseif cmd == byte_lookup["R"] then
					inverse = false
					button = false
					stick = false
					keyShape = false
					extraScale = nil
				elseif cmd == byte_lookup["I"] then
					inverse = true
				elseif cmd == byte_lookup["K"] then
					keyShape = true
					trendered = false
				elseif cmd == byte_lookup["B"] then
					button = true
				elseif cmd == byte_lookup["A"] then
					local angle, extra = string_findParameter(text, drawing, tonumber)
					arrow = angle
					drawing = drawing + extra+1
					scanning = false
					scanDepth = scanDepth -1
					noLetter = true
				elseif cmd == byte_lookup["s"] then
					local special_character, extra = string_findParameter(text, drawing)
					drawing = drawing + extra+1
					byte = fonts.special_characters[special_character]
					scanning = false
					scanDepth = scanDepth -1
				end
			end
			first = false
		end

		if not scanning then
			--video.renderSpriteLine(x, y- 20, x, y + 20, 64, 64, 255, 0)
			local letter = font[byte]
			if letter then
				if letter.sprite then
					local rr, rg, rb = dr,dg,db
					if keyShape then
						if not trendered then
							local totw = letter.w*scale*(extraScale or 1)
							local ofx, ofy = 0,0
							if arrow then
								letter = font[48]
								totw = letter.w*scale*2
								local w = letter.w*scale
								video_renderSpriteStateFreeShape(SPRITES.fontRectangle, x+ w*0.5 - totw*0.5, y-ofy - letter.h*scale*0.2, x+ w*0.5 + totw*0.5, y-ofy- letter.h*scale*0.2, x+ w*0.5 - totw*0.5, y-ofy+letter.h*scale *1.1, x+ w*0.5 + totw*0.5, y-ofy+letter.h*scale *1.1, alpha*0.5, dr,dg,db)
							else
								local len = findNextCode(text, drawing)
								for l=drawing, drawing+len do
									local let = font[string_byte(text, l)]
									if let then
										totw = totw + let.w*scale
									end
								end


								local letL = font[string_byte(text, drawing)]
								local letR = font[string_byte(text, drawing+len-1)]
								video_renderSpriteStateFreeShape(SPRITES.fontRectangle, x-ofx*0.5 - letL.w*scale*0.5, y-ofy - letter.h*scale*0.2, x-ofx + totw - letR.w*0.5*scale, y-ofy- letter.h*scale*0.2, x-ofx*0.5 - letL.w*scale*0.5, y-ofy+letter.h*scale *1.1, x-ofx + totw - letR.w*0.5*scale, y-ofy+letter.h*scale *1.1, alpha*0.5, dr,dg,db)
							end
							trendered = true
						end
						rr, rg, rb = math_min(255,dr*0.1),math_min(255,dg*0.1),math_min(255,db*0.1)
					elseif button then
						video_renderSpriteState(SPRITES.fontCircle, x + letter.w/2*scale, y + letter.h/2.3*scale, (math_max(letter.w,letter.h))/90*scale, 0, alpha, dr*0.5,dg*0.5,db*0.5)
						rr, rg, rb = math_min(255,dr*1.5),math_min(255,dg*1.5),math_min(255,db*1.5)
					elseif stick then
						local ref_letter = font[48]

						local ofx, ofy = 0,0
						if arrow and arrow <= 3 then
							extraScale = 0.6
							ofx, ofy = math_cos(arrow*half_pi + math_pi)*letter.w/5*scale, math_sin(arrow*half_pi + math_pi)*ref_letter.w/5*scale
						elseif arrow then
							extraScale = 0.6
						end

						local let_off = letter.w*scale*0.5
						--x = x + letter.w*scale*0.5*(extraScale or 1)--2 letter.w*scale*(extraScale or 1)*0.5
						--print(letter.byte)
						local scaled = scale*(extraScale or 1)
						local gradient_sprite = SPRITES.fontCircleGradient
						local gx = x + let_off + ofx
						local h_w = ref_letter.w+ref_letter.h
						local half_w = ref_letter.w/2
						local half_h = ref_letter.h/2
						video_renderSpriteState(gradient_sprite, gx, y + half_h*scale, h_w/108*scaled, 0, alpha, dr*0.75,dg*0.75,db*0.75)
						video_renderSpriteState(gradient_sprite, gx, y + half_h*scale, h_w/135*scaled, math_pi, alpha, dr*0.65,dg*0.65,db*0.65)

						for i=0,3 do
							video_renderSpriteState(gradient_sprite, gx + math_cos(i*math_pi/2)*half_w*scaled, y + half_h*scale + math_sin(i*math_pi/2)*half_w*scaled, h_w/1260*scaled, half_pi, alpha, dr*0.65,dg*0.65,db*0.65)
						end

						rr, rg, rb = math_min(255,dr*1.5),math_min(255,dg*1.5),math_min(255,db*1.5)
					elseif inverse then
						video.renderRectangle(x, y, letter.w*scale, letter.h*scale, alpha, dr,dg,db)
						rr, rg, rb = dr*0.25,dg*0.25,db*0.25
					end


					if arrow then
						local let_off = letter.w*scale*0.5
						if stick then
							local ref_letter = font[48]

							if arrow <= 3 then
								local ofx, ofy = math_cos(arrow*half_pi + math_pi)*ref_letter.w/5*scale, math_sin(arrow*half_pi + math_pi)*ref_letter.w/5*scale
								video_renderSpriteState(SPRITES.fontArrow, x+ let_off + ofx  + math_cos(arrow*math_pi/2)*ref_letter.w/1.2*scale, y + ref_letter.h/2*scale + math_sin(arrow*math_pi/2)*ref_letter.w/1.2*scale, (ref_letter.w+ref_letter.h)/500*scale, arrow*math_pi/2, alpha, math_min(255,rr*1.5),math_min(255,rg*1.5),math_min(255,rb*1.5))
								x = x + ofx
							else
								for i=0,3 do
									local ofx, ofy = math_cos(i*half_pi + math_pi)*ref_letter.w/5*scale, math_sin(i*half_pi + math_pi)*ref_letter.w/5*scale
									video_renderSpriteState(SPRITES.fontArrow, x + let_off +  math_cos(i*math_pi/2)*ref_letter.w/1.2*scale, y + ref_letter.h/2*scale + math_sin(i*math_pi/2)*ref_letter.w/1.2*scale, (ref_letter.w+ref_letter.h)/500*scale, i*math_pi/2, alpha, math_min(255,rr*1.5),math_min(255,rg*1.5),math_min(255,rb*1.5))
								end
							end
							stick = false
							--x = x + letter.w*0.5*scale - letter.w*0.5*scale*(extraScale or 1)
						else
							local ofx, ofy = 0,0
							video_renderSpriteState(SPRITES.fontArrow, x + let_off , y + letter.h/2*scale , (letter.w+letter.h)/175*scale, arrow*math_pi/2, alpha, math_min(255,rr*1.5),math_min(255,rg*1.5),math_min(255,rb*1.5))
						end
						arrow = nil
					elseif stick then
						stick = false
						--x = x + (letter.w*scale*0.5 - letter.w*scale*(extraScale or 1)*0.5) * GLOBAL_TEXT_WIDTH_FACTOR
					end
					--print(letter.sprite)
					if not noLetter then
						if extraScale then
							video_renderSpriteState(letter.sprite, x + (letter.w*0.5*scale - extraScale*letter.w*scale*0.5), y+ (letter.h*0.5*scale - extraScale*letter.h*scale*0.5), scale*extraScale, 0, alpha, rr, rg, rb)
							--x = x + letter.w*scale*extraScale * GLOBAL_TEXT_WIDTH_FACTOR
							x = x + letter.w*scale*extraScale * GLOBAL_TEXT_WIDTH_FACTOR
						else
							video_renderSpriteState(letter.sprite, x, y, scale, 0, alpha, rr, rg, rb)
							x = x + letter.w*scale * GLOBAL_TEXT_WIDTH_FACTOR
						end
					else
						noLetter = false
					end
				else
					local ww = letter.w*scale * GLOBAL_TEXT_WIDTH_FACTOR
					x = x + ww
				end
			elseif not RELEASE then
				local byte_code = tostring(byte)
				local ww,hh = video.getTextSpritesSize(byte_code, customFont, scale)
				video.renderSpriteRectangle(x-1, y-1, ww+2, hh+2, alpha, 255-r, 255-g, 255-b, nil, nil, 1)
				video.renderTextSprites(byte_code, x, y, 0, customFont, alpha, r, g, b, nil, scale)
				x = x + ww
			end
			--drawing = drawing + stride
		end

		if byte == gt and scanDepth > 0 and not raw then
			scanDepth = scanDepth -1
			if scanDepth <= 0 then
				scanning = false
			end
		end
			--print("out", text, drawing)
			--drawing = drawing + stride
		--end		
		if drawing > len then
			break
		end
	end
end

local SIZE_CACHE = caches.new("textSpritesSize", 4)

function video.getTextSpritesSize(text, customFont, scale, raw)
	local text = text or ""
	local scale = scale or 1
	local raw = raw or false

	local cached_w = SIZE_CACHE:get(1, raw, customFont, text)
	local cached_h = SIZE_CACHE:get(2, raw, customFont, text)

	if not cached_w then
		local cufont = FONTS[customFont]
		local drawing = 1
		local scanning = false
		local w,h = 0,0
		local lt = lt
		local gt = gt
		local S = S

		local len = string_len(text)
		for i=1, len do
			local byte, stride = getCodePoint(text, drawing)

			drawing = drawing + stride
			if byte == lt and not raw then
				scanning = true
			elseif scanning then
				if byte == byte_lookup["s"] then
					local special_character, extra = string_findParameter(text, drawing)
					drawing = drawing + extra+1
					byte = fonts.special_characters[special_character]
					scanning = false
				end
			end

			if not scanning then
				local fontChar = byte and cufont[byte] or nil
				if fontChar then
					w = w + fontChar.w
					h = math_max(h,fontChar.h)
				end
			elseif byte == gt and scanning and not raw then
				scanning = false
			end

			if drawing > len then
				break
			end
		end

		SIZE_CACHE:set(w, 1, raw, customFont, text)
		SIZE_CACHE:set(h, 2, raw, customFont, text)
		cached_w = w
		cached_h = h
	end
	return cached_w*scale * GLOBAL_TEXT_WIDTH_FACTOR,cached_h*scale
end