/////////////////////////////////////////////////////////////////////////////
// Name:        TitlesetManager.h
// Purpose:     Titleset Manager
// Author:      Alex Thuering
// Created:	27.01.2003
// RCS-ID:      $Id: TitlesetManager.cpp,v 1.41 2010/05/09 18:19:22 ntalex Exp $
// Copyright:   (c) Alex Thuering
// Licence:     GPL
/////////////////////////////////////////////////////////////////////////////

#include "TitlesetManager.h"
#include "MainWin.h"
#include "TitlePropDlg.h"
#include "MenuPropDlg.h"
#include "MPEG.h"
#include "Config.h"
#include "Utils.h"
#include "MessageDlg.h"
#include <wxVillaLib/ThumbnailFactory.h>
#include <wxVillaLib/ImageProc.h>
#include <wxVillaLib/utils.h>
#include <wx/splitter.h>
#include <wx/dnd.h>
#include <wx/clipbrd.h>
#include <wx/utils.h>
#include <wx/artprov.h>
#include "rc/noaudio.png.h"

//////////////////////////////////////////////////////////////////////////////
///////////////////////////// TitleDnDFile ///////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

class TitleDnDFile: public wxFileDropTarget {
public:
	TitleDnDFile(TitlesetManager* pOwner): m_pOwner(pOwner) {
		SetDataObject(new wxFileDataObject);
		SetDefaultAction(wxDragMove);
	}

	virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def) {
		wxFileDropTarget::OnEnter(x, y, def);
		return def;
	}

	virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
		defDrag = def; // wxWidgets bug 1100327 (wxGTK <= 2.6.0)
		wxFileDropTarget::OnDragOver(x, y, def);
		return def;
	}

	virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) {
		defDrag = def;
		wxFileDropTarget::OnData(x, y, def);
		return def;
	}

	virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) {
		bool res = false;
		for (int i = 0; i<(int)filenames.Count(); i++)
			res = AddFile(filenames[i], i == 0) || res;
		return res;
	}
	
	bool AddFile(wxString filename, bool first) {
		if (wxImage::FindHandler(filename.AfterLast('.').Lower(), -1)) {
			m_pOwner->AddImage(filename, defDrag == wxDragMove && first);
			return true;
		} else if (wxThumbnails::IsVideo(filename)) {
			wxFfmpegMediaDecoder decoder;
			if (!decoder.Load(filename))
				return false;
			m_pOwner->AddVideo(filename, defDrag == wxDragMove);
			return true;
		} else if (wxThumbnails::IsAudio(filename)) {
			m_pOwner->AddAudio(filename);
			return true;
		} else if (wxThumbnails::IsSubtitle(filename)) {
			m_pOwner->AddSubtitles(filename);
			return true;
		}
		
		// try to load it as video
		wxFfmpegMediaDecoder decoder;
		if (!decoder.Load(filename))
			return false;
		m_pOwner->AddVideo(filename, defDrag == wxDragMove);
		return true;
	}
protected:
	TitlesetManager *m_pOwner;
	wxDragResult defDrag;
};

//////////////////////////////////////////////////////////////////////////////
/////////////////////////// TitlesetManager //////////////////////////////////
//////////////////////////////////////////////////////////////////////////////

enum {
	MENU_TITLE_DEL_ID = 2650,
	MENU_TITLE_COPY_ID,
	MENU_TITLE_PASTE_ID,
	MENU_TITLE_PROP_ID,
	MENU_TITLE_ADD_ID,
	MENU_TITLE_MOVE_ID,
	MENU_MAIN_ADD_ID,
	MENU_MOVE_TITLE_LEFT_ID,
	MENU_MOVE_TITLE_RIGHT_ID
};

BEGIN_EVENT_TABLE(TitlesetManager, wxThumbnails)
	EVT_UPDATE_UI(MainWin::MENU_DVD_ADD_TITLESET_ID, TitlesetManager::OnAddTitlesetUpdateUI)
	EVT_MENU(MENU_MOVE_TITLE_LEFT_ID, TitlesetManager::OnMoveTitleLeft)
	EVT_MENU(MENU_MOVE_TITLE_RIGHT_ID, TitlesetManager::OnMoveTitleRight)
	EVT_UPDATE_UI(MENU_MOVE_TITLE_LEFT_ID, TitlesetManager::OnMoveTitleLeftUpdateUI)
	EVT_UPDATE_UI(MENU_MOVE_TITLE_RIGHT_ID, TitlesetManager::OnMoveTitleRightUpdateUI)
	EVT_MENU(MENU_TITLE_DEL_ID, TitlesetManager::OnDelete)
	EVT_MENU(MENU_TITLE_COPY_ID, TitlesetManager::OnCopy)
	EVT_MENU(MENU_TITLE_PASTE_ID, TitlesetManager::OnPaste)
	EVT_UPDATE_UI(MENU_TITLE_PASTE_ID, TitlesetManager::OnUpdateUIObjectPaste)
	EVT_MENU(MENU_TITLE_PROP_ID, TitlesetManager::OnProps)
END_EVENT_TABLE()

DEFINE_EVENT_TYPE(EVT_COMMAND_MENU_SELECTED)
DEFINE_EVENT_TYPE(EVT_COMMAND_DVD_CHANGED)

TitlesetManager::TitlesetManager(wxWindow* parent, int id): wxThumbnails(parent, id) {
	SetThumbSize(96, 76);
	SetOrientaion(wxTHUMB_HORIZONTAL);
	SetDropTarget(new TitleDnDFile(this));
	EnableDragging(false); // disable standard dragging of wxThumbnails
	m_dvd = NULL;
	m_selectedMenu = -1;

	wxMenu* addMenu = new wxMenu;
	addMenu->Append(MainWin::MENU_DVD_ADD_FILE_ID, _("&File..."));
	addMenu->Append(MainWin::MENU_DVD_ADD_FILE_AS_CHAPTER_ID, _("File as &chapter..."));
	addMenu->Append(MainWin::MENU_DVD_ADD_MENU_ID, _("&Menu"));
	addMenu->Append(MainWin::MENU_DVD_ADD_VMMENU_ID, _("&vmMenu"));
	addMenu->Append(MainWin::MENU_DVD_ADD_TITLESET_ID, _("&Titleset"));

	wxMenu* moveMenu = new wxMenu;
	moveMenu->Append(MENU_MOVE_TITLE_LEFT_ID, _("&Left"));
	moveMenu->Append(MENU_MOVE_TITLE_RIGHT_ID, _("&Right"));

	m_mainMenu = new wxMenu;
	m_mainMenu->Append(MENU_MAIN_ADD_ID, _("&Add"), addMenu);
	wxMenuItem* item = new wxMenuItem(m_mainMenu, MENU_TITLE_PASTE_ID, _("&Paste"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_PASTE, wxART_MENU));
	m_mainMenu->Append(item);

	m_titleMenu = new wxMenu;
	m_titleMenu->Append(MENU_TITLE_ADD_ID, _("&Add"), addMenu);
	m_titleMenu->Append(MENU_TITLE_MOVE_ID, _("&Move"), moveMenu);
	item = new wxMenuItem(m_titleMenu, MENU_TITLE_COPY_ID, _("&Copy"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_COPY, wxART_MENU));
	m_titleMenu->Append(item);
	item = new wxMenuItem(m_mainMenu, MENU_TITLE_PASTE_ID, _("&Paste"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_PASTE, wxART_MENU));
	m_titleMenu->Append(item);
	item = new wxMenuItem(m_titleMenu, MENU_TITLE_DEL_ID, _("&Delete"));
	item->SetBitmap(wxArtProvider::GetBitmap(wxART_DELETE, wxART_MENU));
	m_titleMenu->Append(item);
	m_titleMenu->Append(MENU_TITLE_PROP_ID, _("&Properties..."));

	SetPopupMenu(m_titleMenu);
	SetGlobalPopupMenu(m_mainMenu);
}

TitlesetManager::~TitlesetManager() {
	// nothing to do
}

void TitlesetManager::SetDVD(DVD* dvd) {
	m_dvd = dvd;
	SetSelected(-1);
	m_selectedMenu = 0;
	UpdateItems();
}

wxBitmap TitlesetManager::GetThumbImage(wxThumb& thumb) {
	if (!m_dvd)
		return wxBitmap();
	int id = thumb.GetId();
	Vob* vob = m_dvd->GetVob(DVD::GetTsi(id), DVD::IsMenu(id), DVD::GetPgci(id), DVD::GetVobi(id));
	wxBitmap img = thumb.GetBitmap();
	if (!img.Ok()) {
		int titleHeight = m_tTextHeight + 2;
		if (vob->GetMenu()) {
			img = wxBitmap(vob->GetMenu()->GetImage(m_tWidth, m_tHeight-titleHeight));
			thumb.SetBitmap(img);
		} else if (vob->GetSlideshow()) {
			img = wxBitmap(vob->GetSlideshow()->GetThumbImage(m_tWidth, m_tHeight-titleHeight));
			thumb.SetBitmap(img);
		} else
			img = thumb.GetBitmap(this, m_tWidth, m_tHeight-titleHeight);
	}
	return img;
}

void TitlesetManager::DrawThumbnail(wxBitmap& bmp, wxThumb& thumb, int index) {
	if (!m_dvd)
		return;
	int id = thumb.GetId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	int vobi = DVD::GetVobi(id);
	bool isMenu = DVD::IsMenu(id);
	Vob* vob = m_dvd->GetVob(tsi, isMenu, pgci, vobi);

	wxMemoryDC dc;
	dc.SelectObject(bmp);
	dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
	int x = m_tBorder/2;
	int y = m_tBorder/2;
	int tw = bmp.GetWidth() - m_tBorder + 2;
	int th = bmp.GetHeight() - m_tBorder + 2;
	int titleHeight = m_tTextHeight + 2;
	// background
	dc.SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
	dc.SetBrush(wxBrush(GetBackgroundColour(), wxSOLID));
	dc.DrawRectangle(0, 0, bmp.GetWidth(), bmp.GetHeight());
	// rounded rectangle
	wxColour rectColour = isMenu ? wxColour(64, 64, 64) : wxColour(0, 0, 0);
	dc.SetBrush(wxBrush(rectColour, wxSOLID));
	dc.DrawRoundedRectangle(x, y, tw, th, 12);
	// draw begin & end of titleset
	if (IsBeginOfTitleset(tsi, pgci, vobi, isMenu)) {
		int h = bmp.GetHeight();
		dc.SetPen(wxPen(*wxRED, 0, wxSOLID));
		dc.DrawLine(1, 1, 10, 1);
		dc.DrawLine(1, 1, 1, 10);
		dc.DrawLine(1, h-2, 10, h-2);
		dc.DrawLine(1, h-2, 1, h-11);
	}
	if (IsEndOfTitleset(tsi, pgci, vobi, isMenu)) {
		int h = bmp.GetHeight();
		int w = bmp.GetWidth();
		dc.SetPen(wxPen(*wxRED, 0, wxSOLID));
		dc.DrawLine(w-1, 1, w-10, 1);
		dc.DrawLine(w-1, 1, w-1, 10);
		dc.DrawLine(w-1, h-2, w-10, h-2);
		dc.DrawLine(w-1, h-2, w-1, h-11);
	}
	// image
	wxBitmap img = GetThumbImage(thumb);
	if (index == m_pointed) {
		wxImage imgTmp = img.ConvertToImage();
		wxAdjustBrightness(imgTmp, wxRect(0, 0, img.GetWidth(), img.GetHeight()),
				POINTED_BRIGHTNESS);
		img = wxBitmap(imgTmp);
	}
	wxRect imgRect(x + (m_tWidth-img.GetWidth())/2, y + titleHeight + (m_tHeight-titleHeight-img.GetHeight())/2,
			img.GetWidth(), img.GetHeight());
	dc.DrawBitmap(img, imgRect.x, imgRect.y);
	// check audio
	if (!vob->GetMenu() && !vob->GetSlideshow() && !vob->HasAudio()) {
		wxBitmap noaudioBitmap = wxBITMAP_FROM_MEMORY(noaudio);
		dc.DrawBitmap(noaudioBitmap,
				imgRect.x + img.GetWidth() - noaudioBitmap.GetWidth(),
				imgRect.y + img.GetHeight() - noaudioBitmap.GetHeight());
	} else if (vob->GetAudioFilenames().GetCount() > 0) {
		wxString text = vob->GetAudioFilenames()[0].AfterLast(wxT('.')).Lower();
		for (int fi = 1; fi < (int)vob->GetAudioFilenames().GetCount(); fi++)
			text += wxT(",") + vob->GetAudioFilenames()[fi].AfterLast(wxT('.')).Lower();
		dc.SetTextForeground(*wxWHITE);
		wxFont font(8, wxFONTFAMILY_ROMAN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
		dc.SetFont(font);
		wxCoord w, h;
		dc.GetTextExtent(text, &w, &h);
		dc.DrawText(text, imgRect.x + img.GetWidth() - w - 4, imgRect.y + img.GetHeight() - h);
	}
	// outline
	if (index == m_selectedMenu) {
		dc.SetPen(wxPen(*wxBLUE, 0, wxSOLID));
		dc.SetBrush(wxBrush(*wxBLACK, wxTRANSPARENT));
		dc.DrawRectangle(imgRect.x-1, imgRect.y-1, imgRect.width+1, imgRect.height+1);
	}
	// draw title caption
	dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
	dc.SetTextForeground(*wxWHITE);
	wxString caption;
	if (isMenu && tsi<0)
		caption = _("vmMenu");
	else if (isMenu)
		caption = _("Menu");
	else
		caption = _("Title");
	caption += wxString::Format(_T(" %d"), pgci+1);
	if (vobi>0)
		caption += wxString::Format(_T("-%d"), vobi+1);
	int sw, sh;
	dc.GetTextExtent(caption, &sw, &sh);
	int tx = x + (m_tWidth-sw)/2;
	int ty = y + (titleHeight-sh)/2;
	dc.DrawText(caption, tx, ty);
	// draw caption
	for (int i=0; i<thumb.GetCaptionLinesCount(m_tWidth-m_tCaptionBorder); i++) {
		wxString caption = thumb.GetCaption(i);
		dc.GetTextExtent(caption, &sw, &sh);
		tx = x + (m_tWidth-sw)/2;
		ty = y + m_tHeight + i*m_tTextHeight + (m_tTextHeight-sh)/2;
		dc.DrawText(caption, tx, ty);
	}

	dc.SelectObject(wxNullBitmap);
}

int TitlesetManager::GetRowHeight(int begRow, int count) {
	int capHeight = 0;
	for (int i=begRow; i<begRow+count; i++)
		if (i < (int) m_tCaptionHeight.GetCount())
			capHeight += m_tCaptionHeight[i]>0 ? m_tCaptionHeight[i] : 1;
	return (m_tHeight + m_tBorder)*count + capHeight*m_tTextHeight;
}

void TitlesetManager::UpdateItems() {
	wxThumbnailFactory::ClearQueue(this);

	int selectedMenuId = -1; // to update m_selectedMenu
	if (m_selectedMenu>=0 && m_selectedMenu<(int)m_items.Count())
		selectedMenuId = m_items[m_selectedMenu]->GetId();
	WX_CLEAR_ARRAY(m_items)
	Menus* menus = &m_dvd->GetVmgm();
	for (int pgci = 0; pgci<(int)menus->Count(); pgci++) {
		Pgc* pgc = (*menus)[pgci];
		if (!pgc->GetVobs().Count())
			continue;
		wxThumb* thumb = new wxThumb(_T(""));
		thumb->SetId(DVD::MakeId(-1, pgci, 0, true));
		m_items.Add(thumb);
	}
	for (int tsi = 0; tsi<(int)m_dvd->GetTitlesets().Count(); tsi++) {
		Titleset* ts = m_dvd->GetTitlesets()[tsi];
		menus = &ts->GetMenus();
		for (int pgci = 0; pgci<(int)menus->Count(); pgci++) {
			Pgc* pgc = (*menus)[pgci];
			if (!pgc->GetVobs().Count())
				continue;
			wxThumb* thumb = new wxThumb(_T(""));
			thumb->SetId(DVD::MakeId(tsi, pgci, 0, true));
			m_items.Add(thumb);
		}
		for (int pgci = 0; pgci<(int)ts->GetTitles().Count(); pgci++) {
			Pgc* pgc = ts->GetTitles()[pgci];
			for (int vobi = 0; vobi<(int)pgc->GetVobs().Count(); vobi++) {
				Vob* vob = pgc->GetVobs()[vobi];
				wxString fname;
				wxString caption;
				if (vob->GetSlideshow()) {
					if (vob->GetSlideshow()->Count() > 1)
						caption = wxString::Format(_("Slideshow (%d images)"),
						vob->GetSlideshow()->Count());
					else
						caption = _("Slideshow (1 image)");
				} else {
					fname = vob->GetFilename();
					caption = fname.AfterLast(wxFILE_SEP_PATH).BeforeLast('.');
				}
				wxThumb* thumb = new wxThumb(fname, caption);
				thumb->SetId(DVD::MakeId(tsi, pgci, vobi, false));
				m_items.Add(thumb);
			}
		}
	}
	if (selectedMenuId>=0) // update m_selectedMenu
		m_selectedMenu = GetIndex(selectedMenuId);
	UpdateProp();
	Refresh();
	wxSplitterWindow* p = (wxSplitterWindow*) GetParent(); 
	p->SetSashPosition(p->GetSashPosition() + (m_rows > 1 ? 0 : 1000));
}

int TitlesetManager::GetIndex(int tsi, int pgci, int vobi, bool menu) {
	return GetIndex(DVD::MakeId(tsi, pgci, vobi, menu));
}

int TitlesetManager::GetIndex(int id) {
	for (int i=0; i<(int)m_items.Count(); i++) {
		//wxMessageBox(wxString::Format(_T("find %d ? %d"), id, m_items[i]->GetId()));
		if (m_items[i]->GetId() == id)
			return i;
	}
	return -1;
}

bool TitlesetManager::AddVideo(const wxString& fname, bool createTitle, int tsi) {
	int videoBitrateOld = m_dvd->GetVideoBitrate();
	
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);
	if (tsi < 0)
		tsi = m_dvd->GetTitlesets().GetCount() - 1;
	PgcArray& pgcs = m_dvd->GetTitlesets()[tsi]->GetTitles();
	Pgc* pgc = pgcs.Count()>0 ? pgcs.Last() : NULL;
	Vob* vob = pgc && pgc->GetVobs().Count()>0 ? pgc->GetVobs().Last() : NULL;
	if (vob && vob->GetFilename().length() && !vob->HasAudio()) {
		wxLogError(wxString(_("The last title isn't complete.\n"))
				+ _("Please add an audio track to it."));
		return false;
	}

	vob = new Vob(_T(""));
	vob->SetFilename(fname);
	// check if reencoding is needed
	for (unsigned int i=0; i<vob->GetStreams().GetCount(); i++) {
		Stream* stream = vob->GetStreams()[i];
		if (stream->GetType() == stVIDEO) {
			// set destination format (default COPY) if codec is not mpeg2
			if (stream->GetSourceCodecName() != wxT("mpeg2video")
					|| stream->GetSourceVideoFormat() != m_dvd->GetVideoFormat())
				stream->SetDestinationFormat(m_dvd->GetVideoFormat());
			else if (vob->GetAudioFilenames().Count() == 0 && MPEG::HasNavPacks(vob->GetFilename()))
				vob->SetDoNotTranscode(true);  // do not remultiplex/transcode if the video is already a VOB
		} else if (stream->GetType() == stAUDIO) {
			// set destination format (default COPY) if codec is not mp2/ac3 or sample rate is not 48 kHz
			if (stream->GetSourceAudioFormat() != m_dvd->GetAudioFormat()
					|| stream->GetSourceSampleRate() != 48000) {
				stream->SetDestinationFormat(m_dvd->GetAudioFormat());
				vob->SetDoNotTranscode(false);
			}
		}
	}
	// set chapters
	int n = 10;
	if (vob->GetDuration() > 0) {
		int duration = (int) vob->GetDuration() / 60;
		n = duration / s_config.GetDefChapterLength();
		if (n > 0 && duration - s_config.GetDefChapterLength()*n
				< s_config.GetDefChapterLength()/5)
			n--;
	}
	if (n>100)
		n = 100;
	int t = s_config.GetDefChapterLength();
	wxString cStr;
	for (int i = 0; i < n; i++) {
		if (cStr.length())
			cStr += wxT(",");
		if (t >= 60)
			cStr += wxString::Format(wxT("%d:%02d:00"), t/60, t%60);
		else
			cStr += wxString::Format(wxT("%02d:00"), t%60);
		t += s_config.GetDefChapterLength();
	}
	if (!createTitle && pgc != NULL)
		cStr = wxT("0:00") + wxString(cStr.length() ? wxT(",") + cStr : wxT(""));
	vob->SetChapters(cStr);
	if (createTitle || pgc == NULL) {
		pgc = new Pgc;
		pgc->SetPostCommands(_T("call vmgm menu 1;"));
		pgc->GetVobs().Add(vob);
		pgcs.Add(pgc);
		m_dvd->UpdateButtonImageFor(tsi, pgcs.GetCount() - 1);
	} else
		pgc->GetVobs().Add(vob);
	
	UpdateItems();
	SendDvdChangedEvent();
	
	if (videoBitrateOld >= 2000 && m_dvd->GetVideoBitrate() < 2000) {
		wxMessageBox(_("The video bitrate is reduced below 2 MB/s to fit your video on DVD. \
This can produce DVD with bad video quality."), wxT("DVDStyler"), wxOK | wxICON_WARNING, this);
	}
	
	return true;
}

bool TitlesetManager::AddAudio(const wxString& fname) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	if (pgcs.GetCount() == 0 || pgcs.Last()->GetVobs().GetCount() == 0) {
		wxLogError(_("Please add a video track first."));
		return false;
	}
	Vob* vob = pgcs.Last()->GetVobs().Last();
	vob->AddAudioFile(fname);
	vob->SetDoNotTranscode(false);
	// check if reencoding is needed
	Stream* stream = vob->GetStreams()[vob->GetStreams().GetCount()-1];
	if (vob->GetStreams().GetCount() == 2 && stream->GetSourceAudioFormat() != m_dvd->GetAudioFormat())
		stream->SetDestinationFormat(m_dvd->GetAudioFormat());
	else if (stream->GetSourceAudioFormat() != afMP2 && stream->GetSourceAudioFormat() != afAC3)
		stream->SetDestinationFormat(m_dvd->GetAudioFormat());
	else if (stream->GetSourceSampleRate() != 48000)
		stream->SetDestinationFormat(stream->GetSourceAudioFormat());
	UpdateItems();
	SendDvdChangedEvent();
	return true;
}

bool TitlesetManager::AddSubtitles(const wxString& fname) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	if (pgcs.Count()==0 || pgcs.Last()->GetVobs().Count()==0) {
		wxLogError(_("Please add a video track first."));
		return false;
	}
	Vob* vob = pgcs.Last()->GetVobs().Last();
	vob->GetSubtitles().Add(new TextSub(fname));
	UpdateItems();
	SendDvdChangedEvent();
	return true;
}

void TitlesetManager::AddImage(const wxString& fname, bool createTitle) {
	if (m_dvd->GetTitlesets().Count() == 0)
		m_dvd->GetTitlesets().Add(new Titleset);

	PgcArray& pgcs = m_dvd->GetTitlesets().Last()->GetTitles();
	Pgc* pgc = pgcs.Count()>0 ? pgcs.Last() : NULL;

	if (createTitle || pgc == NULL) {
		pgc = new Pgc;
		pgc->SetPostCommands(_T("call vmgm menu 1;"));
		pgcs.Add(pgc);
	}

	Slideshow* slideshow = pgc->GetSlideshow();
	if (slideshow == NULL) {
		slideshow = new Slideshow(m_dvd->GetVideoFormat(), m_dvd->GetAspectRatio());
		Vob* vob = new Vob(slideshow);
		pgc->GetVobs().Add(vob);
	}
	slideshow->Add(new Slide(fname));

	UpdateItems();
	SendDvdChangedEvent();
}

void TitlesetManager::SelectMenu(int tsi, int pgci) {
	m_selectedMenu = GetIndex(tsi, pgci, 0, true);
	SendMenuSelectedEvent();
}

void TitlesetManager::SelectFirstMenu() {
	m_selectedMenu = -1;
	for (int tsi=-1; tsi<(int)m_dvd->GetTitlesets().Count(); tsi++) {
		if (m_dvd->GetMenu(tsi, 0)) {
			m_selectedMenu = GetIndex(tsi, 0, 0, true);
			break;
		}
	}
	SendMenuSelectedEvent();
}

bool TitlesetManager::IsBeginOfTitleset(int tsi, int pgci, int vobi, bool isMenu) {
	if (tsi<0 || (m_dvd->GetTitlesets().Count()<=1 && m_dvd->GetVmgm().Count()==0))
		return false;
	return pgci == 0 && vobi == 0
		&& (isMenu || m_dvd->GetTitlesets()[tsi]->GetMenus().Count() == 0);
}

bool TitlesetManager::IsEndOfTitleset(int tsi, int pgci, int vobi, bool isMenu) {
	if (tsi<0 || m_dvd->GetTitlesets().Count() < 2
			|| tsi == (int)m_dvd->GetTitlesets().Count()-1)
		return false;
	Titleset* ts = m_dvd->GetTitlesets()[tsi];
	return (isMenu && ts->GetTitles().Count() == 0
				&& pgci == (int)ts->GetMenus().Count()-1
				&& vobi == (int)ts->GetMenus()[pgci]->GetVobs().Count()-1)
		|| (!isMenu && pgci == (int)ts->GetTitles().Count()-1
				&& vobi == (int)ts->GetTitles()[pgci]->GetVobs().Count()-1);
}

void TitlesetManager::SendMenuSelectedEvent() {
	wxCommandEvent evt(EVT_COMMAND_MENU_SELECTED, this->GetId());
	GetEventHandler()->ProcessEvent(evt);
}

void TitlesetManager::SendDvdChangedEvent() {
	wxCommandEvent evt(EVT_COMMAND_DVD_CHANGED, this->GetId());
	GetEventHandler()->ProcessEvent(evt);
}

void TitlesetManager::OnMouseUp(wxMouseEvent& event) {
	wxThumbnails::OnMouseUp(event);
	// Popup menu
	if (event.RightUp())
		return;
	// select menu
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	if (DVD::IsMenu(id)) {
		SelectMenu(DVD::GetTsi(id), DVD::GetPgci(id));
		Refresh();
	}
}

void TitlesetManager::OnMouseDClick(wxMouseEvent& WXUNUSED(event)) {
	wxCommandEvent evt;
	OnProps(evt);
}

void TitlesetManager::OnMouseMove(wxMouseEvent &event) {
	wxThumbnails::OnMouseMove(event);
	// -- drag & drop --
	if (event.Dragging() && m_selected >= 0 && m_beginDrag) {
		DVDAction action(false, MENU_ACTION);
		action.SetTsi(DVD::GetTsi(GetSelectedId()));
		action.SetPgci(DVD::GetPgci(GetSelectedId()));
		action.SetMenu(DVD::IsMenu(GetSelectedId()));
		wxString actionStr = action.Serialize();
		wxDataFormat format(DATAFORMAT_ACTION);
		wxCustomDataObject dataObj(format);
		dataObj.SetData(actionStr.length(), actionStr.ToAscii());
		wxDropSource source(dataObj, this);
		source.DoDragDrop(wxDrag_DefaultMove);
		m_beginDrag = false;
	}
}

void TitlesetManager::AddMenu() {
	int tsi = m_dvd->GetTitlesets().Count()-1;
	if (GetSelected()>=0)
		tsi = DVD::GetTsi(GetSelectedId());
	int pgci = m_dvd->AddMenu(tsi);
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::AddVmMenu() {
	int tsi = -1;
	int pgci = m_dvd->AddMenu(tsi);
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::AddTitleset() {
	m_dvd->AddTitleset();
	AddMenu();
}

void TitlesetManager::OnAddTitlesetUpdateUI(wxUpdateUIEvent& event) {
	int cnt = m_dvd->GetTitlesets().Count();
	event.Enable(cnt>0 && m_dvd->GetTitlesets()[cnt-1]->GetTitles().Count());
}

void TitlesetManager::OnMoveTitleLeft(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	if (pgci == 0)
		return;
	Pgc* pgc = pgcs[pgci];
	pgcs[pgci] = pgcs[pgci-1];
	pgcs[pgci-1] = pgc;
	if (isMenu)
		SelectMenu(tsi, pgci-1);
	UpdateItems();
	SendDvdChangedEvent();
	m_dvd->UpdateButtonImageFor(tsi, pgci - 1);
	m_dvd->UpdateButtonImageFor(tsi, pgci);
}

void TitlesetManager::OnMoveTitleRight(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	if (pgci == (int)pgcs.Count()-1)
		return;
	Pgc* pgc = pgcs[pgci];
	pgcs[pgci] = pgcs[pgci+1];
	pgcs[pgci+1] = pgc;
	if (isMenu)
		SelectMenu(tsi, pgci+1);
	UpdateItems();
	SendDvdChangedEvent();
	m_dvd->UpdateButtonImageFor(tsi, pgci);
	m_dvd->UpdateButtonImageFor(tsi, pgci + 1);
}

void TitlesetManager::OnMoveTitleLeftUpdateUI(wxUpdateUIEvent& event) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int pgci = DVD::GetPgci(id);
	event.Enable(pgci>0);
}

void TitlesetManager::OnMoveTitleRightUpdateUI(wxUpdateUIEvent& event) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	PgcArray& pgcs = m_dvd->GetPgcArray(tsi, isMenu);
	event.Enable(pgci<(int)pgcs.Count()-1);
}

void TitlesetManager::OnDelete(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	int vobi = DVD::GetVobi(id);
	if (s_config.GetShowTitleDeletePrompt()) {
		wxString caption = isMenu ? _("Menu") : _T("Title");
		caption += wxString::Format(wxT(" %d"), pgci+1);
		if (vobi>0)
			caption += wxString::Format(_T("-%d"), vobi+1);
		MessageDlg msgDlg(this, wxString::Format(_("Do you really want to delete %s?"), caption.c_str()),
				_("Titleset Manager"), wxYES_NO|wxICON_QUESTION);
		if (msgDlg.ShowModal() != wxYES)
			return;
		s_config.SetShowTitleDeletePrompt(!msgDlg.DontShowAgain());
	}
	if (isMenu) {
		Titleset* ts= NULL;
		if (tsi>=0)
			ts = m_dvd->GetTitlesets()[tsi];
		Menus* menus = &m_dvd->GetVmgm();
		if (ts)
			menus = &ts->GetMenus();
		if (pgci>=(int)menus->GetCount())
			return;
		Pgc* pgc = (*menus)[pgci];
		if (vobi>=(int)pgc->GetVobs().Count())
			return;
		delete pgc->GetVobs()[vobi];
		pgc->GetVobs().RemoveAt(vobi);
		// remove pgc if need
		if (pgc->GetVobs().Count() == 0) {
			delete pgc;
			menus->RemoveAt(pgci);
		}
		// remove titleset if need
		if (ts && ts->GetTitles().Count() == 0
				&& ts->GetMenus().Count() == 0
				&& m_dvd->GetTitlesets().Count() > 1) {
			delete ts;
			m_dvd->GetTitlesets().RemoveAt(tsi);
		}
		// select next menu
		if (GetSelected() == m_selectedMenu) {
			if (pgci >= (int)menus->Count())
				pgci = menus->Count()-1;
			if (pgci>=0)
				SelectMenu(tsi, pgci);
			else
				SelectFirstMenu();
		} else if (m_selectedMenu>=0) {
			wxThumb* mThumb = m_items[m_selectedMenu];
			int mTsi = (mThumb->GetId()>>24)-1;
			int mPgci = ((mThumb->GetId()>>8) & 0xFFFF) / 2;
			if (mTsi == tsi && mPgci > pgci)
				m_selectedMenu--;
		}
	} else { // title
		if (tsi >= (int)m_dvd->GetTitlesets().Count())
			return;
		Titleset* ts = m_dvd->GetTitlesets()[tsi];

		//title
		if (pgci>=(int)ts->GetTitles().GetCount())
			return;
		Pgc* pgc = ts->GetTitles()[pgci];
		if (vobi>=(int)pgc->GetVobs().Count())
			return;
		// remove vob
		delete pgc->GetVobs()[vobi];
		pgc->GetVobs().RemoveAt(vobi);
		// remove title if need
		if (pgc->GetVobs().Count() == 0) {
			delete pgc;
			ts->GetTitles().RemoveAt(pgci);
			for (unsigned int pgci2 = pgci; pgci2 <= ts->GetTitles().GetCount(); pgci2++) {
				m_dvd->UpdateButtonImageFor(tsi, pgci2);
			}
		}
		// remove titleset if need
		if (ts->GetTitles().Count() == 0
				&& ts->GetMenus().Count() == 0
				&& m_dvd->GetTitlesets().Count() > 1) {
			delete ts;
			m_dvd->GetTitlesets().RemoveAt(tsi);
		}
	}
	SetSelected(-1);
	UpdateItems();
	SendDvdChangedEvent();
}

void TitlesetManager::OnProps(wxCommandEvent& WXUNUSED(event)) {
	if (GetSelected()<0)
		return;
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	bool isMenu = DVD::IsMenu(id);
	int vobi = DVD::GetVobi(id);
	if (isMenu) { // menu
		MenuPropDlg propDlg(this->GetParent(), m_dvd, tsi, pgci);
		if (propDlg.ShowModal() == wxID_OK) {
			SendMenuSelectedEvent(); // updated MenuEditor
			SendDvdChangedEvent();
		}
	} else { //title
		TitlePropDlg propDlg(this->GetParent(), m_dvd, tsi, pgci, vobi);
		if (propDlg.ShowModal() == wxID_OK) {
			m_dvd->UpdateButtonImageFor(tsi, pgci);
			SendMenuSelectedEvent(); // updated MenuEditor
			SendDvdChangedEvent();
		}
	}
	m_pointed = -1;
	Refresh();
}

void TitlesetManager::OnCopy(wxCommandEvent& WXUNUSED(event)) {
	int id = GetSelectedId();
	int tsi = DVD::GetTsi(id);
	int pgci = DVD::GetPgci(id);
	int vobi = DVD::GetVobi(id);
	Vob* vob = m_dvd->GetVob(tsi, DVD::IsMenu(id), pgci, vobi);
	if (!vob || !vob->GetMenu())
		return;
	CopyXmlToClipboard(vob->GetMenu()->GetXML(DVDSTYLER_XML), DATAFORMAT_MENU);
}

void TitlesetManager::OnPaste(wxCommandEvent& WXUNUSED(event)) {
	int tsi = GetSelected() >= 0 ? DVD::GetTsi(GetSelectedId()) : m_dvd->GetTitlesets().Count() - 1;
	Menu* menu = new Menu(m_dvd, tsi, m_dvd->GetVideoFormat(), m_dvd->GetAspectRatio());
	wxSvgXmlDocument doc;
	if (!GetXmlFromClipboard(DATAFORMAT_MENU, doc))
		return;
	menu->PutXML(doc.GetRoot());
	int pgci = m_dvd->AddMenu(menu, tsi);
	UpdateItems();
	SelectMenu(tsi, pgci);
	SendDvdChangedEvent();
}

void TitlesetManager::OnUpdateUIObjectPaste(wxUpdateUIEvent& event) {
	event.Enable(wxTheClipboard->IsSupported(wxDataFormat(DATAFORMAT_MENU)));
}

