-- Aegisub Automation demonstration script
-- Original written by Niels Martin Hansen
-- Given into the public domain
-- But still, please don't use the effect generated by this script
-- (unchanged) for your own works.

-- Note that many of the techniques used in this script are heavily deprecated, much of the processing done
-- is also done by the karaskel scripts.
-- In fact, this can almost be viewed as an example of how *not* to do things...

-- Since this script assumes some rather specific things about the ASS input, it might not be very useful at first.
-- The original ASS file this was written for is available upon request.

include("utils.lua")

name = "Advanced karaoke effect"
description = "An advanced karaoke effect, making heavy use of both line-copying and per-syllable text placement. Also demonstrates how to treat lines differently, based on their style, and syllables differently based on the timing tag used."
version, kind, configuration = 3, 'basic_ass', {}

function process_lines(meta, styles, lines, config)
	local output = { n=0 }
	math.randomseed(5922) -- just to make sure it's initialised the same every time
	for curline = 0, lines.n-1 do
		aegisub.report_progress(curline/lines.n*100)
		local lin = lines[curline]
		if lin.kind == "dialogue" and lin.style == "op romaji" then
			doromaji(lin, output, styles["op romaji"], 30)
		elseif lin.kind == "dialogue" and lin.style == "op kanji" then
			-- Just one of these lines should be uncommented.
			-- This script was written before configuration worked, otherwise it would use configuration
			-- to select which of the two effects to use for "op kanji" style lines.
			--doromaji(lin, output, styles["op kanji"], 20)
			dokanji(lin, output, styles["op kanji"])
		else
			-- Unknown lines are copied verbatim
			output.n = output.n + 1
			output[output.n] = lin
		end
	end
	return output
end

function doromaji(lin, output, sty, linetop)
	aegisub.set_status(lin.text_stripped)
	
	--local linetop = 50
	
	-- prepare syllable data
	local linewidth = 0
	local syltime = 0
	local syls = {n=0}
	for i = 1, lin.karaoke.n-1 do
		local syl = lin.karaoke[i]
		syl.width, syl.height, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped)
		syl.left = linewidth
		syl.start_time = syltime
		syl.end_time = syltime + syl.duration
		syltime = syltime + syl.duration
		if syl.kind == "kf" and (syl.text == " " or syl.text == "  ") then
			-- The font this effect was made to work with has too wide spaces, so make those half width
			syl.width = math.floor(syl.width / 2)
			syl.kind = "space"
		elseif syl.kind == "kf" then
			syl.kind = "deco"
		elseif syl.kind == "k" then
			syl.kind = "reg"
		elseif syl.kind == "ko" then
			syl.kind = "dance"
		else
			syl.kind = "ignore"
		end
		if syl.text == "#" then
			syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration
			syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration
		else
			linewidth = linewidth + syl.width
			syls[syls.n] = syl
			syls.n = syls.n + 1
		end
	end
	local lineofs = math.floor((640 - linewidth) / 2)
	
	for i = 0, syls.n-1 do
		local syl = syls[i], copy_line(lin)
		if syl.kind == "space" then
			-- Spaces are skipped. Since the width of them is already incorporated in the position calculations
			-- for the following syllables, they can safely be stripped from output, without losing the spaces.
		elseif syl.kind == "ignore" then
			-- These are actually syllable kinds we don't know what to do about. They are not included in the output.
		else
			local startx, starty, shakex, shakey, enterangle
			-- angle to enter from
			enterangle = math.rad((180 / lin.karaoke.n) * i - 90)
			-- position to enter from (350 pixels from (320, 50))
			startx = math.sin(enterangle)*350 + 320
			starty = linetop - math.cos(enterangle)*350
			if syl.kind == "reg" then
				shakex = (syl.left+lineofs) + math.random(-5, 5)
				shakey = linetop + math.random(-5, 5)
			elseif syl.kind == "deco" then
				shakex, shakey = syl.left+lineofs, linetop
			elseif syl.kind == "dance" then
				shakex = (syl.left+lineofs) + math.random(-5, 5)
				shakey = linetop + math.random(-5, 5)
			end
			-- origin for rotation
			local orgx = syl.left + lineofs + syl.width/2
			local orgy = linetop + syl.height/2
			-- entry effect
			local enterlin = copy_line(lin)
			enterlin.start_time = lin.start_time - 40
			enterlin.end_time = lin.start_time
			enterlin.text = string.format("{\\move(%d,%d,%d,%d)\\fr%d\\t(\\fr0)\\an7}%s", startx, starty, shakex, shakey, -math.deg(enterangle), syl.text)
			output.n = output.n + 1
			output[output.n] = enterlin
			-- main highlight effect
			local newlin = copy_line(lin)
			local hilistart, hilimid, hiliend = syl.start_time*10, (syl.start_time+syl.duration/2)*10, (syl.start_time+syl.duration)*10
			newlin.text = string.format("\\move(%d,%d,%d,%d,%d,%d)\\an7}%s", shakex, shakey, syl.left+lineofs, linetop, hilistart, hiliend, syl.text)
			newlin.layer = 1
			if syl.kind == "dance" then
				local fx = string.format("\\org(%d,%d)\\t(%d,%d,\\fr30)\\t(%d,%d,\\fr-30)\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)\\t(%d,%d,\\fr0)", orgx, orgy, hilistart, hilistart+20, hilistart+20, hiliend, hilistart, hilimid, hilimid, hiliend, hiliend, hiliend+syl.duration*10)
				newlin.text = fx .. newlin.text
			else
				local fx = string.format("\\t(%d,%d,\\3c&H0000ff&)\\t(%d,%d,\\3c&H000080&)", hilistart, hilimid, hilimid, hiliend)
				newlin.text = fx .. newlin.text
			end
			local bord = copy_line(newlin)
			bord.layer = 0
			bord.text = "{" .. bord.text
			newlin.text = "{\\bord0" .. newlin.text
			output.n = output.n + 2
			output[output.n-1] = bord
			output[output.n] = newlin
			-- leave effect
			-- cut the line over in two, lower half "drops down", upper just fades away
			local tophalf = copy_line(lin)
			tophalf.start_time = lin.end_time
			tophalf.end_time = lin.end_time + 100
			tophalf.text = string.format("\\3c&H000080&\\fad(0,700)\\pos(%d,%d)\\an7}%s", syl.left+lineofs, linetop, syl.text_stripped)
			local bottomhalf = copy_line(tophalf)
			tophalf.text = string.format("{\\t(0,200,\\1c&H000080&)\\clip(0,0,640,%d)%s", linetop+syl.height/2, tophalf.text)
			bottomhalf.text = string.format("{\\org(%d,%d)\\clip(0,%d,640,480)\\t(0,200,\\1c&H000080&)\\t(200,1000,1.2,\\frx90\\clip(0,%d,640,480)%s", 320, linetop+syl.height, linetop+syl.height/2, linetop+syl.height, bottomhalf.text)
			output.n = output.n + 2
			output[output.n-1] = tophalf
			output[output.n] = bottomhalf
		end
	end
end

function dokanji(lin, output, sty)
	aegisub.set_status(lin.text_stripped)

	local fontname = "@HGSHigemoji"
	
	local lineheight = 0
	local syltime = 0
	local syls = {n=0}
	for i = 1, lin.karaoke.n-1 do
		local syl = lin.karaoke[i]
		syl.height, syl.width, syl.descent, syl.extlead = aegisub.text_extents(sty, syl.text_stripped)
		syl.start_time = syltime
		syl.end_time = syltime + syl.duration
		syltime = syltime + syl.duration
		if syl.text == "#" then
			syls[syls.n-1].duration = syls[syls.n-1].duration + syl.duration
			syls[syls.n-1].end_time = syls[syls.n-1].end_time + syl.duration
		--elseif syl.text == "" then
			-- skip
		else
			lineheight = lineheight + syl.height
			syls[syls.n] = syl
			syls.n = syls.n + 1
		end
	end
	
	for i = 0, syls.n-1 do
		local syl = syls[i]
		local top = 170
		for j = 0, 8 do
			if i+j >= syls.n then
				break
			end
			local newlin = copy_line(lin)
			newlin.start_time = lin.start_time + syl.start_time
			newlin.end_time = newlin.start_time + syl.duration
			local startalpha, targetalpha
			if j == 0 then
				startalpha = 0
				targetalpha = 255
			else
				startalpha = j * 255 / 8
				targetalpha = (j-1) * 255 / 8
			end
			newlin.text = string.format("{\\fn%s\\an7\\fr-90\\move(%d,%d,%d,%d)\\1a&H%2x&\\3a&H%2x&\\t(\\1a&H%2x&\\3a&H%2x&)}%s", fontname, 620, top, 620, top-syl.height, startalpha, startalpha, targetalpha, targetalpha, syls[i+j].text)
			top = top + syls[i+j].height
			output.n = output.n + 1
			output[output.n] = newlin
		end
	end
end
