local log
version = '1.0.5'

haveDepCtrl, DependencyControl = pcall require, 'l0.DependencyControl'

if haveDepCtrl
	version = DependencyControl {
		name: 'TrimHandler'
		:version
		description: 'A class for encoding video clips.'
		author: 'torque'
		url: 'https://github.com/TypesettingTools/Aegisub-Motion'
		moduleName: 'a-mo.TrimHandler'
		feed: 'https://raw.githubusercontent.com/TypesettingTools/Aegisub-Motion/DepCtrl/DependencyControl.json'
		{
			{ 'a-mo.Log',  version: '1.0.0' }
		}
	}
	log = version\requireModules!

else
	log  = require 'a-mo.Log'

windows = jit.os == "Windows"

class TrimHandler
	@version: version

	@windows: windows

	existingPresets: {
		"x264", "ffmpeg"
	}

	-- Set up encoder presets.
	defaults: {
		x264:    '"#{encbin}" --crf 16 --tune fastdecode -i 250 --fps 23.976 --sar 1:1 --index "#{prefix}/#{index}.index" --seek #{startf} --frames #{lenf} -o "#{prefix}/#{output}[#{startf}-#{endf}].mp4" "#{inpath}/#{input}"'
		ffmpeg:  '"#{encbin}" -ss #{startt} -an -sn -i "#{inpath}/#{input}" -q:v 1 -vsync passthrough -frames:v #{lenf} "#{prefix}/#{output}[#{startf}-#{endf}]-%05d.jpg"'
		-- avs2pipe: 'echo FFVideoSource("#{inpath}#{input}",cachefile="#{prefix}#{index}.index").trim(#{startf},#{endf}).ConvertToRGB.ImageWriter("#{prefix}/#{output}-[#{startf}-#{endf}]\\",type="png").ConvertToYV12 > "#{temp}/a-mo.encode.avs"\nmkdir "#{prefix}#{output}-[#{startf}-#{endf}]"\n"#{encbin}" video "#{temp}/a-mo.encode.avs"\ndel "#{temp}/a-mo.encode.avs"'
		-- vapoursynth:
	}

	-- Example trimConfig:
	-- trimConfig = {
	-- 	-- The prefix is the directory the output will be written to. It
	-- 	-- is passed through aegisub.decode_path.
	-- 	prefix: "?video"

	-- 	-- The name of the built in encoding preset to use. Overridden by
	-- 	-- command if that is neither nil nor an empty string.
	-- 	preset: "x264"

	-- 	-- The path of the executable used to actually do the encoding.
	-- 	-- Full path is recommended as the shell environment may be
	-- 	-- different than expected on non-windows systems.
	-- 	encBin: "C:\x264.exe"

	-- 	-- A custom encoding command that can be used to override the
	-- 	-- built-in defaults. Usable token documentation to come.
	-- 	-- Overrides preset if that is set.
	-- 	command: nil

	-- 	-- Script should attempt to create prefix directory.
	-- 	makePfix: nil

	-- 	-- Script should attempt to log output of the encoding command.
	-- 	writeLog: true
	-- }
	new: ( trimConfig ) =>
		@tokens = { }
		if trimConfig.command != nil
			trimConfig.command = trimConfig.command\gsub "[\t \r\n]*$", ""
			if trimConfig.command != ""
				@command = trimConfig.command
			else
				@command = @defaults[trimConfig.preset]
		else
			@command = @defaults[trimConfig.preset]

		@makePrefix = trimConfig.makePfix
		@writeLog   = trimConfig.writeLog

		with @tokens
			.temp = aegisub.decode_path "?temp"
			-- For some reason, aegisub appends / to the end of ?temp but not
			-- other tokens.
			finalTemp = .temp\sub -1, -1
			if finalTemp == '\\' or finalTemp == '/'
				.temp = .temp\sub 1, -2
			.encbin = trimConfig.encBin
			.prefix = aegisub.decode_path trimConfig.prefix
			.inpath = aegisub.decode_path "?video"
			.log    = aegisub.decode_path "#{.temp}/a-mo.encode.log"

		getVideoName @

	getVideoName = =>
		with @tokens
			video = aegisub.project_properties!.video_file
			if video\len! == 0
				log.windowError "Aegisub thinks your video is 0 frames long.\nTheoretically it should be impossible to get this error."
			if video\match "^?dummy"
				log.windowError "I can't encode that dummy video for you."
			.input = video\gsub( "^[A-Z]:\\", "", 1 )\gsub ".+[^\\/]-[\\/]", "", 1
			.index = .input\match "(.+)%.[^%.]+$"
			.output = .index

	calculateTrimLength: ( lineCollection ) =>
		with @tokens
			.startt = lineCollection.startTime / 1000
			.endt   = lineCollection.endTime / 1000
			.lent   = .endt - .startt
			.startf = lineCollection.startFrame
			.endf   = lineCollection.endFrame - 1
			.lenf   = lineCollection.totalFrames

	performTrim: =>
		with platform = ({
				[true]:  {
					pre: @tokens.temp
					ext: ".ps1"
					-- This needs to be run from cmd or it will not work.
					exec: 'powershell -c iex "$(gc "%s" -en UTF8)"'
					preCom: (@makePrefix and "mkdir -Force \"#{@tokens.prefix}\"; & " or "& ")
					postCom: (@writeLog and " 2>&1 | Out-File #{@tokens.log} -en UTF8; if($LASTEXITCODE -ne 0) {echo \"If there is no log before this, your encoder is not a working executable or your encoding command is invalid.\" | ac -en utf8 #{@tokens.log}; exit 1}" or "") .. "; exit 0"
					execFunc: ( encodeScript ) ->
						-- clear out old logfile because it doesn't get overwritten
						-- when certain errors occur.
						if @writeLog
							logFile = io.open @tokens.log, 'wb'
							logFile\close! if logFile
						success = os.execute encodeScript
						if @writeLog and not success
							logFile = io.open @tokens.log, 'r'
							unless logFile
								log.windowError "Could not read log file #{@tokens.log}.\nSomething has gone horribly wrong."
							encodeLog = logFile\read '*a'
							logFile\close!
							log.warn "\nEncoding error:"
							log.warn encodeLog
							log.windowError "Encoding failed. Log has been printed to progress window."
						elseif not success
							log.windowError "Encoding seems to have failed but you didn't write a log file."
				}
				[false]: {
					pre: @tokens.temp
					ext: ".sh"
					exec: 'sh "%s"'
					preCom: @makePrefix and "mkdir -p \"#{@tokens.prefix}\"\n" or ""
					postCom: " 2>&1; if [[ $? -ne 0 ]]; then echo \"If there is no log before this, your encoder is not a working executable or your encoding command is invalid.\"; false; fi"
					execFunc: ( encodeScript ) ->
						logFile = io.popen encodeScript, 'r'
						encodeLog = logFile\read '*a'
						-- When closing a file handle created with io.popen,
						-- file:close returns the same values returned by
						-- os.execute.
						success = logFile\close!
						unless success
							log.warn "\nEncoding error:"
							log.warn encodeLog
							log.windowError "Encoding failed. Log has been printed to progress window."
				}
			})[windows]
			-- check encoder binary exists
			encoder = io.open @tokens.encbin, "rb"
			unless encoder
				log.windowError "Encoding binary (#{@tokens.encbin}) does not appear to exist."
			encoder\close!

			encodeScript = aegisub.decode_path "#{.pre}/a-mo.encode#{.ext}"
			encodeScriptFile = io.open encodeScript, "w+"
			unless encodeScriptFile
				log.windowError "Encoding script could not be written.\nSomething is wrong with your temp dir (#{.pre})."
			encodeString = .preCom .. @command\gsub( "#{(.-)}", ( token ) -> @tokens[token] ) .. .postCom
			if windows
				encodeString = encodeString\gsub "`", "``"
			log.debug encodeString
			encodeScriptFile\write encodeString
			encodeScriptFile\close!
			.execFunc .exec\format encodeScript

if haveDepCtrl
	return version\register TrimHandler
else
	return TrimHandler
