<commit_before>package com.mrpowergamerbr.loritta.commands.vanilla.utils

import com.github.kevinsawicki.http.HttpRequest
import com.github.salomonbrys.kotson.*
import com.mrpowergamerbr.loritta.Loritta
import com.mrpowergamerbr.loritta.commands.AbstractCommand
import com.mrpowergamerbr.loritta.commands.CommandCategory
import com.mrpowergamerbr.loritta.commands.CommandContext
import com.mrpowergamerbr.loritta.utils.*
import com.mrpowergamerbr.loritta.utils.locale.BaseLocale
import net.dv8tion.jda.core.EmbedBuilder
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.safety.Whitelist
import java.awt.*
import java.awt.image.BufferedImage
import java.io.File
import java.io.IOException
import javax.imageio.ImageIO

class LyricsCommand : AbstractCommand("lyrics", listOf("letra", "letras"), category = CommandCategory.UTILS) {
	override fun getDescription(locale: BaseLocale): String {
		return locale.get("LYRICS_Description")
	}

	override fun getUsage(): String {
		return "url conteúdo"
	}

	override fun getExample(): List<String> {
		return listOf("she - Atomic")
	}

	override fun run(context: CommandContext, locale: BaseLocale) {
		val args = context.rawArgs

		val joined = args.joinToString(" ")
		val split = joined.split(" - ")

		if (split.size == 2) {
			val artist = split[0]
			val musicName = split[1]
			// Nós iremos verificar as lyrics em mútiplos websites, o primeiro que der certo, nós iremos usar
			// É necessário posicionar na ordem de mais confiável -> menos confiável
			val songInfo = retrieveSongInfoFromLyricWikia(artist, musicName)
					?: retrieveSongInfoFromGenius(artist, musicName)
					?: retrieveSongInfoFromLyricsOvh(artist, musicName)
					?: retrieveSongInfoFromSongLyrics(artist, musicName)
					?: retrieveSongInfoFromLetrasMus(artist, musicName)
					?: retrieveSongInfoFromVagalume(artist, musicName) // Pesquisa no Vagalume, isto deve ser a última opção!

			if (songInfo == null) {
				context.reply(
						LoriReply(
								"Eu não consegui encontrar a música... Desculpe pela inconveniência",
								Constants.ERROR
						)
				)
				return
			}

			val lyrics = songInfo.lyrics

			// Vamos pegar a versão "compactada" da lyric
			val compactLyrics = getCompactLyricsFromLyrics(lyrics.split("\n"))
			// Para ficar melhor para ver, nós iremos separar em colunas
			val columns = divideLyricsInColumns(compactLyrics)

			val lyricFont = Constants.VOLTER.deriveFont(18f)
			val fallbackFont = Constants.JACKEY.deriveFont(18f)

			val c = Canvas() // Canvas funciona até em headless mode, e é um jeito para a gente conseguir pegar as font metrics da fonte!
			val lyricFontMetrics = c.getFontMetrics(lyricFont)
			val fallbackFontMetrics = c.getFontMetrics(fallbackFont)

			// Fazer por colunas é mais... difícil do que parece na verdade!
			var imageWidth = 0
			for (column in columns) {
				val biggestString = column.maxBy { getStringWidth(it, lyricFont, lyricFontMetrics, fallbackFontMetrics) }!!
				imageWidth += getStringWidth(biggestString, lyricFont, lyricFontMetrics, fallbackFontMetrics) + 4
			}

			var imageHeight = 22

			for (line in columns.sortedByDescending { it.size }[0]) { // Agora nós iremos pegar a coluna que tem mais letras
				if (line.isBlank()) {
					imageHeight += 6
					continue
				}
				imageHeight += lyricFontMetrics.height
			}

			val image = BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB)
			val graphics = image.graphics

			val cover = getCoverArt(songInfo.albumUrl)

			val averageColor = cover!!.getScaledInstance(1, 1, BufferedImage.SCALE_AREA_AVERAGING)
					.toBufferedImage()
					.getRGB(0, 0)

			drawCoverArt(graphics, cover, imageWidth, imageHeight, Math.max(imageWidth, imageHeight))

			graphics.color = Color(0, 0, 0, 220)
			graphics.fillRect(0, 0, imageWidth, imageHeight)

			graphics.font = lyricFont
			graphics.color = Color.BLACK
			var x = 2
			var y = 22

			for (column in columns) {
				val originalX = x
				for (line in column) {
					if (line.isBlank()) {
						y += 6
						continue
					}

					for (ch in line) {
						graphics.color = Color.BLACK
						if (lyricFont.canDisplay(ch)) {
							graphics.font = lyricFont
						} else {
							graphics.font = fallbackFont
						}

						graphics.drawString(ch.toString(), x - 2, y)
						graphics.drawString(ch.toString(), x + 2, y)
						graphics.drawString(ch.toString(), x, y + 2)
						graphics.drawString(ch.toString(), x, y - 2)

						graphics.color = Color.WHITE
						graphics.drawString(ch.toString(), x, y)
						x += graphics.fontMetrics.charWidth(ch)
					}
					y += graphics.fontMetrics.height
					x = originalX
				}


				val biggestString = column.maxBy { it.length }!!
				x += getStringWidth(biggestString, lyricFont, lyricFontMetrics, fallbackFontMetrics) + 2
				y = 22
			}

			val embed = EmbedBuilder().apply {
				setTitle("\uD83C\uDFB6\uD83D\uDCC4 ${songInfo.artistName} - ${songInfo.songName}")
				setImage("attachment://lyrics.png")
				setColor(averageColor)
			}

			context.sendFile(
					image,
					"lyrics.png",
					embed.build()
			)
		}<commit_msg>:pencil: | Adicionar modo "low resolution" no "+lyrics", pequenas correções no "+lyrics"<commit_after>package com.mrpowergamerbr.loritta.commands.vanilla.utils

import com.github.kevinsawicki.http.HttpRequest
import com.github.salomonbrys.kotson.*
import com.mrpowergamerbr.loritta.Loritta
import com.mrpowergamerbr.loritta.commands.AbstractCommand
import com.mrpowergamerbr.loritta.commands.CommandCategory
import com.mrpowergamerbr.loritta.commands.CommandContext
import com.mrpowergamerbr.loritta.utils.*
import com.mrpowergamerbr.loritta.utils.locale.BaseLocale
import net.dv8tion.jda.core.EmbedBuilder
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.safety.Whitelist
import java.awt.*
import java.awt.image.BufferedImage
import java.io.File
import java.io.IOException
import javax.imageio.ImageIO

class LyricsCommand : AbstractCommand("lyrics", listOf("letra", "letras"), category = CommandCategory.UTILS) {
	override fun getDescription(locale: BaseLocale): String {
		return locale.get("LYRICS_Description")
	}

	override fun getUsage(): String {
		return "artista - nome da música"
	}

	override fun getExample(): List<String> {
		return listOf(
				"she - Atomic",
				"she - Chiptune Memories",
				"C418 - tsuki no koibumi",
				"MC Hariel - Tá Fácil Dizer Que Me Ama",
				"<NAME> - Jun<NAME>",
				"Pusher - Clear",
				"Sega - Sonic Boom",
				"Macklemore & <NAME> - White Walls"
		)
	}

	override fun run(context: CommandContext, locale: BaseLocale) {
		val args = context.rawArgs

		val joined = args.joinToString(" ")
		val split = joined.split(" - ")

		if (split.size == 2) {
			val artist = split[0]
			val musicName = split[1]
			// Nós iremos verificar as lyrics em mútiplos websites, o primeiro que der certo, nós iremos usar
			// É necessário posicionar na ordem de mais confiável -> menos confiável
			val songInfo = retrieveSongInfoFromLyricWikia(artist, musicName)
					?: retrieveSongInfoFromGenius(artist, musicName)
					?: retrieveSongInfoFromLyricsOvh(artist, musicName)
					?: retrieveSongInfoFromSongLyrics(artist, musicName)
					?: retrieveSongInfoFromLetrasMus(artist, musicName)
					?: retrieveSongInfoFromVagalume(artist, musicName) // Pesquisa no Vagalume, isto deve ser a última opção!

			if (songInfo == null) {
				context.reply(
						LoriReply(
								"${locale["LYRICS_CouldntFind"]} ${locale["ERROR_SorryForTheInconvenience"]} \uD83D\uDE2D",
								Constants.ERROR
						)
				)
				return
			}

			val lyrics = songInfo.lyrics

			// Vamos pegar a versão "compactada" da lyric
			val compactLyrics = getCompactLyricsFromLyrics(lyrics.split("\n"))
			// Para ficar melhor para ver, nós iremos separar em colunas
			val columns = divideLyricsInColumns(compactLyrics)

			val useHighResolution = 3 > columns.size // Para evitar OutOfMemoryExceptions, vamos fazer fallback de resolução
			val fontSize = if (useHighResolution) 18f else 9f
			val initialImageHeight = if (useHighResolution) 22 else 11
			val blankHeight = if (useHighResolution) 6 else 3
			val outlinePadding = if (useHighResolution) 2 else 1

			val lyricFont = Constants.VOLTER.deriveFont(fontSize)
			val fallbackFont = Constants.JACKEY.deriveFont(fontSize)

			val c = Canvas() // Canvas funciona até em headless mode, e é um jeito para a gente conseguir pegar as font metrics da fonte!
			val lyricFontMetrics = c.getFontMetrics(lyricFont)
			val fallbackFontMetrics = c.getFontMetrics(fallbackFont)

			// Fazer por colunas é mais... difícil do que parece na verdade!
			var imageWidth = 0
			for (column in columns) {
				val biggestString = column.maxBy { getStringWidth(it, lyricFont, lyricFontMetrics, fallbackFontMetrics) }!!
				imageWidth += getStringWidth(biggestString, lyricFont, lyricFontMetrics, fallbackFontMetrics) + 4
			}

			var imageHeight = initialImageHeight

			for (line in columns.sortedByDescending { it.size }[0]) { // Agora nós iremos pegar a coluna que tem mais letras
				if (line.isBlank()) {
					imageHeight += blankHeight
					continue
				}
				imageHeight += lyricFontMetrics.height
			}

			val image = BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB)
			val graphics = image.graphics

			val cover = getCoverArt(songInfo.albumUrl)

			val averageColor = cover!!.getScaledInstance(1, 1, BufferedImage.SCALE_AREA_AVERAGING)
					.toBufferedImage()
					.getRGB(0, 0)

			drawCoverArt(graphics, cover, imageWidth, imageHeight, Math.max(imageWidth, imageHeight))

			graphics.color = Color(0, 0, 0, 220)
			graphics.fillRect(0, 0, imageWidth, imageHeight)

			graphics.font = lyricFont
			graphics.color = Color.BLACK
			var x = 2
			var y = initialImageHeight

			for (column in columns) {
				val originalX = x
				for (line in column) {
					if (line.isBlank()) {
						y += blankHeight
						continue
					}

					for (ch in line) {
						graphics.color = Color.BLACK
						if (lyricFont.canDisplay(ch)) {
							graphics.font = lyricFont
						} else {
							graphics.font = fallbackFont
						}

						graphics.drawString(ch.toString(), x - outlinePadding, y)
						graphics.drawString(ch.toString(), x + outlinePadding, y)
						graphics.drawString(ch.toString(), x, y + outlinePadding)
						graphics.drawString(ch.toString(), x, y - outlinePadding)

						graphics.color = Color.WHITE
						graphics.drawString(ch.toString(), x, y)
						x += graphics.fontMetrics.charWidth(ch)
					}
					y += graphics.fontMetrics.height
					x = originalX
				}


				val biggestString = column.maxBy { it.length }!!
				x += getStringWidth(biggestString, lyricFont, lyricFontMetrics, fallbackFontMetrics) + 2
				y = initialImageHeight
			}

			val embed = EmbedBuilder().apply {
				setTitle("\uD83C\uDFB6\uD83D\uDCC4 ${songInfo.artistName} - ${songInfo.songName}")
				setImage("attachment://lyrics.png")
				setColor(averageColor)
			}

			context.sendFile(
					image,
					"lyrics.png",
					context.getAsMention(true),
					embed.build()
			)
		} else {
			context.explain()
		}
{'fraction_non_alphanumeric': 0.10194875496210755, 'fraction_numerical': 0.007939372067845544, 'mean_word_length': 3.6091476091476093, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5714416', 'n_tokens_mistral': 4428, 'n_tokens_neox': 3588, 'n_words': 866}