//**************************************************************************
//*                     This file is part of the                           *
//*                MMC - Mpxplay Multimedia Commander                      *
//*                   The source code of MMC is                            *
//*        (C) copyright 1998-2018 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: FFmpeg and hw video filter handling (deinterlace, video-mixer)

#include "moc_video_qt.h"

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG

extern "C" {
 extern unsigned long mpxplay_config_video_audiovisualization_type;
}

static unsigned int videofilter_process_condition_audiovisualization(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe)
{
	if(mpxplay_config_video_audiovisualization_type == DISPQT_AUDIOVISUALIZATIONTYPE_NONE)
		return FALSE;
	if(inframe->channels <= 0)  // non audio frame identification
		return FALSE;
	return TRUE;
}

static unsigned int videofilter_process_condition_yadif(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mmc_config.video_deinterlace_type == DISPQT_VIDEO_DEINTERLACETYPE_ENABLE)
		return TRUE;
	if((mmc_config.video_deinterlace_type == DISPQT_VIDEO_DEINTERLACETYPE_AUTO) && inframe->interlaced_frame)
		return TRUE;
	return FALSE;
}

static unsigned int videofilter_process_condition_vis_avectorscope(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(!videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mpxplay_config_video_audiovisualization_type != DISPQT_AUDIOVISUALIZATIONTYPE_AVECTORSCOPE)
		return FALSE;
//	snprintf(filtername_withconfig, fnwcbuf_size, "avectorscope=size=640x360:mode=lissajous_xy:zoom=1.3");
//	snprintf(filtername_withconfig, fnwcbuf_size, "avectorscope=size=640x360:mode=lissajous_xy:rc=120:gc=40:bc=240:zoom=1.3");
	snprintf(filtername_withconfig, fnwcbuf_size, "avectorscope=size=640x360:mode=lissajous_xy:rc=120:gc=40:bc=240");
	return TRUE;
}

static unsigned int videofilter_process_condition_vis_showfreqs(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(!videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mpxplay_config_video_audiovisualization_type != DISPQT_AUDIOVISUALIZATIONTYPE_SHOWFREQS)
		return FALSE;
	return TRUE;
}

static unsigned int videofilter_process_condition_vis_showspectrum(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(!videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mpxplay_config_video_audiovisualization_type != DISPQT_AUDIOVISUALIZATIONTYPE_SHOWSPECTRUM)
		return FALSE;
	return TRUE;
}

static unsigned int videofilter_process_condition_vis_showcqt(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(!videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mpxplay_config_video_audiovisualization_type != DISPQT_AUDIOVISUALIZATIONTYPE_SHOWCQT)
		return FALSE;
	snprintf(filtername_withconfig, fnwcbuf_size, "showcqt=text=0");
	return TRUE;
}

static unsigned int videofilter_process_condition_vis_showwaves(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(!videofilter_process_condition_audiovisualization(gcfg, inframe))
		return FALSE;
	if(mpxplay_config_video_audiovisualization_type != DISPQT_AUDIOVISUALIZATIONTYPE_SHOWWAVES)
		return FALSE;
	snprintf(filtername_withconfig, fnwcbuf_size, "showwaves=mode=cline:split_channels=1");
	return TRUE;
}

#if 0
static unsigned int videofilter_process_condition_deshake(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if(mmc_config.video_deinterlace_type == DISPQT_VIDEO_DEINTERLACETYPE_ENABLE) // TODO: change variable and constant
	{
		snprintf(filtername_withconfig, fnwcbuf_size, "deshake=search=1:x=%d:y=%d:w=%d:h=%d", inframe->width / 4, inframe->height / 4, inframe->width / 2, inframe->height / 2);
		return TRUE;
	}
	return FALSE;
}
#endif

struct dispqt_videofilter_processdescriptor_s mpxplay_dispqt_videofilter_workerprocess_descriptors_all[] =
{
	{
		"yadif",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		0,
		&videofilter_process_condition_yadif,
		NULL
	},
	{
		"avectorscope",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC,
		&videofilter_process_condition_vis_avectorscope,
		NULL
	},
	{
		"showfreqs",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC,
		&videofilter_process_condition_vis_showfreqs,
		NULL
	},
	{
		"showspectrum",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC,
		&videofilter_process_condition_vis_showspectrum,
		NULL
	},
	{
		"showcqt",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC,
		&videofilter_process_condition_vis_showcqt,
		NULL
	},
	{
		"showwaves",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		DISPQT_VIDEOFILTER_PROCESSFLAG_AUDIOSRC,
		&videofilter_process_condition_vis_showwaves,
		NULL
	}
#if 0 // add vmixer button later
	,
	{
		"deshake",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_DECODER,
		0,
		&videofilter_process_condition_deshake,
		NULL
	}
	// TODO: add normalize
#endif
};

//-----------------------------------------------------------------------------------------------------------------------------------------
// transformers

static unsigned int videofilter_process_condition_pad(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	if((inframe->width & 7) || (inframe->height & 1)) // input frame width is not multiply of 8 or height is not multiply of 2 (other filters require this)
	{
		snprintf(filtername_withconfig, fnwcbuf_size, "pad=width=%d:height=%d", ((inframe->width + 7) & ~7), ((inframe->height + 1) & ~1));
		return TRUE;
	}
	return FALSE;
}

static int videofilter_process_rotate_get_type(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe)
{
	int rot_type = gcfg->video_rotation_type;
	if(rot_type == DISPQT_VIDEO_ROTATIONTYPE_AUTO)
	{
		AVDictionaryEntry *t = av_dict_get(inframe->metadata, "Orientation", NULL, 0);
		if(t && t->value)
		{
			int exif_orientation_num = atoi(t->value);
			switch(exif_orientation_num) // by https://www.exif.org/Exif2-2.PDF -> Page 18, Orientation (alt: https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html)
			{
				//case 1: // no transformation is required
				//case 2: // flip horizontally
				case 3: rot_type = DISPQT_VIDEO_ROTATIONTYPE_ROT180; break;
				//case 4: // flip vertically
				//case 5: // flip horizontally and rotate left 90
				case 6: rot_type = DISPQT_VIDEO_ROTATIONTYPE_ROTR90; break;
				//case 7: // flip horizontally and rotate right 90
				case 8: rot_type = DISPQT_VIDEO_ROTATIONTYPE_ROTL90; break;
			}
		}
	}
	return rot_type;
}

static unsigned int videofilter_process_condition_rotate(struct mmc_dispqt_config_s *gcfg, AVFrame *inframe, char *filtername_withconfig, unsigned int fnwcbuf_size)
{
	int degree = 0;
	switch(videofilter_process_rotate_get_type(gcfg, inframe))
	{
		case DISPQT_VIDEO_ROTATIONTYPE_ROTL90: degree = 270; break;
		case DISPQT_VIDEO_ROTATIONTYPE_ROTR90: degree =  90; break;
		case DISPQT_VIDEO_ROTATIONTYPE_ROT180: degree = 180; break;
		default: return FALSE;
	}
	if((degree == 270) || (degree == 90))
	{
		snprintf(filtername_withconfig, fnwcbuf_size, "rotate=%d*PI/180:ow=ih:oh=iw", degree);
	}
	else
	{
		snprintf(filtername_withconfig, fnwcbuf_size, "rotate=%d*PI/180", degree);
	}
	return TRUE;
}

#if 0
static struct dispqt_videofilter_elemdescriptor_s videofilter_elems_crop[] =
{
	{"w", 0, 1, 0, &mmc_config.video_filter_crop_values[DISPQT_VIDEO_FILTERCROP_ELEM_W], DISPQT_VIDEOFILTER_INVALID_VALUE, DISPQT_VIDEOFILTER_ELEMTYPE_INTEGER, DISPQT_VIDEO_FILTERCROP_ELEM_W},
	{"h", 0, 1, 0, &mmc_config.video_filter_crop_values[DISPQT_VIDEO_FILTERCROP_ELEM_H], DISPQT_VIDEOFILTER_INVALID_VALUE, DISPQT_VIDEOFILTER_ELEMTYPE_INTEGER, DISPQT_VIDEO_FILTERCROP_ELEM_H},
	{"x", 0, 1, 0, &mmc_config.video_filter_crop_values[DISPQT_VIDEO_FILTERCROP_ELEM_X], DISPQT_VIDEOFILTER_INVALID_VALUE, DISPQT_VIDEOFILTER_ELEMTYPE_INTEGER, DISPQT_VIDEO_FILTERCROP_ELEM_X},
	{"y", 0, 1, 0, &mmc_config.video_filter_crop_values[DISPQT_VIDEO_FILTERCROP_ELEM_Y], DISPQT_VIDEOFILTER_INVALID_VALUE, DISPQT_VIDEOFILTER_ELEMTYPE_INTEGER, DISPQT_VIDEO_FILTERCROP_ELEM_Y},
	{NULL, 0, 0, 0, NULL, 0, 0}
};
#endif

struct dispqt_videofilter_processdescriptor_s mpxplay_dispqt_videofilter_transform_descriptors_all[] =
{
	{
		"rotate",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_TRANSFORM,
		0,
		&videofilter_process_condition_rotate,
		NULL
	}
#if 0 // later for EVR (zoom, autocrop)
	,
	{
		"crop",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_TRANSFORM,
		0,
		NULL,
		&videofilter_elems_crop[0]
	}
#endif
};

#endif // MPXPLAY_LINK_ORIGINAL_FFMPEG

//-----------------------------------------------------------------------------------------------------------------------------------------
// eq

static struct dispqt_videofilter_elemdescriptor_s videofilter_elems_eq[] =
{
	{"brightness",   0, 150, DISPQT_VMIXER_VALUE_CENTER, &mmc_config.video_mixer_values[DISPQT_VIDEO_VMIXERTYPE_BRIGHTNESS], DISPQT_VMIXER_VALUE_CENTER, DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT, DISPQT_VIDEO_VMIXERTYPE_BRIGHTNESS},
	{"contrast",   100, 100, DISPQT_VMIXER_VALUE_CENTER, &mmc_config.video_mixer_values[DISPQT_VIDEO_VMIXERTYPE_CONTRAST],   DISPQT_VMIXER_VALUE_CENTER, DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT, DISPQT_VIDEO_VMIXERTYPE_CONTRAST},
	{"saturation", 100, 100, DISPQT_VMIXER_VALUE_CENTER, &mmc_config.video_mixer_values[DISPQT_VIDEO_VMIXERTYPE_SATURATION], DISPQT_VMIXER_VALUE_CENTER, DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT, DISPQT_VIDEO_VMIXERTYPE_SATURATION},
	{NULL, 0, 0, 0, NULL, 0, 0}
};

static struct dispqt_videofilter_elemdescriptor_s videofilter_elems_hue[] =
{
	{"H", 0, 32, DISPQT_VMIXER_VALUE_CENTER, &mmc_config.video_mixer_values[DISPQT_VIDEO_VMIXERTYPE_HUE], DISPQT_VMIXER_VALUE_CENTER, DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT, DISPQT_VIDEO_VMIXERTYPE_HUE},
	{NULL, 0, 0, 0, NULL, 0, 0}
};

#if 0
static struct dispqt_videofilter_elemdescriptor_s videofilter_elems_unsharp[] =
{
	{"la", 0, 50, DISPQT_VMIXER_VALUE_CENTER, &mmc_config.video_mixer_values[DISPQT_VIDEO_VMIXERTYPE_SHARPNESS], DISPQT_VMIXER_VALUE_CENTER, DISPQT_VIDEOFILTER_ELEMTYPE_FLOAT, DISPQT_VIDEO_VMIXERTYPE_SHARPNESS},
	{NULL, 0, 0, 0, NULL, 0, 0}
};
#endif

struct dispqt_videofilter_processdescriptor_s mpxplay_dispqt_videofilter_eq_descriptors_all[] =
{
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
	{
		"pad",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_TRANSFORM,
		DISPQT_VIDEOFILTER_PROCESSFLAG_DEFAULT,
		&videofilter_process_condition_pad,
		NULL
	},
#endif
	{
		"eq",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_VMIXERSLIDER,
		0,
		NULL,
		&videofilter_elems_eq[0]
	},
	{
		"hue",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_VMIXERSLIDER,
		0,
		NULL,
		&videofilter_elems_hue[0]
	}
#if 0 // very slow and non-configurable runtime
	,
	{
		"unsharp", // "unsharp=la=2.0",
		DISPQT_VIDEOFRAMEFILTER_PROCESSTYPE_VMIXERSLIDER,
		0,
		NULL,
		&videofilter_elems_unsharp[0]
	}
#endif
};

#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
unsigned int mpxplay_dispqt_videofilter_workerprocess_nb_descriptors = sizeof(mpxplay_dispqt_videofilter_workerprocess_descriptors_all) / sizeof(struct dispqt_videofilter_processdescriptor_s);
unsigned int mpxplay_dispqt_videofilter_transform_nb_descriptors = sizeof(mpxplay_dispqt_videofilter_transform_descriptors_all) / sizeof(struct dispqt_videofilter_processdescriptor_s);
#endif

unsigned int mpxplay_dispqt_videofilter_eq_nb_descriptors = sizeof(mpxplay_dispqt_videofilter_eq_descriptors_all) / sizeof(struct dispqt_videofilter_processdescriptor_s);
