/////////////////////////////////////////////////////////////////////////////
// Name:        DVD.h
// Purpose:     The class to store a DVD Structure (Titles/Menus)
// Author:      Alex Thuering
// Created:	29.01.2003
// RCS-ID:      $Id: DVD.h,v 1.52 2010/05/03 06:03:20 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#ifndef DVDAUTHOR_H
#define DVDAUTHOR_H

#include "Slideshow.h"
#include <wx/wx.h>
#include <wx/image.h>
#include <wx/dynarray.h>
#include <wx/hashset.h>
#include <wxSVG/mediadec_ffmpeg.h>
#include <map>

using namespace std;

WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, StringSet);

class wxSvgXmlNode;
class wxSvgXmlDocument;
class DVD;
class Menu;
class Cache;

class TextSub {
public:
	TextSub(wxString filename = wxT(""));

	wxString GetFilename() { return m_filename; }
	void SetFilename(wxString value) { m_filename = value; }
	
	wxString GetCharacterSet() { return m_characterSet; }
	void SetCharacterSet(wxString value) { m_characterSet= value; }
	
	wxString GetFontFile();
	
	wxString GetFontFamily() { return m_fontFamily; }
	void SetFontFamily(wxString value) { m_fontFamily = value; }
	
	wxString GetFontStyle() { return m_fontStyle; }
	void SetFontStyle(wxString value) { m_fontStyle = value; }

	double GetFontSize() { return m_fontSize; }
	void SetFontSize(double value) { m_fontSize = value; }
	
	wxAlignment GetAlignment() { return m_alignment; }
	void SetAlignment(wxAlignment value) { m_alignment = value; }
	
	int GetLeftMargin() { return m_leftMargin; }
	void SetLeftMargin(int value) { m_leftMargin = value; }
	
	int GetRightMargin() { return m_rightMargin; }
	void SetRightMargin(int value) { m_rightMargin = value; }
	
	int GetTopMargin() { return m_topMargin; }
	void SetTopMargin(int value) { m_topMargin = value; }
	
	int GetBottomMargin() { return m_bottomMargin; }
	void SetBottomMargin(int value) { m_bottomMargin = value; }
	
	double GetSubtitleFps() { return m_subtitleFps; }
	void SetSubtitleFps(double value) { m_subtitleFps = value; }
	
	double GetMovieFps() { return m_movieFps; }
	void SetMovieFps(double value) { m_movieFps = value; }
	
	int GetMovieWidth() { return m_movieWidth; }
	void SetMovieWidth(int value) { m_movieWidth = value; }
	
	int GetMovieHeight() { return m_movieHeight; }
	void SetMovieHeight(int value) { m_movieHeight = value; }
	
	wxSvgXmlNode* GetXML(DVDFileType type);
	bool PutXML(wxSvgXmlNode* node);
	
	/** saves a configuration for spumux */
	bool SaveSpumux(wxString filename);
	
private:
    wxString m_filename;
    wxString m_characterSet;
    wxString m_fontFamily;
    wxString m_fontStyle;
    double m_fontSize;
    wxAlignment m_alignment;
    int m_leftMargin;
    int m_rightMargin;
    int m_topMargin;
    int m_bottomMargin;
    double m_subtitleFps;
    double m_movieFps;
    int m_movieWidth;
    int m_movieHeight;
};

WX_DEFINE_ARRAY(TextSub*, TextSubArray);

class Stream {
public:
	Stream(StreamType type, wxString codecName);
	
	StreamType GetType() { return m_type; }
	void SetType(StreamType type) { m_type = type; }
	
	wxString GetSourceCodecName() { return m_sourceCodecName; }
	void SetSourceCodecName(wxString sourceCodecName) { m_sourceCodecName = sourceCodecName; }
	
	wxSize GetSourceVideoSize() { return m_sourceVideoSize; }
	void SetSourceVideoSize(wxSize sourceVideoSize) { m_sourceVideoSize = sourceVideoSize; }
	
	VideoFormat GetSourceVideoFormat();
	AudioFormat GetSourceAudioFormat();
	
	int GetSourceBitrate() { return m_sourceBitrate; }
	void SetSourceBitrate(int sourceBitrate) { m_sourceBitrate = sourceBitrate; }
	
	int GetSourceChannelNumber() { return m_sourceChannelNumber; }
	void SetSourceChannelNumber(int sourceChannelNumber) { m_sourceChannelNumber = sourceChannelNumber; }
	
	int GetSourceSampleRate() { return m_sourceSampleRate; }
	void SetSourceSampleRate(int sourceSampleRate) { m_sourceSampleRate = sourceSampleRate; }

	/** @return the stream format string */
	wxString GetSourceFormat();
	
	int GetDestinationFormat() { return m_destinationFormat; }
	void SetDestinationFormat(int destinationFormat) { m_destinationFormat = destinationFormat; }
	
	VideoFormat GetVideoFormat() { return (VideoFormat) m_destinationFormat; }
	void SetVideoFormat(VideoFormat videoFormat) { m_destinationFormat = videoFormat; }
	
	AudioFormat GetAudioFormat() { return (AudioFormat) m_destinationFormat; }
	void SetAudioFormat(AudioFormat audioFormat) { m_destinationFormat = audioFormat; }
	
	SubtitleFormat GetSubtitleFormat() { return (SubtitleFormat) m_destinationFormat; }
	void SetSubtitleFormat(SubtitleFormat subtitleFormat) { m_destinationFormat = subtitleFormat; }
	
private:
	StreamType m_type; // audio/video/subtitle
	wxString m_sourceCodecName; // source codec name
	int m_sourceBitrate; // source bitrate
	int m_sourceChannelNumber; // number of audio channels
	int m_sourceSampleRate; // sample rate of audio
	wxSize m_sourceVideoSize; // frame size of source video
	int m_destinationFormat; // destination audio, video or subtitle format
};

WX_DEFINE_ARRAY(Stream*, StreamArray);

class Vob {
public:
	Vob(): m_pause(0), m_menu(NULL), m_slideshow(NULL), m_doNotTranscode(false) {}
	Vob(wxString filename): m_pause(0), m_menu(NULL), m_slideshow(NULL), m_doNotTranscode(false) { SetFilename(filename); }
	Vob(Menu* menu): m_pause(0), m_menu(menu), m_slideshow(NULL), m_doNotTranscode(false) {}
	Vob(Slideshow* slideshow): m_pause(0), m_menu(NULL), m_slideshow(slideshow), m_doNotTranscode(false) {}
	Vob(const Vob& vob);
	~Vob();
	
	/** Sets name of mpeg file for this vob*/
	bool SetFilename(wxString value);
	/** Returns name of mpeg file */
	wxString GetFilename() { return m_filename; }
	
	/** Returns true if there are audio streams in input file(s) */
	bool HasAudio();
	/** Returns count of audio streams */
	unsigned int GetAudioStreamCount();
	
    /** returns array with audio tracks */
	const wxArrayString& GetAudioFilenames() { return m_audioFilenames; }
	/** adds the given audio file to the vob */
	bool AddAudioFile(wxString filename);
	/** removes audio file with given index from the vob */
	void RemoveAudioFile(int index);
	
    /** returns array with subtitle tracks */
    TextSubArray& GetSubtitles() { return m_subtitles; }
	/** Returns count of subtitle streams */
	unsigned int GetSubtitleStreamsCount();
	
    /** sets file name of tempory vob file */
	void SetTmpFilename(wxString value) { m_tmpFilename = value; }
    /** returns file name of tempory vob file */
	wxString GetTmpFilename() { return m_tmpFilename; }
	
    /** sets chapter list */
	void SetChapters(wxString value) { m_chapters = value; }
    /** returns chapter list */
	wxString GetChapters() { return m_chapters; }
	/** returns the time of given chapter */
	long GetChapterTime(int chapter);
	
    /** sets pause */
	void SetPause(int value) { m_pause = value; }
    /** returns pause */
	int GetPause() { return m_pause; }

	void SetMenu(Menu* menu) { m_menu = menu; }
	Menu* GetMenu() { return m_menu; }
	
    void SetSlideshow(Slideshow* slideshow) { m_slideshow = slideshow; }
	Slideshow* GetSlideshow() { return m_slideshow; }
	
	/** returns duration of video in ms */
	inline double GetDuration() { return m_duration; }
	/** returns URI of video frame */
	wxString GetVideoFrameUri(int chapter, long position = -1);
    /** returns array with stream parameters */
    StreamArray& GetStreams() { return m_streams; }
	
    /** returns true if the source video file must not be remultiplexed/transcoded */
   	inline bool GetDoNotTranscode() { return m_doNotTranscode; }
  	/** sets if the source video file must not be remultiplexed/transcoded */
   	inline void SetDoNotTranscode(bool doNotTranscode) { m_doNotTranscode = doNotTranscode; }
	
	wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd, int tsi);
	
	/**
	 * Returns size in KB
     * @param generated true if vob file already generated (before running of dvdauthor)
     * @return Size of VOB file in KB
     */
	int GetSize(DVD* dvd, bool generated = false);
	
	/**
	 * Returns size in KB required for generation
	 * @return Size in KB
	 */
	int GetRequiredSize(DVD* dvd, Cache* cache);
	
	static Vob* CreateEmptyVob(VideoFormat videoFormat, AudioFormat audioFormat);
	
private:
	wxString m_filename;
	wxArrayString m_audioFilenames;
    TextSubArray m_subtitles;
	wxString m_tmpFilename;
	wxString m_chapters;
	int m_pause;
	Menu* m_menu;
    Slideshow* m_slideshow;
	static unsigned int GetFileSize(wxString filename);
	
	double m_duration;
	StreamArray m_streams;
	bool m_doNotTranscode;
};

WX_DEFINE_ARRAY(Vob*, VobArray);

class Pgc {
public:
	Pgc() { }
	~Pgc() { WX_CLEAR_ARRAY(m_vobs) }
	
	wxString GetEntriesAsString();
	const StringSet& GetEntries() { return m_entries; }
	void SetEntries(const StringSet& entries) { m_entries = entries; }
	void RemoveEntries(const StringSet& entries);
	
	void SetPreCommands(wxString value) { m_pre = value; }
	wxString GetPreCommands() { return m_pre; }
	void SetPostCommands(wxString value) { m_post = value; }
	wxString GetPostCommands() { return m_post; }
	void SetPalette(wxString value) { m_palette = value; }
	wxString GetPalette() { return m_palette; }
	
	VobArray& GetVobs() { return m_vobs; }
	
	Menu* GetMenu();
    Slideshow* GetSlideshow();

    unsigned int GetChapterCount();
	
	wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd, wxString nextTitle);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd, int tsi);
	
private:
	VobArray m_vobs;
	StringSet m_entries;
	wxString m_pre, m_post, m_palette;
};

WX_DEFINE_ARRAY(Pgc*, PgcArrayBase);

class Video {
public:
	void SetFormat(wxString value) { m_format = value; }
	wxString GetFormat() { return m_format; }
	wxArrayString GetFormatStrings(wxString def);
	
	void SetAspect(wxString value) { m_aspect = value; }
	wxString GetAspect() { return m_aspect; }
	wxArrayString GetAspectStrings(wxString def);
	
	void SetResolution(wxString value) { m_resolution = value; }
	wxString GetResolution() { return m_resolution; }
	
	void SetCaption(wxString value) { m_caption = value; }
	wxString GetCaption() { return m_caption; }
	
	void SetWidescreen(wxString value) { m_widescreen = value; }
	wxString GetWidescreen() { return m_widescreen; }
	wxArrayString GetWidescreenStrings(wxString def);
	
	wxSvgXmlNode* GetXML(DVDFileType type);
	bool PutXML(wxSvgXmlNode* node);
	
private:
	wxString m_format;
	wxString m_aspect;
	wxString m_resolution;
	wxString m_caption;
	wxString m_widescreen;
};

class PgcArray: public PgcArrayBase {
public:
	wxArrayString& GetAudioLangCodes() { return m_audioLangCodes; }
	wxArrayString& GetSubtitleLangCodes() { return m_subtitleLangCodes; }
	Video& GetVideo() { return m_video; }
	
	/** Returns count of audio streams */
	unsigned int GetAudioStreamCount();
	/** Returns count of subtitle streams */
	unsigned int GetSubtitleStreamsCount();
	
protected:
	wxArrayString m_audioLangCodes;
	wxArrayString m_subtitleLangCodes;
	Video m_video;
	void UpdateAudioLangCodes();
	void UpdateSubtitleLangCodes();
};

class Menus: public PgcArray {
public:
	StringSet GetEntries();
	
	wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd, int tsi);
};

class Titles: public PgcArray {
public:
	wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd, int nextTitleset);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd, int tsi);
};

class Titleset {
public:
	Titleset() { WX_CLEAR_ARRAY(m_menus); WX_CLEAR_ARRAY(m_titles); }
	
	Menus& GetMenus() { return m_menus; }
	Titles& GetTitles() { return m_titles; }
	
	inline bool IsEmpty() { return GetMenus().Count() == 0 && GetTitles().Count() == 0; }
	
	wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd, int nextTitleset = -1);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd, int tsi);
	
private:
	Menus m_menus;
	Titles m_titles;
};

class Vmgm: public Menus {
public:
	/** Returns the fpc (First Program Chain) commands */
    wxString GetFpc() { return m_fpc; }
    /** Sets the FPC (First Program Chain) commands */
    void SetFpc(wxString fpc) { m_fpc = fpc; }
	
    wxSvgXmlNode* GetXML(DVDFileType type, DVD* dvd);
	bool PutXML(wxSvgXmlNode* node, DVD* dvd);
	
private:
	wxString m_fpc;
};

WX_DEFINE_ARRAY(Titleset*, TitlesetArray);

enum DiscCapacity {
	dcDVD47 = 0,
	dcDVD85,
	dcUNLIMITED
};

class DVD {
public:
    DVD();
    ~DVD();
	
    /** Returns disc label */
    wxString GetLabel() { return m_label; }
    /** Sets disc label */
    void SetLabel(wxString label) { m_label = label; }
	
    /** Returns disc capacity */
    DiscCapacity GetCapacity() { return m_capacity; }
    /** Sets disc capacity */
    void SetCapacity(DiscCapacity capacity) { m_capacity = capacity; }
    /** Returns disc capacity in KB */
    long GetCapacityValue();
	
    /** Returns true if video bitrate will be calculated automatically */
	bool IsVideoBitrateAuto() { return m_videoBitrateAuto; }
	/** Sets if video bitrate will be calculated automatically */
	void SetVideoBitrateAuto(bool bitrateAuto) { m_videoBitrateAuto = bitrateAuto; }
	/** Returns video bitrate */
    int GetVideoBitrate() { return m_videoBitrate; }
    /** Sets video bitrate */
    void SetVideoBitrate(int bitrate) { m_videoBitrate = bitrate; }
    /** Calculates video bitrate if it set to auto */
    wxArrayInt CalculateVideoBitrate();
	
	/** Returns true if creation of jumppads enabled */
	bool IsJumppadsEnabled() { return m_jumppad; }
	/** Enables creation of jumppads */
	void EnableJumppads(bool enable = true) { m_jumppad = enable; }
	
	/** Returns true if creation of empty menu enabled */
	bool IsEmptyMenuEnabled() { return m_emptyMenu; }
	/** Enables creation of empty menu */
	void EnableEmptyMenu(bool enable = true) { m_emptyMenu = enable; }
	
	/** Returns titlesets */
	TitlesetArray& GetTitlesets() { return m_titlesets; }
	/** Returns vmgm menus */
	Vmgm& GetVmgm() { return m_vmgm; }
	
	/** Returns count of audio streams */
	unsigned int GetAudioStreamCount();
	/** Returns count of subtitle streams */
	unsigned int GetSubtitleStreamsCount();
	
	/**
	 * Adds a new Titleset
	 * @return Returns index of new titleset (tsi)
	 **/
	int AddTitleset();
	/**
	 * Adds a new Menu
	 * @return Returns index of new PGC
	 **/
	int AddMenu(int tsi = -1);
	/**
	 * Adds a new Menu
	 * @return Returns index of new PGC
	 **/
	int AddMenu(Menu* menu, int tsi = -1);
	/**
	 * Adds a new PGC
	 * @return Returns index of new PGC
	 **/
	int AddPgc(int tsi, bool menu, Pgc* pgc = NULL);
	/** returns pgcs */
	PgcArray& GetPgcArray(int tsi, bool menus);
	/** returns vob from given titleset, pgc */
	Vob* GetVob(int tsi, bool isMenu, int pgci, int vobi);
	/** returns vob with menu from given titleset and pgc */
	Vob* GetMenuVob(int tsi, int pgci);
	/** returns menu from titleset tsi und pgc pgci */
	Menu* GetMenu(int tsi, int pgci);
	/** Returns true if all actions are valid */
	bool CheckActions(bool skipInvalidTarget);
	/** Updates image in buttons with given jump action */
	void UpdateButtonImageFor(int actionTsi, int actionPgci);
	/** Fix coordinates of buttons if they are out of range */
	void FixButtonCoordinates();
	
	/** returns id for specified vob */
	static inline int MakeId(int tsi, int pgci, int vobi, bool menu)
	{ return ((tsi+1)<<24) + ((pgci*2+(menu ? 0 : 1))<<8) + vobi; }
	static inline int GetTsi(int id)  { return (id>>24)-1; }
	static inline int GetPgci(int id) { return ((id>>8) & 0xFFFF) / 2; }
	static inline bool IsMenu(int id) { return !(((id>>8) & 0xFFFF) % 2); }
	static inline int GetVobi(int id) { return id & 0xFF; }
	
	/** Returns estimated size of DVD in KB */
	long GetSize(bool generated = false);
	/** Returns size required for generation of DVD in KB */
	long GetRequiredSize(Cache* cache);
	
	/**
	 *  Returns number of g-register that will be used to implement "play all"
	 *  or -1 if "play all" is not used
	 */
	inline int GetPlayAllRegister() { return m_playAllRegister; }
	
	/** Returns video format of DVD */
	inline VideoFormat GetVideoFormat() { return m_videoFormat; }
	/** Sets video format of DVD */
	void SetVideoFormat(VideoFormat format) { m_videoFormat = format; }
	/** Returns audio format of DVD */
	inline AudioFormat GetAudioFormat() { return m_audioFormat; }
	/** Sets audio format of DVD */
	void SetAudioFormat(AudioFormat format) { m_audioFormat = format; }
	/** Returns aspect ratio of DVD */
	inline AspectRatio GetAspectRatio() { return m_aspectRatio; }
	/** Sets aspect ratio of DVD */
	void SetAspectRatio(AspectRatio format) { m_aspectRatio = format; }
	
    /** Loads a project file */
	bool Open(wxString fname);
    /** Saves a project file */
	bool Save(wxString fname = wxEmptyString);
    /** Stores object data to string */
	wxString Serialize();
    /** Restores object from data */
	void Deserialize(const wxString& data);
    /** Returns project file name with path */
    wxString GetFilename() { return m_filename; }
    /** Returns path of loaded project file */
    wxString GetPath(bool withSep = true);
    /** Returns project file name with path */
    wxString GetTemplateFile() { return m_templateFile; }
    /** Returns if project uses template */
    bool HasTemplate() { return m_templateFile.length() > 0; }
	
    /** Saves a configuration for dvdauthor */
	bool SaveDVDAuthor(wxString fname);
	
	static wxArrayString GetVideoFormatLabels(bool copy = false, bool none = false, bool menu = false);
	static wxArrayString GetAudioFormatLabels(bool copy = false, bool none = false);
	static wxArrayString GetAspectRatioLabels(bool autom = false);
	static wxArrayString GetCapacityLabels();
	static wxArrayString GetVideoBitrateLabels();
	static wxArrayString GetVideoFormatNames();
	static wxArrayString GetAudioFormatNames();
	static VideoFormat GetVideoFormatByName(wxString name);
	static AudioFormat GetAudioFormatByName(wxString name);
	static wxString GetVideoFormatName(VideoFormat format);
	static wxString GetAudioFormatName(AudioFormat format);
	/** Returns list of available language codes for audio tracks (ISO 639) */
	static wxArrayString GetAudioLanguageCodes();
	/** Returns map of languages to language codes (ISO 639) */
	static map<wxString, wxString>& GetLanguageMap();
	/** Returns list of languages (ISO 639) */
	static wxArrayString& GetLanguageNames();
	/** Returns list of encodings supported by libiconv */
	static wxArrayString& GetCharsets();
	
private:
	/** project file name */
	wxString m_filename;
	/** template file name */
	wxString m_templateFile;
	/** disc label */
    wxString m_label;
    /** dics capacity */
    DiscCapacity m_capacity;
    bool m_videoBitrateAuto;
    int m_videoBitrate;
	bool m_jumppad;
	bool m_emptyMenu;
	TitlesetArray m_titlesets;
	Vmgm m_vmgm;
	int m_playAllRegister;
	VideoFormat m_videoFormat;
	AudioFormat m_audioFormat;
	AspectRatio m_aspectRatio;
	/** Returns true if some of buttons have flag "Play All" checked */
	bool HasPlayAllButton();
	/** Returns number of g-regsiter that is not used */
	int FindFreeRegister();
	/** Returns true if given g-register is used in pgc pre/post or in menu button action */
	bool IsRegisterUsed(int g);
    /** Returns XML Document */
	wxSvgXmlDocument* GetXml();
	/** Initializes object with XML data */
	bool PutXML(const wxSvgXmlDocument& xml);
};

#endif //DVDAUTHOR_H
