local M = {}

function GetPath(str)
	local sep = '/'
	if ya.target_family() == "windows" then
		sep = '\\'
	end
    return str:match("(.*"..sep..")")
end

function Exiftool(...)
	local child = Command("exiftool")
		:arg({
			"-q", "-q", "-S", "-Title", "-SortName",
			"-TitleSort", "-TitleSortOrder", "-Artist",
			"-SortArtist", "-ArtistSort", "-PerformerSortOrder",
			"-Album", "-SortAlbum", "-AlbumSort", "-AlbumSortOrder",
			"-AlbumArtist", "-SortAlbumArtist", "-AlbumArtistSort",
			"-AlbumArtistSortOrder", "-Genre", "-TrackNumber",
			"-Year", "-Duration", "-SampleRate", 
			"-AudioSampleRate", "-AudioBitrate", "-AvgBitrate",
			"-Channels", "-AudioChannels", tostring(...),
		})
		:stdout(Command.PIPED)
		:stderr(Command.NULL)
		:spawn()
	return child
end

function Mediainfo(...)
	local file, cache_dir = ...
	local template = cache_dir.."mediainfo.txt"
	local child = Command("mediainfo")
		:arg({
			"--Output=file://"..template, tostring(file)
		})
		:stdout(Command.PIPED)
		:stderr(Command.NULL)
		:spawn()
	return child
end

function M:peek(job)
	local cache = ya.file_cache(job)
	if not cache then
		return
	end

	-- Get cache dir to find the mediainfo template file
	local cache_dir = GetPath(tostring(cache))

	-- Try mediainfo, otherwise use exiftool
	local status, child = pcall(Mediainfo, job.file.url, cache_dir)
	if not status or child == nil then
		status, child = pcall(Exiftool, job.file.url)
		if not status or child == nil then
			local error = ui.Line { ui.Span("Make sure exiftool is installed and in your PATH") }
			-- TODO)) Remove legacy method when v0.4 gets released
			local function display_error_legacy()
				local p = ui.Paragraph(job.area, { error }):wrap(ui.Paragraph.WRAP)
				ya.preview_widgets(job, { p })
			end
			local function display_error()
				local p = ui.Text(error):area(job.area):wrap(ui.Text.WRAP)
				ya.preview_widgets(job, { p })
			end
			if pcall(display_error) then else pcall(display_error_legacy) end
			return
		end
	end

	local limit = job.area.h
	local i, metadata = 0, {}
	repeat
		local next, event = child:read_line()
		if event == 1 then
			return self:fallback_to_builtin()
		elseif event ~= 0 then
			break
		end

		i = i + 1
		if i > job.skip then
			local m_title, m_tag = Prettify(next)
			if m_title ~= "" and m_tag ~= "" then
				local ti = ui.Span(m_title):bold()
				local ta = ui.Span(m_tag)
				table.insert(metadata, ui.Line{ti, ta})
				table.insert(metadata, ui.Line{})
			end
		end
	until i >= job.skip + limit

	-- TODO)) Remove legacy method when v0.4 gets released
	local function display_metadata_legacy()
		local p = ui.Paragraph(job.area, metadata):wrap(ui.Paragraph.WRAP)
		ya.preview_widgets(job, { p })
	end
	local function display_metadata()
		local p = ui.Text(metadata):area(job.area):wrap(ui.Text.WRAP)
		ya.preview_widgets(job, { p })
	end
	if pcall(display_metadata) then else pcall(display_metadata_legacy) end

	local cover_width = job.area.w / 2 - 5
	local cover_height = (job.area.h / 4) + 3

	local bottom_right = ui.Rect {
		x = job.area.right - cover_width,
		y = job.area.bottom - cover_height,
		w = cover_width,
		h = cover_height,
	}

	if self:preload(job) == 1 then
		ya.image_show(cache, bottom_right)
	end
end

function Prettify(metadata)
	local substitutions = {
		Sortname = "Sort Title:",
		SortName = "Sort Title:",
		TitleSort = "Sort Title:",
		TitleSortOrder = "Sort Title:",
		ArtistSort = "Sort Artist:",
		SortArtist = "Sort Artist:",
		Artist = "Artist:",
		ARTIST = "Artist:",
		PerformerSortOrder = "Sort Artist:",
		SortAlbumArtist = "Sort Album Artist:",
		AlbumArtistSortOrder = "Sort Album Artist:",
		AlbumArtistSort = "Sort Album Artist:",
		AlbumSortOrder = "Sort Album:",
		AlbumSort = "Sort Album:",
		SortAlbum = "Sort Album:",
		Album = "Album:",
		ALBUM = "Album:",
		AlbumArtist = "Album Artist:",
		Genre = "Genre:",
		GENRE = "Genre:",
		TrackNumber = "Track Number:",
		Year = "Year:",
		Duration = "Duration:",
		AudioBitrate = "Bitrate:",
		AvgBitrate = "Average Bitrate:",
		AudioSampleRate = "Sample Rate:",
		SampleRate = "Sample Rate:",
		AudioChannels = "Channels:"
	}

	for k, v in pairs(substitutions) do
		metadata = metadata:gsub(tostring(k)..":", v, 1)
	end

	-- Separate the tag title from the tag data
	local t={}
	for str in string.gmatch(metadata , "([^"..":".."]+)") do
		if str ~= "\n" then
			table.insert(t, str)
		else
			table.insert(t, nil)
		end
	end

	-- Add back semicolon to title, rejoin tag data if it happened to contain a semicolon
	local title, tag_data = "", ""
	if t[1] ~= nil then
		title, tag_data = t[1]..":", table.concat(t, ":", 2)
	end
	return title, tag_data

end

function M:seek(job)
	local h = cx.active.current.hovered
	if h and h.url == job.file.url then
		ya.manager_emit("peek", {
			tostring(math.max(0, cx.active.preview.skip + job.units)),
			only_if = tostring(job.file.url),
		})
	end
end

function M:preload(job)
	local cache = ya.file_cache(job)
	if not cache or fs.cha(cache) then
		return 1
	end

	local mediainfo_template = 'General;"\
$if(%Track%,Title: %Track%,)\
$if(%Track/Sort%,Sort Title: %Track/Sort%,)\
$if(%Title/Sort%,Sort Title: %Title/Sort%,)\
$if(%TITLESORT%,Sort Title: %TITLESORT%,)\
$if(%Performer%,Artist: %Performer%,)\
$if(%Performer/Sort%,Sort Artist: %Performer/Sort%,)\
$if(%ARTISTSORT%,Sort Artist: %ARTISTSORT%,)\
$if(%Album%,Album: %Album%,)\
$if(%Album/Sort%,Sort Album: %Album/Sort%)\
$if(%ALBUMSORT%,Sort Album: %ALBUMSORT%)\
$if(%Album/Performer%,Album Artist: %Album/Performer%)\
$if(%Album/Performer/Sort%,Sort Album Artist: %Album/Performer/Sort%)\
$if(%Genre%,Genre: %Genre%)\
$if(%Track/Position%,Track Number: %Track/Position%)\
$if(%Recorded_Date%,Year: %Recorded_Date%)\
$if(%Duration/String%,Duration: %Duration/String%)\
$if(%BitRate/String%,Bitrate: %BitRate/String%)\
"\
Audio;"Sample Rate: %SamplingRate%\
Channels: %Channel(s)%"\
'

	-- Write the mediainfo template file into yazi's cache dir
	local cache_dir = GetPath(tostring(cache))
	fs.write(Url(cache_dir.."mediainfo.txt"), mediainfo_template)

	local output = Command("exiftool")
		:arg({ "-b", "-CoverArt", "-Picture", tostring(job.file.url) })
		:stdout(Command.PIPED)
		:stderr(Command.PIPED)
		:output()

	if not output then
		return 0
	end

	return fs.write(cache, output.stdout) and 1 or 2
end

return M
