/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2004
 *					All rights reserved
 *
 *  This file is part of GPAC / Osmo4 wxWidgets GUI
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 *		
 */

#include "wxOsmo4.h"
#include "Playlist.h"


#include "playlist.xpm"


PLEntry::PLEntry(wxString url)
{
	m_url = strdup(url.mb_str(wxConvUTF8));
	Bool is_remote = 0;;
	wxCharBuffer the_url = (const char *) url.mb_str(wxConvUTF8);
	const char *_url = strstr(the_url, "://");
	if (_url) {_url += 3; is_remote = 1; }
	else _url = (const char *) the_url;

	char *str = strrchr(_url, '\\');
	if (!str) str = strrchr(_url, '/');
	if (str && strlen(str+1)) {
		m_disp_name = strdup(str+1);
		str = strrchr(m_disp_name, '.');
		if (str) str[0] = 0;
	} else {
		m_disp_name = strdup(_url);
		if (!is_remote) {
			m_disp_name = strdup(_url);
			str = strrchr(m_disp_name, '.');
			if (str) str[0] = 0;
		}
	}
	m_duration = 0;
	m_bIsDead = 0;
	m_bIsPlaying = 0;
	m_bIsSelected = 0;
}

PLEntry::~PLEntry()
{
	if (m_url) free(m_url);
	if (m_disp_name) free(m_disp_name);

}

wxPlaylist::wxPlaylist(wxWindow *parent)
             : wxFrame(parent, -1, wxString(_T("Osmo4 Playlist")), wxDefaultPosition, wxDefaultSize, 
			 wxCLOSE_BOX | wxSYSTEM_MENU | wxCAPTION | wxRESIZE_BORDER)
{

	m_pApp = (wxOsmo4Frame *)parent;

	m_pOpen = new wxBitmap(pl_open);
	m_pSave = new wxBitmap(pl_save);
	m_pAdd = new wxBitmap(pl_add);
	m_pRem = new wxBitmap(pl_rem);
	m_pUp = new wxBitmap(pl_up);
	m_pDown = new wxBitmap(pl_down);
	m_pSort = new wxBitmap(pl_sort);

	m_pToolBar = CreateToolBar(wxTB_HORIZONTAL);
	m_pAddBut = new wxMenuButton(m_pToolBar, ID_PL_ADD_FILE, *m_pAdd);
	wxMenu *menu = new wxMenu();
	menu->Append(ID_PL_ADD_URL, wxT("&Url"));
	menu->Append(ID_PL_ADD_DIR, wxT("&Directory"));
	menu->Append(ID_PL_ADD_DIR_REC, wxT("&Directory and subfolders"));
	m_pAddBut->AssignMenu(menu);
	m_pAddBut->SetToolTip(wxString(wxT("Add Files")));

	m_pRemBut = new wxMenuButton(m_pToolBar, ID_PL_REM_FILE, *m_pRem);
	menu = new wxMenu();
	menu->Append(ID_PL_REM_ALL, wxT("&Clear"));
	menu->Append(ID_PL_REM_DEAD, wxT("&Remove dead entries"));
	m_pRemBut->AssignMenu(menu);
	m_pRemBut->SetToolTip(wxString(wxT("Remove Selected Files")));

	m_pSortBut = new wxMenuButton(m_pToolBar, ID_PL_SORT_FILE, *m_pSort);
	menu = new wxMenu();
	menu->Append(ID_PL_SORT_TITLE, wxT("&Sort by Title"));
	menu->Append(ID_PL_SORT_FILE, wxT("&Sort by file name"));
	menu->Append(ID_PL_SORT_DUR, wxT("&Sort by Duration"));
	menu->AppendSeparator();
	menu->Append(ID_PL_REVERSE, wxT("&Reverse"));
	menu->Append(ID_PL_RANDOMIZE, wxT("&Randomize"));
	m_pSortBut->AssignMenu(menu);
	m_pSortBut->SetToolTip(wxString(wxT("Sort Playlist by filename")));

	m_pToolBar->AddTool(ID_PL_OPEN, wxT(""), *m_pOpen, wxT("Open Playlist"));
	m_pToolBar->AddTool(ID_PL_SAVE, wxT(""), *m_pSave, wxT("Save Playlist"));
	m_pToolBar->AddSeparator();
	m_pToolBar->AddControl(m_pAddBut);
	m_pToolBar->AddControl(m_pRemBut);
	m_pToolBar->AddSeparator();
	m_pToolBar->AddTool(ID_PL_UP, wxT(""), *m_pUp, wxT("Moves Selected Files Up"));
	m_pToolBar->AddTool(ID_PL_DOWN, wxT(""), *m_pDown, wxT("Moves Selected Files Down"));
	m_pToolBar->AddSeparator();
	m_pToolBar->AddControl(m_pSortBut);
	m_pToolBar->Realize();

	m_FileList = new wxListCtrl(this, ID_FILE_LIST, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);

	m_FileList->InsertColumn(0, wxT(""), wxLIST_FORMAT_LEFT, 1);
	m_FileList->InsertColumn(1, wxT("Title"), wxLIST_FORMAT_LEFT, 1);
	m_FileList->InsertColumn(2, wxT("Duration"), wxLIST_FORMAT_LEFT, 1);

	m_entries = NewChain();
	m_cur_entry = -1;
	m_all_dead_entries = -1;

	SetSize(300, 400);
	Centre();
}

wxPlaylist::~wxPlaylist()
{
	Clear();
	DeleteChain(m_entries);
	delete m_pAddBut;
	delete m_pRemBut;
	delete m_pSortBut;
	delete m_pOpen;
	delete m_pSave;
	delete m_pAdd;
	delete m_pRem;
	delete m_pUp;
	delete m_pDown;
	delete m_pSort;
}


BEGIN_EVENT_TABLE(wxPlaylist, wxFrame)
	EVT_CLOSE(wxPlaylist::OnClose)
	EVT_SIZE(wxPlaylist::OnSize)
	EVT_TOOL(ID_PL_ADD_FILE, wxPlaylist::OnAddFile) 
	EVT_TOOL(ID_PL_ADD_URL, wxPlaylist::OnAddURL) 
	EVT_TOOL(ID_PL_ADD_DIR, wxPlaylist::OnAddDir) 
	EVT_TOOL(ID_PL_ADD_DIR_REC, wxPlaylist::OnAddDirRec) 
	EVT_TOOL(ID_PL_REM_FILE, wxPlaylist::OnRemFile) 
	EVT_TOOL(ID_PL_REM_ALL, wxPlaylist::OnRemAll) 
	EVT_TOOL(ID_PL_REM_DEAD, wxPlaylist::OnRemDead) 
	EVT_TOOL(ID_PL_UP, wxPlaylist::OnSelUp) 
	EVT_TOOL(ID_PL_DOWN, wxPlaylist::OnSelDown) 
	EVT_TOOL(ID_PL_SAVE, wxPlaylist::OnSave) 
	EVT_TOOL(ID_PL_OPEN, wxPlaylist::OnOpen) 
	EVT_MENU(ID_PL_PLAY, wxPlaylist::OnPlay) 
	EVT_MENU(ID_PL_RANDOMIZE, wxPlaylist::OnRandomize) 
	EVT_MENU(ID_PL_REVERSE, wxPlaylist::OnReverseList) 
	EVT_MENU(ID_PL_SEL_REV, wxPlaylist::OnReverseSelection) 
	EVT_MENU(ID_PL_SORT_TITLE, wxPlaylist::OnSortTitle) 
	EVT_MENU(ID_PL_SORT_FILE, wxPlaylist::OnSortFile) 
	EVT_MENU(ID_PL_SORT_DUR, wxPlaylist::OnSortDuration) 
	EVT_LIST_ITEM_ACTIVATED(ID_FILE_LIST, wxPlaylist::OnItemActivate) 
	EVT_LIST_ITEM_RIGHT_CLICK(ID_FILE_LIST, wxPlaylist::OnRightClick)
END_EVENT_TABLE()

void wxPlaylist::OnClose(wxCloseEvent &event)
{
	if (event.CanVeto()) {
		event.Veto();
		Hide();
	}
}

void wxPlaylist::OnSize(wxSizeEvent &event)
{
	wxSize s = event.GetSize();
	m_FileList->SetSize(0, 0, s.GetWidth()-5, s.GetHeight());
	m_FileList->SetColumnWidth(0, 30);
	m_FileList->SetColumnWidth(2, 60);
	m_FileList->SetColumnWidth(1, s.GetWidth()-100);
}

void wxPlaylist::Clear()
{
	m_FileList->DeleteAllItems();
	while (ChainGetCount(m_entries)) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, 0);
		ChainDeleteEntry(m_entries, 0);
		delete ple;
	}
	m_cur_entry = -1;
}

void wxPlaylist::ClearButPlaying()
{
	PLEntry *p = NULL;
	if (m_cur_entry >= 0) {
		p = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
		ChainDeleteEntry(m_entries, m_cur_entry);
	}
	Clear();
	if (p) {
		ChainAddEntry(m_entries, p);
		m_cur_entry = 0;
	}
	RefreshList();
}

void wxPlaylist::UpdateEntry(u32 idx)
{
	char szText[20];

	PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, idx);
	if (idx+1<10) sprintf(szText, "00%d", idx+1);
	else if (idx+1<100) sprintf(szText, "0%d", idx+1);
	else sprintf(szText, "%d", idx+1);
	m_FileList->SetItem(idx, 0, wxString(szText, wxConvUTF8));

	wxString str;
	if (ple->m_bIsDead) str = wxT("!! ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" (DEAD)!!)");
	else if (ple->m_bIsPlaying) str = wxT(">> ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" >>");
	else str = wxString(ple->m_disp_name, wxConvUTF8);
	m_FileList->SetItem(idx, 1, str);
	
	if (ple->m_duration) {
		u32 h = (u32) (ple->m_duration / 3600);
		u32 m = (u32) (ple->m_duration / 60) - h*60;
		u32 s = (u32) (ple->m_duration) - h*3600 - m*60;
		m_FileList->SetItem(idx, 2, wxString::Format(wxT("%02d:%02d:%02d"), h, m, s) );
	} else {
		m_FileList->SetItem(idx, 2, wxT("Unknown"));
	}
}

void wxPlaylist::RefreshList()
{
	u32 i, top_idx;
	char szPath[M4_MAX_PATH];
	Bool first_sel;

	top_idx = m_FileList->GetTopItem();
	m_FileList->DeleteAllItems();

	first_sel = 0;
	for (i=0; i<ChainGetCount(m_entries); i++) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, i);
		m_FileList->InsertItem(i, wxT(""));
		m_FileList->SetItemData(i, (u32) ple);
		UpdateEntry(i);

		if (ple->m_bIsPlaying) m_cur_entry = i;

		if (ple->m_bIsSelected) {
			m_FileList->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED | wxLIST_MASK_STATE);
			ple->m_bIsSelected = 0;
			/*ensure first item of selection is visible*/
			if (!first_sel) {
				first_sel = 1;
				top_idx = i;
			}
		}
	}
	
	if (m_cur_entry >= (s32) ChainGetCount(m_entries)-1) m_cur_entry = ChainGetCount(m_entries)-1;
	else {
		s32 last_idx = top_idx + m_FileList->GetCountPerPage();
		m_FileList->EnsureVisible(top_idx);
		if (ChainGetCount(m_entries)<1+ (u32) last_idx) last_idx = ChainGetCount(m_entries)-1;
		m_FileList->EnsureVisible(last_idx);
	}

	strcpy((char *) szPath, m_pApp->szAppPath);
#ifdef WIN32
	strcat(szPath, "gpac_pl.m3u");
#else
	strcat(szPath, ".gpac_pl.m3u");
#endif
	Save(szPath, 1);
}

void wxPlaylist::OnAddFile(wxCommandEvent &WXUNUSED(event)) 
{
	wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), m_pApp->GetFileFilter(), wxOPEN | wxCHANGE_DIR | wxHIDE_READONLY | wxMULTIPLE);

	if (dlg.ShowModal() == wxID_OK) {
		wxArrayString stra;
		dlg.GetPaths(stra);
		for (u32 i=0; i<stra.GetCount(); i++) {
			PLEntry *ple = new PLEntry(stra[i]);
			ChainAddEntry(m_entries, ple);
		}
		m_all_dead_entries = -1;
		RefreshList();
	}
}


static Bool pl_enum_dir_item(void *cbck, char *item_name, char *item_path)
{
	wxPlaylist *_this = (wxPlaylist *)cbck;

	if (is_supported_file(_this->m_pApp->m_user.config, item_name, 1)) {
		PLEntry *ple = new PLEntry(wxString(item_path, wxConvUTF8) );
		ChainAddEntry(_this->m_entries, ple);
	}
	return 0;
}

static Bool pl_enum_dir_dirs(void *cbck, char *item_name, char *item_path)
{
	DIR_Enum(item_path, 0, pl_enum_dir_item, cbck);
	DIR_Enum(item_path, 1, pl_enum_dir_dirs, cbck);
	return 0;
}


void wxPlaylist::AddDir(Bool do_recurse) 
{
	wxDirDialog dlg(this);
	dlg.SetPath(wxString(szCacheDir, wxConvUTF8) );
	if (dlg.ShowModal() != wxID_OK) return;

	strcpy(szCacheDir, dlg.GetPath().mb_str(wxConvUTF8));
	DIR_Enum(szCacheDir, 0, pl_enum_dir_item, this);
	if (do_recurse) DIR_Enum(szCacheDir, 1, pl_enum_dir_dirs, this);
	m_all_dead_entries = -1;
	RefreshList();
}
void wxPlaylist::OnAddDir(wxCommandEvent &WXUNUSED(event)) 
{
	AddDir(0);
}
void wxPlaylist::OnAddDirRec(wxCommandEvent &WXUNUSED(event)) 
{
	AddDir(1);
}

void wxPlaylist::OnAddURL(wxCommandEvent &WXUNUSED(event)) 
{
	OpenURLDlg dlg(this, m_pApp->m_user.config);
	if (dlg.ShowModal() != wxID_OK) return;
	PLEntry *ple = new PLEntry(dlg.m_urlVal);
	ChainAddEntry(m_entries, ple);
	m_all_dead_entries = -1;
	RefreshList();
}

void wxPlaylist::OnRemFile(wxCommandEvent &WXUNUSED(event)) 
{
	if (!m_FileList->GetSelectedItemCount()) return;

	long item = -1;
	for (;;) {
        item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
		if (item == -1) break;
		PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item);
		ChainDeleteItem(m_entries, ple);
		delete ple;
    }
	RefreshList();
}

void wxPlaylist::OnRemAll(wxCommandEvent &WXUNUSED(event)) 
{
	Clear();
	RefreshList();
	m_cur_entry = -1;
	m_all_dead_entries = 1;
}

void wxPlaylist::OnRemDead(wxCommandEvent &WXUNUSED(event)) 
{
	for (u32 i=0; i<ChainGetCount(m_entries); i++) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, i);
		if (!ple->m_bIsDead) continue;
		ChainDeleteEntry(m_entries, i);
		i--;
		delete ple;
	}
	m_all_dead_entries = ChainGetCount(m_entries) ? 0 : 1;
	RefreshList();
}


void wxPlaylist::OnSelUp(wxCommandEvent &WXUNUSED(event)) 
{
	s32 i;
	if (!m_FileList->GetSelectedItemCount()) return;
	long item = -1;
	item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
	if (item <= 0) return;
	
	item = -1;
	for (;;) {
        item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
		if (item == -1) break;
		PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item);
		i = ChainDeleteItem(m_entries, ple);
		assert(i>=1);
		ChainInsertEntry(m_entries, ple, i-1);
		ple->m_bIsSelected = 1;
    }
	RefreshList();
}

void wxPlaylist::OnSelDown(wxCommandEvent &WXUNUSED(event)) 
{
	s32 i;

	if (!m_FileList->GetSelectedItemCount()) return;
	long item = -1;
	for (;;) {
		item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
		if (item == -1) break;
	}
	if ((u32) item + 1 == ChainGetCount(m_entries)) return;
	
	item = -1;
	for (;;) {
        item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
		if (item == -1) break;
		PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item);
		i = ChainDeleteItem(m_entries, ple);
		assert(i>=1);
		ChainInsertEntry(m_entries, ple, i+1);
		ple->m_bIsSelected = 1;
    }
	RefreshList();
}



void wxPlaylist::OnSave(wxCommandEvent & WXUNUSED(event)) 
{
	Bool save_m3u;
	char szPath[M4_MAX_PATH];
	if (!ChainGetCount(m_entries)) return;

	wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxSAVE | wxCHANGE_DIR | wxOVERWRITE_PROMPT);
	if (dlg.ShowModal() != wxID_OK) return;

	strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8));
	strlwr(szPath);
	save_m3u = (dlg.GetFilterIndex()==0) ? 1 : 0;
	if (save_m3u) {
		if (!strstr(szPath, ".m3u")) {
			strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8));
			strcat(szPath, ".m3u");
		} else {
			strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8));
		}
	} else {
		if (!strstr(szPath, ".pls")) {
			strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8));
			strcat(szPath, ".pls");
		} else {
			strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8));
		}
	}
	Save(szPath, save_m3u);
}

void wxPlaylist::Save(char *szPath, Bool save_m3u)
{
	FILE *out = fopen(szPath, "wt");
	if (!save_m3u) 
		fprintf(out, "[playlist]\nNumberOfEntries=%d\n", ChainGetCount(m_entries));

	for (u32 i=0; i<ChainGetCount(m_entries); i++) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, i);
		if (save_m3u) {
			fprintf(out, "%s\n", ple->m_url);
		} else {
			fprintf(out, "File%d=%s\n", i+1, ple->m_url);
			fprintf(out, "Title%d=%s\n", i+1, ple->m_disp_name);
			if (ple->m_duration) fprintf(out, "Length%d=%d\n", i+1, ple->m_duration);
			else fprintf(out, "Length%d=-1\n", i+1);
		}
	}
	if (!save_m3u) fprintf(out, "Version=2\n");

	fprintf(out, "\n");
	fclose(out);
}

void wxPlaylist::OnOpen(wxCommandEvent & WXUNUSED(event)) 
{
	wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U & PLS Playlists|*.m3u;*.pls|M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxOPEN | wxCHANGE_DIR | wxHIDE_READONLY);
	if (dlg.ShowModal() != wxID_OK) return;

	Clear();
	OpenPlaylist(dlg.GetPath());
	m_cur_entry = 0;
	Play();
}

void wxPlaylist::OpenPlaylist(wxString filename)
{
	FILE *pl;
	PLEntry *ple;
	Bool load_m3u, go;
	char szLine[M4_MAX_PATH];
	pl = fopen(filename.mb_str(wxConvUTF8) , "rt");
	if (!pl) return;

	ple = NULL;
	load_m3u = 1;
	while (!feof(pl)) {
		fgets(szLine, M4_MAX_PATH, pl);
		go = 1;
		while (go) {
			switch (szLine[strlen(szLine)-1]) {
			case '\n':
			case '\r':
			case ' ':
				szLine[strlen(szLine)-1] = 0;
				break;
			default:
				go = 0;
				break;
			}
		}
		if (!strlen(szLine)) continue;
		if (!stricmp(szLine, "[playlist]")) {
			load_m3u = 0;
		} else if (load_m3u) {
			ple = new PLEntry(wxString(szLine, wxConvUTF8) );
			ChainAddEntry(m_entries, ple);
		} else if (!strnicmp(szLine, "file", 4)) {
			char *st = strchr(szLine, '=');
			if (!st) ple = NULL;
			else {
				ple = new PLEntry(wxString(st + 1, wxConvUTF8) );
				ChainAddEntry(m_entries, ple);
			}
		} else if (ple && !strnicmp(szLine, "Length", 6)) {
			char *st = strchr(szLine, '=');
			s32 d = atoi(st + 1);
			if (d>0) ple->m_duration = d;
		} else if (ple && !strnicmp(szLine, "Title", 5)) {
			char *st = strchr(szLine, '=');
			free(ple->m_disp_name);
			ple->m_disp_name = strdup(st + 6);
		}
	}
	fclose(pl);
	m_all_dead_entries = -1;
	m_cur_entry = -1;
	RefreshList();
}

void wxPlaylist::OnRightClick(wxListEvent & event) 
{
	if (!m_FileList->GetItemCount()) return;

	wxMenu *popup = new wxMenu();	

	if (m_FileList->GetSelectedItemCount()==1) {
		popup->Append(ID_PL_PLAY, wxT("Play"));
		popup->AppendSeparator();
	}
	popup->Append(ID_PL_SEL_REV, wxT("Inverse Selection"));
	if (m_FileList->GetSelectedItemCount()) popup->Append(ID_PL_REM_FILE, wxT("Remove File(s)"));
	if (m_FileList->GetItemCount()>1) {
		popup->AppendSeparator();
		popup->Append(ID_PL_SORT_TITLE, wxT("Sort By Title"));
		popup->Append(ID_PL_SORT_FILE, wxT("Sort By File Name"));
		popup->Append(ID_PL_SORT_DUR, wxT("Sort By Duration"));
		popup->AppendSeparator();
		popup->Append(ID_PL_REVERSE, wxT("Reverse List"));
		popup->Append(ID_PL_RANDOMIZE, wxT("Randomize"));
	}
	
	PopupMenu(popup, event.GetPoint());
	delete popup;
}

void wxPlaylist::OnReverseSelection(wxCommandEvent &WXUNUSED(event) ) 
{
	u32 i;
	long item = -1;
	for (;;) {
		item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
		if (item == -1) break;
		PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item);
		ple->m_bIsSelected = 1;
	}
	for (i=0; i<ChainGetCount(m_entries); i++) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, i);
		ple->m_bIsSelected = !ple->m_bIsSelected;
	}
	RefreshList();
}

void wxPlaylist::OnReverseList(wxCommandEvent &WXUNUSED(event) )
{
	u32 count = ChainGetCount(m_entries);
	u32 hcount = count / 2;
	count--;
	for (u32 i=0; i<hcount; i++) {
		PLEntry *ple1 = (PLEntry *) ChainGetEntry(m_entries, i);
		PLEntry *ple2 = (PLEntry *) ChainGetEntry(m_entries, count-i);
		ChainDeleteEntry(m_entries, i);
		ChainInsertEntry(m_entries, ple2, i);
		ChainDeleteEntry(m_entries, count-i);
		ChainInsertEntry(m_entries, ple1, count-i);
	}
	RefreshList();
}

void wxPlaylist::OnRandomize(wxCommandEvent &WXUNUSED(event) )
{
	Chain *new_entries = NewChain();

	M4_RandInit(0);

	while (ChainGetCount(m_entries)>1) {
		u32 pos = M4_Rand() % (ChainGetCount(m_entries)-1);
		PLEntry *ple = (PLEntry *)ChainGetEntry(m_entries, pos);
		ChainDeleteEntry(m_entries, pos);
		ChainAddEntry(new_entries, ple);
	}
	PLEntry *ple = (PLEntry *)ChainGetEntry(m_entries, 0);
	ChainDeleteEntry(m_entries, 0);
	ChainAddEntry(new_entries, ple);

	DeleteChain(m_entries);
	m_entries = new_entries;
	m_cur_entry = -1;
	RefreshList();
}

void wxPlaylist::Sort(u32 type)
{
	u32 i, j, smallest;

	for (i=0; i<ChainGetCount(m_entries)-1; i++) {
		smallest = i;
		for (j=i+1; j<ChainGetCount(m_entries); j++) {
			PLEntry *ple2 = (PLEntry *) ChainGetEntry(m_entries, smallest);
			PLEntry *ple1 = (PLEntry *) ChainGetEntry(m_entries, j);
			s32 test = 0;
			switch (type) {
			case 0:
                test = stricmp(ple1->m_url, ple2->m_url);
				break;
			case 1:
                test = stricmp(ple1->m_disp_name, ple2->m_disp_name);
				break;
			case 2:
                test = ple1->m_duration - ple2->m_duration;
				break;
			}
			if (test<0) smallest = j;
		}
		PLEntry *ple = (PLEntry *)ChainGetEntry(m_entries, smallest);
		ChainDeleteEntry(m_entries, smallest);
		ChainInsertEntry(m_entries, ple, i);
	}
	m_cur_entry = -1;
	RefreshList();
}

void wxPlaylist::OnSortFile(wxCommandEvent &WXUNUSED(event) ) { Sort(0); }
void wxPlaylist::OnSortTitle(wxCommandEvent &WXUNUSED(event) ) { Sort(1); }
void wxPlaylist::OnSortDuration(wxCommandEvent &WXUNUSED(event) ) { Sort(2); }

void wxPlaylist::RefreshCurrent()
{
	PLEntry *ple;
	if (m_cur_entry<0) return;
	ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple && ple->m_bIsPlaying) {
		ple->m_bIsPlaying = 0;
		UpdateEntry(m_cur_entry);
	}
}

Bool wxPlaylist::HasValidEntries()
{
	u32 nb_dead = 0;
	if (m_all_dead_entries==-1) {
		for (u32 i=0; i<ChainGetCount(m_entries); i++) {
			PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, i);
			ple->m_bIsPlaying = 0;
			if (ple->m_bIsDead) nb_dead ++;
		}
		m_all_dead_entries = (nb_dead==ChainGetCount(m_entries)) ? 1 : 0;
	}
	return !m_all_dead_entries;
}

void wxPlaylist::Play()
{
	PLEntry *ple;

	if (!HasValidEntries()) return;

	RefreshCurrent();

	if (m_cur_entry >= (s32)ChainGetCount(m_entries)) {
		if (!m_pApp->m_loop) return;
		m_cur_entry = 0;
	}

	ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple->m_bIsDead) {
		m_cur_entry++;
		Play();
	} else {
		char szPLE[20];
		ple->m_bIsPlaying = 1;
		UpdateEntry(m_cur_entry);
		sprintf(szPLE, "%d", m_cur_entry);
		IF_SetKey(m_pApp->m_user.config, "General", "PLEntry", szPLE);
		m_pApp->DoConnect();
	}
}

void wxPlaylist::OnItemActivate(wxListEvent &WXUNUSED(event) ) 
{
	long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
	if (item==-1) return;
	RefreshCurrent();
	m_cur_entry = item;
	Play();
}


void wxPlaylist::OnPlay(wxCommandEvent &WXUNUSED(event))
{
	long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
	if (item==-1) return;

	RefreshCurrent();
	m_cur_entry = item;
	Play();
}

void wxPlaylist::Truncate()
{
	if (m_cur_entry<0) return;
	while ((u32) m_cur_entry+1 < ChainGetCount(m_entries)) {
		PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry+1);
		ChainDeleteEntry(m_entries, m_cur_entry+1);
		delete ple;
	}
	RefreshList();
}

void wxPlaylist::QueueURL(wxString filename)
{
	char *ext = strrchr(filename.mb_str(wxConvUTF8), '.');
	if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls")) ) {
		OpenPlaylist(filename);
	} else {
		PLEntry *ple = new PLEntry(filename);
		ChainAddEntry(m_entries, ple);
	}
}

void wxPlaylist::PlayNext()
{
	RefreshCurrent();
	if (1+m_cur_entry < (s32)ChainGetCount(m_entries)) {
		m_cur_entry++;
		Play();
	}
}

void wxPlaylist::PlayPrev()
{
	RefreshCurrent();
	if (m_cur_entry>0) {
		m_cur_entry--;
		Play();
	}
}

void wxPlaylist::SetDead()
{
	PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple) {
		ple->m_bIsDead = 1;
		UpdateEntry(m_cur_entry);
		if (ple->m_bIsPlaying) PlayNext();
		m_all_dead_entries = -1;
	}
}
void wxPlaylist::SetDuration(u32 duration)
{
	PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple) {
		ple->m_duration = duration;
		UpdateEntry(m_cur_entry);
	}
}

wxString wxPlaylist::GetDisplayName()
{
	PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple) return wxString(wxString(ple->m_disp_name, wxConvUTF8) );
	return wxT("");
}

wxString wxPlaylist::GetURL()
{
	PLEntry *ple = (PLEntry *) ChainGetEntry(m_entries, m_cur_entry);
	if (ple) return wxString(ple->m_url, wxConvUTF8);
	return wxT("");
}

