/*  smplayer, GUI front-end for mplayer.
    Copyright (C) 2006-2025 Ricardo Villalba <ricardo@smplayer.info>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "subtracks.h"
#include "deviceinfo.h"
#include <QDir>
#include <QDebug>

void MplayerProcess::initializeOptionVars() {
	qDebug("MplayerProcess::initializeOptionVars");
	PlayerProcess::initializeOptionVars();
}

void MplayerProcess::setMedia(const QString & media, bool is_playlist) {
	if (is_playlist) arg << "-playlist";
	arg << media;
}

void MplayerProcess::setPredefinedOptions() {
	arg << "-noquiet" << "-slave" << "-identify";
}

void MplayerProcess::disableConfig() {
}

void MplayerProcess::disableInput() {
	arg << "-nomouseinput";

#if !defined(Q_OS_WIN) && !defined(Q_OS_OS2)
	arg << "-input" << "nodefault-bindings:conf=/dev/null";
#endif
}

#ifdef CAPTURE_STREAM
void MplayerProcess::setCaptureDirectory(const QString & dir) {
	PlayerProcess::setCaptureDirectory(dir);
	if (!capture_filename.isEmpty()) {
		arg << "-capture" << "-dumpfile" << capture_filename;
	}
}
#endif

void MplayerProcess::enableScreenshots(const QString & dir, const QString & /* templ */, const QString & /* format */) {
	QString f = "screenshot";
	if (!dir.isEmpty()) {
		QString d = QDir::toNativeSeparators(dir);
		f += "="+ d + "/shot";
		arg << "-vf-add" << f;
	}
}

void MplayerProcess::setOption(const QString & option_name, const QVariant & value) {
#if defined(USE_COREVIDEO_BUFFER) || defined(USE_SHM)
	if (option_name == "vo") {
		QString buffer_name = QString("/smplayer-%1").arg(QCoreApplication::applicationPid());
		QString vo = value.toString();
		qDebug() << "MplayerProcess::setOption: vo:" << vo;
		#ifdef USE_COREVIDEO_BUFFER
		if (vo.startsWith("corevideo")) {
			vo = QString("corevideo:buffer_name=%1").arg(buffer_name);
			arg << "-vo" << vo + ",";
			arg << "-vf-add" << "format=UYVY";
			return;
		}
		#endif
		#ifdef USE_SHM
		if (vo.startsWith("shm")) {
			vo = QString("shm:buffer_name=%1").arg(buffer_name);
			arg << "-vo" << vo + ",";
			return;
		}
		#endif
	}
	else
	if (option_name == "wid") {
		foreach(QString s, arg) {
			if (s.contains("corevideo") || s.contains("shm")) {
				qDebug("MplayerProcess::setOption: wid ignored");
				return;
			}
		}
	}
#endif

	if (option_name == "ao") {
		QString ao = value.toString();
		if (ao.contains(":")) {
			QStringList l = DeviceInfo::extractDevice(ao);
			qDebug() << "MplayerProcess::setOption: ao:" << l;
			if (l.count() > 1) {
				#ifndef Q_OS_WIN
				if (l[0] == "alsa") {
					ao = "alsa:device=hw=" + l[1];
				}
				else
				if (l[0] == "pulse") {
					ao = "pulse::" + l[1];
				}
				#else
				if (l[0] == "dsound") {
					ao = "dsound:device=" + l[1];
				}
				#endif
			}
		}
		arg << "-ao" << ao + ",";
	}
	else
	if (option_name == "cache") {
		int cache = value.toInt();
		if (cache > 31) {
			arg << "-cache" << value.toString();
		} else {
			arg << "-nocache";
		}
	}
	else
	if (option_name == "cache_auto") {
		// Nothing to do
	}
	else
	if (option_name == "stop-xscreensaver") {
		bool stop_ss = value.toBool();
		if (stop_ss) arg << "-stop-xscreensaver"; else arg << "-nostop-xscreensaver";
	}
	else
	if (option_name == "correct-pts") {
		bool b = value.toBool();
		if (b) arg << "-correct-pts"; else arg << "-nocorrect-pts";
	}
	else
	if (option_name == "framedrop") {
		QString o = value.toString();
		if (o.contains("vo"))  arg << "-framedrop";
		if (o.contains("decoder")) arg << "-hardframedrop";
	}
	else
	if (option_name == "osd-level") {
		arg << "-osdlevel" << value.toString();
	}
	else
	if (option_name == "osd-fractions") {
	}
	else
	if (option_name == "osd-bar-pos") {
		int position = value.toInt();
		if (position >= 0 && position <= 100) {
			arg << "-progbar-align" << QString::number(position);
		}
	}
	else
	if (option_name == "osd-scale") {
		arg << "-subfont-osd-scale" << value.toString();
	}
	else
	if (option_name == "verbose") {
		arg << "-v";
	}
	else
	if (option_name == "enable_streaming_sites_support") {
		// Not supported
	}
	else
	if (option_name == "ytdl_quality") {
		// Not supported
	}
	else
	if (option_name == "ytdl_path") {
		// Not supported
	}
	else
	if (option_name == "hwdec") {
		// Not supported
	}
	else
	if (option_name == "fontconfig") {
		bool b = value.toBool();
		if (b) arg << "-fontconfig"; else arg << "-nofontconfig";
	}
	else
	if (option_name == "mute") {
		// Not supported
	}
	else
	if (option_name == "softvol") {
		if (value.toString() != "off") {
			int v = value.toInt();
			if (v < 100) v = 100;
			arg << "-softvol" << "-softvol-max" << QString::number(v);
		}
	}
	else
	if (option_name == "channels") {
		QString v;
		switch (value.toInt()) {
			case 2:
			case 4:
			case 6:
			case 8: v = QString::number(value.toInt());
		}
		if (!v.isEmpty()) arg << "-channels" << v;
	}
	else
	if (option_name == "keepaspect" ||
	    option_name == "dr" || option_name == "double" ||
	    option_name == "fs" || option_name == "slices" ||
	    option_name == "flip-hebrew")
	{
		bool b = value.toBool();
		if (b) arg << "-" + option_name; else arg << "-no" + option_name;
	}
	else {
		arg << "-" + option_name;
		if (!value.isNull()) arg << value.toString();
	}
}

void MplayerProcess::addUserOption(const QString & option) {
	arg << option;
}

void MplayerProcess::setSubEncoding(const QString & codepage, const QString & enca_lang) {
	QString encoding;
	if (!enca_lang.isEmpty()) {
		encoding = "enca:"+ enca_lang;
		if (!codepage.isEmpty()) {
			encoding += ":"+ codepage;
		}
	}
	else
	if (!codepage.isEmpty()) {
		encoding = codepage;
	}

	if (!encoding.isEmpty()) {
		arg << "-subcp" << encoding;
	}
}

void MplayerProcess::addVF(const QString & filter_name, const QVariant & value) {
	QString option = value.toString();

	if (filter_name == "blur" || filter_name == "sharpen") {
		arg << "-vf-add" << "unsharp=" + option;
	}
	else
	if (filter_name == "deblock") {
		arg << "-vf-add" << "pp=" + option;
	}
	else
	if (filter_name == "dering") {
		arg << "-vf-add" << "pp=dr";
	}
	else
	if (filter_name == "postprocessing") {
		arg << "-vf-add" << "pp";
	}
	else
	if (filter_name == "lb" || filter_name == "l5") {
		arg << "-vf-add" << "pp=" + filter_name;
	}
	else
	if (filter_name == "subs_on_screenshots") {
		if (option == "ass") {
			arg << "-vf-add" << "ass";
		} else {
			arg << "-vf-add" << "expand=osd=1";
		}
	}
	else
	if (filter_name == "flip") {
		// expand + flip doesn't work well, a workaround is to add another
		// filter between them, so that's why harddup is here
		arg << "-vf-add" << "harddup,flip";
	}
	else
	if (filter_name == "rotate") {
		if (option == "4") {
			arg << "-vf-add" << "mirror,flip";
		} else {
			arg << "-vf-add" << "rotate=" + option;
		}
	}
	else
	if (filter_name == "letterbox") {
		QSize desktop_size = value.toSize();
		double aspect = (double) desktop_size.width() / desktop_size.height();
		arg << "-vf-add" << "expand=aspect=" + QString::number(aspect) + ",harddup";
		// harddup fixes subtitles not disappearing
	}
	/*
	else
	if (filter_name == "expand") {
		arg << "-vf-add" << "expand=" + option + ",harddup";
		// Note: on some videos (h264 for instance) the subtitles doesn't disappear,
		// appearing the new ones on top of the old ones. It seems adding another
		// filter after expand fixes the problem. I chose harddup 'cos I think
		// it will be harmless in mplayer.
	}
	*/
	else {
		QString s = filter_name;
		QString option = value.toString();
		if (!option.isEmpty()) s += "=" + option;
		arg << "-vf-add" << s;
	}
}

void MplayerProcess::addStereo3DFilter(const QString & in, const QString & out) {
	QString filter = "stereo3d=" + in + ":" + out;
	filter += ",scale"; // In my PC it doesn't work without scale :?
	arg << "-vf-add" << filter;
}

void MplayerProcess::setVideoEqualizerOptions(int contrast, int brightness, int hue, int saturation, int gamma, bool soft_eq) {
	if (contrast != 0) arg << "-contrast" << QString::number(contrast);
	if (brightness != 0) arg << "-brightness" << QString::number(brightness);
	if (hue != 0) arg << "-hue" << QString::number(hue);
	if (saturation != 0) arg << "-saturation" << QString::number(saturation);
	if (gamma != 0) arg << "-gamma" << QString::number(gamma);

	if (soft_eq) {
		arg << "-vf-add" << "eq2,hue";
	}
}

void MplayerProcess::addAF(const QString & filter_name, const QVariant & value) {
	QString s = filter_name;
	if (filter_name == "earwax") {
		// Not supported
	}
	else
	if (filter_name == "stereo-mode") {
		QString o = value.toString();
		if (o == "left") arg << "-af-add" << "channels=2:2:0:1:0:0";
		else
		if (o == "right") arg << "-af-add" << "channels=2:2:1:0:1:1";
		else
		if (o == "reverse") arg << "-af-add" << "channels=2:2:0:1:1:0";
		else
		if (o == "mono") arg << "-af-add" << "pan=1:0.5:0.5";
	}
	else
	if (filter_name == "equalizer") {
		AudioEqualizerList l = value.toList();
		QString o = AudioEqualizerHelper::equalizerListToString(l);
		arg << "-af-add" << "equalizer=" + o;
	} else {
		if (!value.isNull()) s += "=" + value.toString();
		arg << "-af-add" << s;
	}
}

void MplayerProcess::quit() {
	sendCommand("quit");
}

void MplayerProcess::setVolume(int v) {
	sendCommand("volume " + QString::number(v) + " 1");
}

void MplayerProcess::setOSD(int o) {
	sendCommand(pausing_prefix + " osd " + QString::number(o));
}

void MplayerProcess::setAudio(int ID) {
	sendCommand("switch_audio " + QString::number(ID));
}

void MplayerProcess::setVideo(int ID) {
	sendCommand("set_property switch_video " + QString::number(ID));
}

void MplayerProcess::setSubtitle(int type, int ID) {
	switch (type) {
		case SubData::Vob:
			sendCommand( "sub_vob " + QString::number(ID) );
			break;
		case SubData::Sub:
			sendCommand( "sub_demux " + QString::number(ID) );
			break;
		case SubData::File:
			sendCommand( "sub_file " + QString::number(ID) );
			break;
		default: {
			qWarning("MplayerProcess::setSubtitle: unknown type!");
		}
	}
}

void MplayerProcess::disableSubtitles() {
	sendCommand("sub_source -1");
}

void MplayerProcess::setSubtitlesVisibility(bool b) {
	sendCommand(QString("sub_visibility %1").arg(b ? 1 : 0));
}

void MplayerProcess::seek(double secs, int mode, bool precise) {
	QString s = QString("seek %1 %2").arg(secs).arg(mode);
	if (precise) s += " 1"; else s += " -1";
	sendCommand(s);
}

void MplayerProcess::mute(bool b) {
	sendCommand(pausing_prefix + " mute " + QString::number(b ? 1 : 0));
}

void MplayerProcess::setPause(bool /*b*/) {
	sendCommand("pause"); // pauses / unpauses
}

void MplayerProcess::frameStep() {
	sendCommand("frame_step");
}

void MplayerProcess::frameBackStep() {
	qDebug("MplayerProcess::frameBackStep: function not supported by mplayer");
	showOSDText(tr("This option is not supported by MPlayer"), 3000, 1);
}


void MplayerProcess::showOSDText(const QString & text, int duration, int level) {
	QString str = QString("osd_show_text \"%1\" %2 %3").arg(text).arg(duration).arg(level);
	if (!pausing_prefix.isEmpty()) str = pausing_prefix + " " + str;
	sendCommand(str);
}

void MplayerProcess::showFilenameOnOSD(int duration) {
	QString s = "${filename}";

	if (!osd_media_info.isEmpty()) s = osd_media_info;

	sendCommand(QString("osd_show_property_text \"%1\" %2 0").arg(s).arg(duration));
}

void MplayerProcess::showMediaInfoOnOSD() {
	showFilenameOnOSD();
}

void MplayerProcess::showTimeOnOSD() {
	sendCommand("osd_show_property_text \"${time_pos} / ${length} (${percent_pos}%)\" 2000 0");
}

void MplayerProcess::setContrast(int value) {
	sendCommand(pausing_prefix + " contrast " + QString::number(value) + " 1");
}

void MplayerProcess::setBrightness(int value) {
	sendCommand(pausing_prefix + " brightness " + QString::number(value) + " 1");
}

void MplayerProcess::setHue(int value) {
	sendCommand(pausing_prefix + " hue " + QString::number(value) + " 1");
}

void MplayerProcess::setSaturation(int value) {
	sendCommand(pausing_prefix + " saturation " + QString::number(value) + " 1");
}

void MplayerProcess::setGamma(int value) {
	sendCommand(pausing_prefix + " gamma " + QString::number(value) + " 1");
}

void MplayerProcess::setChapter(int ID) {
	sendCommand("seek_chapter " + QString::number(ID) +" 1");
}

void MplayerProcess::nextChapter() {
	sendCommand("seek_chapter 1 0");
}

void MplayerProcess::previousChapter() {
	sendCommand("seek_chapter -1 0");
}

void MplayerProcess::setExternalSubtitleFile(const QString & filename) {
	sendCommand("sub_load \""+ filename +"\"");
}

void MplayerProcess::setSubPos(int pos) {
	sendCommand("sub_pos " + QString::number(pos) + " 1");
}

void MplayerProcess::setSubScale(double value) {
	sendCommand("sub_scale " + QString::number(value) + " 1");
}

void MplayerProcess::setSubStep(int value) {
	sendCommand("sub_step " + QString::number(value));
}

#ifdef MPV_SUPPORT
void MplayerProcess::seekSub(int /*value*/) {
	/* Not supported */
	showOSDText(tr("This option is not supported by MPlayer"), 3000, 1);
};
#endif

void MplayerProcess::setSubForcedOnly(bool b) {
	sendCommand(QString("forced_subs_only %1").arg(b ? 1 : 0));
}

void MplayerProcess::setSpeed(double value) {
	sendCommand("speed_set " + QString::number(value));
}

void MplayerProcess::enableKaraoke(bool b) {
	if (b) sendCommand("af_add karaoke"); else sendCommand("af_del karaoke");
}

void MplayerProcess::enableExtrastereo(bool b) {
	if (b) sendCommand("af_add extrastereo"); else sendCommand("af_del extrastereo");
}

void MplayerProcess::enableVolnorm(bool b, const QString & option) {
	if (b) sendCommand("af_add volnorm=" + option); else sendCommand("af_del volnorm");
}

void MplayerProcess::setAudioEqualizer(AudioEqualizerList l) {
	QString values = AudioEqualizerHelper::equalizerListToString(l);
	sendCommand("af_cmdline equalizer " + values);
}

void MplayerProcess::setAudioDelay(double delay) {
	sendCommand(pausing_prefix + " audio_delay " + QString::number(delay) +" 1");
}

void MplayerProcess::setSubDelay(double delay) {
	sendCommand(pausing_prefix + " sub_delay " + QString::number(delay) +" 1");
}

void MplayerProcess::setLoop(int v) {
	sendCommand(QString("loop %1 1").arg(v));
}

void MplayerProcess::setAMarker(int /*sec*/) {
	/* Not supported */
}

void MplayerProcess::setBMarker(int /*sec*/) {
	/* Not supported */
}

void MplayerProcess::clearABMarkers() {
	/* Not supported */
}

void MplayerProcess::takeScreenshot(ScreenshotType t, bool /*include_subtitles*/) {
	if (t == Single) {
		sendCommand(pausing_prefix + " screenshot 0");
	} else {
		sendCommand("screenshot 1");
	}
}

#ifdef CAPTURE_STREAM
void MplayerProcess::switchCapturing() {
	sendCommand("capturing");
}
#endif

void MplayerProcess::setTitle(int ID) {
	sendCommand("switch_title " + QString::number(ID));
}

#if DVDNAV_SUPPORT
void MplayerProcess::discSetMousePos(int x, int y) {
	sendCommand(QString("set_mouse_pos %1 %2").arg(x).arg(y));
}

void MplayerProcess::discButtonPressed(const QString & button_name) {
	sendCommand("dvdnav " + button_name);
}
#endif

void MplayerProcess::setAspect(double aspect) {
	sendCommand("switch_ratio " + QString::number(aspect));
}

void MplayerProcess::setFullscreen(bool b) {
	sendCommand(QString("vo_fullscreen %1").arg(b ? "1" : "0"));
}

#if PROGRAM_SWITCH
void MplayerProcess::setTSProgram(int ID) {
	sendCommand("set_property switch_program " + QString::number(ID) );
	sendCommand("get_property switch_audio");
	sendCommand("get_property switch_video");
}
#endif

void MplayerProcess::toggleDeinterlace() {
	sendCommand("step_property deinterlace");
}

void MplayerProcess::askForLength() {
	sendCommand(pausing_prefix + " get_property length");
}

void MplayerProcess::setOSDScale(double /*value*/) {
	// not available
	/* sendCommand("set_property subfont-osd-scale " + QString::number(value)); */
}

void MplayerProcess::changeVF(const QString & filter, bool enable, const QVariant & option) {
	// not supported
	Q_UNUSED(filter);
	Q_UNUSED(enable);
	Q_UNUSED(option);
}

void MplayerProcess::changeAF(const QString & filter, bool enable, const QVariant & option) {
	/* Not used yet */
	Q_UNUSED(filter);
	Q_UNUSED(enable);
	Q_UNUSED(option);
};

void MplayerProcess::changeStereo3DFilter(bool /*enable*/, const QString & /*in*/, const QString & /*out*/) {
	// not supported
}

void MplayerProcess::setSubStyles(const AssStyles & styles, const QString & assStylesFile) {
	if (assStylesFile.isEmpty()) {
		qWarning("MplayerProcess::setSubStyles: assStylesFile is invalid");
		return;
	}

	// Load the styles.ass file
	if (!QFile::exists(assStylesFile)) {
		// If file doesn't exist, create it
		styles.exportStyles(assStylesFile);
	}
	if (QFile::exists(assStylesFile)) {
		setOption("ass-styles", assStylesFile);
	} else {
		qWarning("MplayerProcess::setSubStyles: '%s' doesn't exist", assStylesFile.toUtf8().constData());
	}
}
