-- Almost entirely copypasted from that other theme I'm working on;
-- mostly convenience stuff.

-- #################################################
-- ParaMania specific stuff.

-- TODO: translatables
local paraStyles = {"parapara", "techpara", "torapara"}
-- Check if the song's genre matches the hardcoded para styles above.
function Song:GenreIsParaStyle()
	for i, para in ipairs(paraStyles) do
		if string.lower(self:GetGenre()) == para then return true end
	end
	return false
end

-- Old extra metadata storage method.
-- dancers in video [dancer group]:choreographer(s) or dancer group:Freestyle VJ:dancers in ALTERNATE video [dancer group]
function Song:GetParamaniaTxt()
	local ret = {"???", "???"}
	
	if FILEMAN:DoesFileExist(self:GetSongDir() .. "paramania.txt") then
		-- is there a less verbose way to do this?
		local file = RageFileUtil.CreateRageFile()
		file:Open(self:GetSongDir() .. "paramania.txt", 1)
		ret = split(":",file:Read())
		file:destroy()
	end
	
	return ret
end

-- Current extra metadata storage method.
function Song:GetParamaniaIni()
	local ret
	
	if FILEMAN:DoesFileExist(self:GetSongDir() .. "paramania.ini") then
		local theFile = IniFile.ReadFile(self:GetSongDir() .. "paramania.ini")
		ret = theFile
		-- Check for the required section's existence.
		-- If it doesn't exist, panic
		if theFile.Metadata and #theFile.Metadata > 0 then
			ret.Metadata.Dancers = "???"
			ret.Metadata.Choreo = "???"
			ret.Metadata.FreestyleVJ = "???"
		end
	elseif FILEMAN:DoesFileExist(self:GetSongDir() .. "paramania.txt") then
		-- Fallback on the old method.
		local txt = self:GetParamaniaTxt()
		ret = {
			Metadata = {
				Dancers=txt[1] or "",
				Choreo=txt[2] or "",
				FreestyleVJ=txt[3] or "",
				AltDancers=txt[4] or "",
				AltVidSource=txt[5] or ""
			}
		}
	else -- Uh oh.
		ret = {
			Metadata = {
				Dancers="",
				Choreo=""
			}
		}
	end
	
	return ret
end

-- Check if both Beginner and Easy/Basic (designated slots for ParaPara and ParaPara+ charts respectively)
-- are absent in this file. This check will never be made outside PARA mode, so only check that steps type*.
-- TODO: compensate for non-canon styles when added
-- Difficulty[1] = Beginner and Difficulty[2] = Easy/Basic.
function Song:IsFreestyleOnly()
	return not self:GetOneSteps("Para_Single", Difficulty[1]) and not self:GetOneSteps("Para_Single", Difficulty[2])
end

-- #################################################
-- Everything else.

function Center1Player()
	local styleType = GAMESTATE:GetCurrentStyle():GetStyleType()
	
	-- popn mode doesn't support two players, so why bother keeping things to one side?
	if IsGame("popn") then
		return true
	-- always center in OnePlayerTwoSides ( Doubles ) or TwoPlayersSharedSides ( Couples )
	elseif styleType == "StyleType_OnePlayerTwoSides" or styleType == "StyleType_TwoPlayersSharedSides" then
		return true
	-- only Center1P if Pref enabled and OnePlayerOneSide.
	-- (implicitly excludes Rave, Battle, Versus, Routine)
	elseif PREFSMAN:GetPreference("Center1Player") then
		--return styleType == "StyleType_OnePlayerOneSide"
		return GAMESTATE:GetNumPlayersEnabled() <= 1
	else
		return false
	end
end

function GetSongBackground()
	-- not always a song it tries to get...
	local target = GAMESTATE:IsCourseMode() and GAMESTATE:GetCurrentCourse() or GAMESTATE:GetCurrentSong()
	if target then
		local path = target:GetBackgroundPath()
		if path then
			return path
		end
	end
	return THEME:GetPathG("Common","fallback background")
end

-- Alternative to checking a song's title and artist the long way.
-- (mostly for easter eggs involving certain songs)
function Song:IdentityCheck(displayFullTitle, artist, isTransliterated)
	if isTransliterated then
		return (displayFullTitle == self:GetTranslitFullTitle() and artist == self:GetTranslitArtist())
	end
	
	return (displayFullTitle == self:GetDisplayFullTitle() and artist == self:GetDisplayArtist())
end

-- Returns the song (and subtitle, if necessary) with nice line breaks.
function Song:FormattedSongTitle()
	local ret = "???"
	
	if self:GetDisplaySubTitle() == "" then
		ret = self:GetDisplayMainTitle()
	else
		ret = self:GetDisplayMainTitle() .. "\n" .. self:GetDisplaySubTitle()
	end
	
	return ret
end

-- Same as FormattedSongTitle, but adds the artist at the end too.
function Song:FormattedSongTitleAndArtist()
	local ret = "???"
	
	if self:GetDisplaySubTitle() == "" then
		ret = self:GetDisplayMainTitle() .. "\n/ " .. self:GetDisplayArtist()
	else
		ret = self:GetDisplayMainTitle() .. "\n" .. self:GetDisplaySubTitle() .. "\n/ " .. self:GetDisplayArtist()
	end
	
	return ret
end

function Song:HasUsableGraphics()
	if not self:HasJacket() and not self:HasBackground() and not self:HasBanner() then
		return false
	end
	return true
end

function Song:GetJacketOrSubstitutePath()
	if self:HasJacket() then
		return self:GetJacketPath()
	elseif self:HasBackground() then
		return self:GetBackgroundPath()
	elseif self:HasBanner() then
		return self:GetBannerPath()
	end
	
	return THEME:GetPathG("Common","fallback jacket")
end

-- Same as above, but for banners.
function Song:GetBannerOrSubstitutePath()
	if self:HasBanner() then
		return self:GetBannerPath()
	elseif self:HasJacket() then
		return self:GetJacketPath()
	elseif self:HasBackground() then
		return self:GetBackgroundPath()
	end
	
	return THEME:GetPathG("Common","fallback banner")
end

-- Same as above AGAIN, but for backgrounds.
function Song:GetBGOrSubstitutePath()
	if self:HasBackground() then
		return self:GetBackgroundPath()
	elseif self:HasJacket() then
		return self:GetJacketPath()
	elseif self:HasBanner() then
		return self:GetBannerPath()
	end
	
	return THEME:GetPathG("Common","fallback background")
end

-- Same as above AGAIN, but now for preview videos!!
-- If a preview vid isn't present, get the background.
-- Additionally, if not playing on PARA mode, we'll try to avoid using para routine preview vids.
function Song:GetPreviewVidOrBGPath()
	local usePVid = GAMESTATE:GetCurrentGame():GetName() == "para" or (not self:GenreIsParaStyle())
	if usePVid and self:HasPreviewVid() then
		return self:GetPreviewVidPath()
	end
	
	return self:GetBGOrSubstitutePath()
end

-- The shortest song still in DDR is KEEP ON MOVIN', at 1:10 (70 seconds).
-- A fair number of the ParaMania files are shorter still!
-- todo: figure out what I could possibly use this check for.
function Song:IsShort()
	return self:MusicLengthSeconds() < 70
end

-- Same as all the Song functions, except for courses.
-- (Courses don't have jacket graphics yet.)
function Course:GetBannerOrSubstitutePath()
	if self:HasBanner() then
		return self:GetBannerPath()
	elseif self:HasBackground() then
		return self:GetBackgroundPath()
	end
	return THEME:GetPathG("Common","fallback jacket")
end

function Course:GetBGOrSubstitutePath()
	if self:HasBackground() then
		return self:GetBackgroundPath()
	elseif self:HasBanner() then
		return self:GetBannerPath()
	end
	return THEME:GetPathG("Common","fallback jacket")
end

-- oops
function Course:GetJacketOrSubstitutePath()
	return self:GetBGOrSubstitutePath()
end

-- Slightly different to Steps:GetAuthorCredit()
-- in that it returns "Unknown" if the author field is blank.
function Steps:Author()
	return self:GetAuthorCredit() ~= "" and self:GetAuthorCredit() or THEME:GetString("Misc", "Unknown")
end

-- Similarly, returns the default difficulty level name if there's no title;
-- or the title is the same as the author credit, which will probably be the case for every non-.ssc file
function Steps:Title()
	return (self:GetChartName() ~= "" and self:GetChartName() ~= self:GetAuthorCredit()) and self:GetChartName() or CustomDifficultyToLocalizedString(ToEnumShortString(self:GetDifficulty()))
end

-- Not used too often, but still checks for weird cases where a trail only uses one song.
function Trail:AllSongsAreTheSame()
	local reference = self:GetTrailEntry(0):GetSong()
	
	for entry in ivalues(self:GetTrailEntries()) do
		if entry:GetSong() ~= reference then return false end
	end
	return true
end

-- Oops.
function Trail:Title()
	return CustomDifficultyToLocalizedString(ToEnumShortString(self:GetDifficulty()))
end
function Trail:IsAutogen()
	return false
end

-- Consistently coloring difficulty text!
function BitmapText:DifficultyColoring(color, isAutogen)
	self:diffuse(color):strokecolor(Color.Black)
	if isAutogen then
		self:diffuse(Color.Black):strokecolor(color)
	end
	return self
end

--Since it's not hard-coded in the API; get a song group's jacket.
--However, it will only return if the file name, extension included, is exactly "jacket.png".
--todo: make it looser without ripping waiei off entirely
function GetSongGroupJacketOrBanner(group, skipJacket)
	local path = "/Songs/" .. group .. "/jacket.png"
	
	if FILEMAN:DoesFileExist(path) and not skipJacket then
		return path
	elseif FILEMAN:DoesFileExist(SONGMAN:GetSongGroupBannerPath(group)) then
		return SONGMAN:GetSongGroupBannerPath(group)
	end
	return nil
end

function StepsDispRowDesc(self,param)
	if not param.Steps then return end -- failsafe
	-- Well, if the chart has a proper title, replace the text with it
	self:settext( param.Steps:Title() )
	
	local theColor = param.CustomDifficulty and CustomDifficultyToColor(param.CustomDifficulty, param.Steps:Title()) or Color.Black
	local isAutogen = (param.Steps and param.Steps:IsAutogen()) and true or false
	self:DifficultyColoring(theColor, isAutogen)
end

-- I was half asleep when I wrote this, so it only handles squares.
-- It does pretty good at its sole job, though!
function Sprite:scaletoclipped(width, height)
	self:scaletocover(-width/2,-height/2,width/2,height/2)
	
	local cropFactor = clamp( (math.min(self:GetWidth(),self:GetHeight()) / math.max(self:GetWidth(),self:GetHeight())) , 0, 1)
	cropFactor = scale(cropFactor, 0, 1, 1, 0)
	if self:GetHeight() > self:GetWidth() then
		self:cropleft(0):cropright(0):croptop(cropFactor/2):cropbottom(cropFactor/2)
	elseif self:GetHeight() < self:GetWidth() then
		self:cropleft(cropFactor/2):cropright(cropFactor/2):croptop(0):cropbottom(0)
	else
		self:cropleft(0):cropright(0):croptop(0):cropbottom(0)
	end
	
	return self
end

function ThreeOrFive()
	return PREFSMAN:GetPreference("ThreeKeyNavigation") and "_3" or "_5"
end
