/***

  Olive - Non-Linear Video Editor
  Copyright (C) 2022 Olive Team
  Modifications Copyright (C) 2025 mikesolar

  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 3 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, see <http://www.gnu.org/licenses/>.

***/

#ifndef VIDEOPARAMS_H
#define VIDEOPARAMS_H

#include <olive/core/core.h>
#include <QVector2D>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <string>

namespace olive
{

using namespace core;

class VideoParams {
public:
	enum Interlacing {
		kInterlaceNone,
		kInterlacedTopFirst,
		kInterlacedBottomFirst
	};

	enum Type { kVideoTypeVideo, kVideoTypeStill, kVideoTypeImageSequence };
	enum ColorRange {
		kColorRangeLimited, // 16_235
		kColorRangeFull, // 0-255

		kColorRangeDefault = kColorRangeLimited
	};

	VideoParams();
	VideoParams(int width, int height, PixelFormat format, int nb_channels,
				const rational &pixel_aspect_ratio = 1,
				Interlacing interlacing = kInterlaceNone, int divider = 1);
	VideoParams(int width, int height, int depth, PixelFormat format,
				int nb_channels, const rational &pixel_aspect_ratio = 1,
				Interlacing interlacing = kInterlaceNone, int divider = 1);
	VideoParams(int width, int height, const rational &time_base,
				PixelFormat format, int nb_channels,
				const rational &pixel_aspect_ratio = 1,
				Interlacing interlacing = kInterlaceNone, int divider = 1);

	int width() const
	{
		return width_;
	}

	void set_width(int width)
	{
		width_ = width;
		calculate_effective_size();
	}

	/**
   * @brief Returns width multiplied by pixel aspect ratio where applicable
   */
	int square_pixel_width() const
	{
		return par_width_;
	}

	QVector2D resolution() const
	{
		return QVector2D(width_, height_);
	}

	QVector2D square_resolution() const
	{
		return QVector2D(par_width_, height_);
	}

	int height() const
	{
		return height_;
	}

	void set_height(int height)
	{
		height_ = height;
		calculate_effective_size();
	}

	int depth() const
	{
		return depth_;
	}

	void set_depth(int depth)
	{
		depth_ = depth;
		calculate_effective_size();
	}

	bool is_3d() const
	{
		return depth_ > 1;
	}

	const rational &time_base() const
	{
		return time_base_;
	}

	void set_time_base(const rational &r)
	{
		time_base_ = r;
	}

	rational frame_rate_as_time_base() const
	{
		return frame_rate_.flipped();
	}

	int divider() const
	{
		return divider_;
	}

	void set_divider(int d)
	{
		divider_ = d;
		calculate_effective_size();
	}

	int effective_width() const
	{
		return effective_width_;
	}

	int effective_height() const
	{
		return effective_height_;
	}

	int effective_depth() const
	{
		return effective_depth_;
	}

	PixelFormat format() const
	{
		return format_;
	}

	void set_format(PixelFormat f)
	{
		format_ = f;
	}

	int channel_count() const
	{
		return channel_count_;
	}

	void set_channel_count(int c)
	{
		channel_count_ = c;
	}
	void set_channel_count(const std::string &ofxComponent);
	const rational &pixel_aspect_ratio() const
	{
		return pixel_aspect_ratio_;
	}

	void set_pixel_aspect_ratio(const rational &r)
	{
		pixel_aspect_ratio_ = r;
		validate_pixel_aspect_ratio();
	}

	Interlacing interlacing() const
	{
		return interlacing_;
	}

	void set_interlacing(Interlacing i)
	{
		interlacing_ = i;
	}

	static int generate_auto_divider(qint64 width, qint64 height);

	bool is_valid() const;

	bool operator==(const VideoParams &rhs) const;
	bool operator!=(const VideoParams &rhs) const;

	static int GetBytesPerChannel(PixelFormat format);
	int GetBytesPerChannel() const
	{
		return GetBytesPerChannel(format_);
	}

	static int GetBytesPerPixel(PixelFormat format, int channels);
	int GetBytesPerPixel() const
	{
		return GetBytesPerPixel(format_, channel_count_);
	}

	static int GetBufferSize(int width, int height, PixelFormat format,
							 int channels)
	{
		return width * height * GetBytesPerPixel(format, channels);
	}
	int GetBufferSize() const
	{
		return GetBufferSize(width_, height_, format_, channel_count_);
	}

	static QString GetNameForDivider(int div);

	static bool FormatIsFloat(PixelFormat format)
	{
		return format.is_float();
	}

	static QString GetFormatName(PixelFormat format);

	static int GetDividerForTargetResolution(int src_width, int src_height,
											 int dst_width, int dst_height);

	static const int kInternalChannelCount;

	static const rational kPixelAspectSquare;
	static const rational kPixelAspectNTSCStandard;
	static const rational kPixelAspectNTSCWidescreen;
	static const rational kPixelAspectPALStandard;
	static const rational kPixelAspectPALWidescreen;
	static const rational kPixelAspect1080Anamorphic;

	static const QVector<rational> kSupportedFrameRates;
	static const QVector<rational> kStandardPixelAspects;
	static const QVector<int> kSupportedDividers;

	static const int kHSVChannelCount = 3;
	static const int kRGBChannelCount = 3;
	static const int kRGBAChannelCount = 4;

	/**
   * @brief Convert rational frame rate (i.e. flipped timebase) to a user-friendly string
   */
	static QString FrameRateToString(const rational &frame_rate);

	static QStringList GetStandardPixelAspectRatioNames();
	static QString FormatPixelAspectRatioString(const QString &format,
												const rational &ratio);

	static int GetScaledDimension(int dim, int divider);

	bool enabled() const
	{
		return enabled_;
	}

	void set_enabled(bool e)
	{
		enabled_ = e;
	}

	float x() const
	{
		return x_;
	}
	void set_x(float x)
	{
		x_ = x;
	}
	float y() const
	{
		return y_;
	}
	void set_y(float y)
	{
		y_ = y;
	}
	QVector2D offset() const
	{
		return QVector2D(x_, y_);
	}

	int stream_index() const
	{
		return stream_index_;
	}

	void set_stream_index(int s)
	{
		stream_index_ = s;
	}

	Type video_type() const
	{
		return video_type_;
	}

	void set_video_type(Type t)
	{
		video_type_ = t;
	}

	const rational &frame_rate() const
	{
		return frame_rate_;
	}

	void set_frame_rate(const rational &frame_rate)
	{
		frame_rate_ = frame_rate;
	}

	int64_t start_time() const
	{
		return start_time_;
	}

	void set_start_time(int64_t start_time)
	{
		start_time_ = start_time;
	}

	int64_t duration() const
	{
		return duration_;
	}

	void set_duration(int64_t duration)
	{
		duration_ = duration;
	}

	bool premultiplied_alpha() const
	{
		return premultiplied_alpha_;
	}

	void set_premultiplied_alpha(bool premultiplied_alpha)
	{
		premultiplied_alpha_ = premultiplied_alpha;
	}

	const QString &colorspace() const
	{
		return colorspace_;
	}

	void set_colorspace(const QString &c)
	{
		colorspace_ = c;
	}

	const ColorRange &color_range() const
	{
		return color_range_;
	}
	void set_color_range(const ColorRange &color_range)
	{
		color_range_ = color_range;
	}

	int64_t get_time_in_timebase_units(const rational &time) const;

	void Load(QXmlStreamReader *reader);

	void Save(QXmlStreamWriter *writer) const;

private:
	void calculate_effective_size();

	void validate_pixel_aspect_ratio();

	void set_defaults_for_footage();

	void calculate_square_pixel_width();

	int width_;
	int height_;
	int depth_;
	rational time_base_;

	PixelFormat format_;

	int channel_count_;

	rational pixel_aspect_ratio_;

	Interlacing interlacing_;

	int divider_;

	// Cached values
	int effective_width_;
	int effective_height_;
	int effective_depth_;
	int par_width_;

	bool enabled_;
	int stream_index_;
	Type video_type_;
	rational frame_rate_;
	int64_t start_time_;
	int64_t duration_;
	bool premultiplied_alpha_;
	QString colorspace_;
	float x_;
	float y_;
	ColorRange color_range_;
};

}

Q_DECLARE_METATYPE(olive::VideoParams)
Q_DECLARE_METATYPE(olive::VideoParams::Interlacing)

#endif // VIDEOPARAMS_H
