// LMainWindow.cpp
// ```````````````

#include "stdafx.h"
#include "LMainWindow.h"
#include "getLinkTime.h"
#include "SettingsManager.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// HELPER STRUCTURES
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

struct listItemData {

	TSTRING m_path;
	TSTRING m_fname;
	bool m_bIsQueued;
	bool m_bAlreadyPlayed;

	listItemData(TSTRING& path, TCHAR* fname) {
		m_path.assign(path);
		m_fname.assign(fname);
		m_bIsQueued = false;
		m_bAlreadyPlayed = false;
	}

	TSTRING getFullPath() {
		TSTRING ret = m_path;
		ret.append(_T("\\"));
		ret.append(m_fname);
		return ret;
	}

	TSTRING format(int display_mode) {
		switch (display_mode) {
			case LMainWindow::DISPLAY_FULLPATH: {
				return getFullPath();
			} break;
			case LMainWindow::DISPLAY_NAMEONLY: {
				return m_fname;
			} break;
			default: {
			return _T("?");
			}
		}
	}
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CONSTRUCTION / DECONSTRUCTION
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

LMainWindow::LMainWindow() :
MGWindow((void*)IDI_LILLYMAIN, _T(APPLICATION_TITLE))
{
	// Restore window position
	this->resize(
		SettingsManager::get(SM_WINDOWXPOS,   100),
		SettingsManager::get(SM_WINDOWYPOS,   100),
		SettingsManager::get(SM_WINDOWWIDTH,  400),
		SettingsManager::get(SM_WINDOWHEIGHT, 250)
	);

	// Window properties
	this->showStatusBar(true);
	this->onClosing.assign(this, MGCB(LMainWindow::OnWindowClosing));
	this->onResize.assign(this, MGCB(LMainWindow::OnWindowResize));

	// Child windows
	wndSettings = 0;
	
	// Create other UI elements
	pbProgress = new MGProgressBar(this, true, true);
	pbProgress->removeStyles();
	pbProgress->onDrag.assign(this, MGCB(LMainWindow::OnProgressDragged));
	pbProgress->onMouseDown.assign(this, MGCB(LMainWindow::OnProgressMDown));
	/*pbVolume = new MGProgressBar(this, false);
	pbVolume->setValue(100);
	pbVolume->removeStyles();*/
	pbLevelLeft = new MGProgressBar(this, false, false);
	pbLevelLeft->setValue(0);
	pbLevelLeft->removeStyles();
	pbLevelLeft->removeBorder();
	pbLevelRight = new MGProgressBar(this, false, false);
	pbLevelRight->setValue(0);
	pbLevelRight->removeStyles();
	pbLevelRight->removeBorder();
	lbMain = new MGListBox(this, false, true);
	lbMain->onDoubleClick.assign(this, MGCB(LMainWindow::OnListDoubleClick));
	llProgress = new MGLabel(this, true);
	llVolume = new MGLabel(this, false);

	// Register hotkeys
	hkNextSong = new MGHotkey(   this, true, false, true, MGVK_S, MGCB(LMainWindow::playNext) );
	hkPlayPause = new MGHotkey(  this, true, false, true, MGVK_D, MGCB(LMainWindow::OnPlayPause) );
	hkFocusWindow = new MGHotkey(this, true, false, true, MGVK_F, MGCB(LMainWindow::OnHotkeyFocusWindow) );
		
	// Create all menu items
	mnuFile				= new MGMenuItem(this, _T("&File"));
		mnuFileOpenFolder=(new MGMenuItem(mnuFile, _T("Open folder...\tF4"),		MGCB(LMainWindow::OnFileOpenFolder)))->setShortcut(false, false, false, MGVK_F4);
		mnuFileAddFolder= (new MGMenuItem(mnuFile, _T("&Add folder...\tF3"),		MGCB(LMainWindow::OnFileAddFolder))) ->setShortcut(false, false, false, MGVK_F3);
		mnuFileAddFiles = (new MGMenuItem(mnuFile, _T("Add &file(s)...\tCtrl+L"),MGCB(LMainWindow::OnFileAddFiles)))  ->setShortcut(false, true, false, MGVK_L);
		mnuFile->addChildMenuSeparator();
		mnuFileOpenPlaylist=new MGMenuItem(mnuFile, _T("O&pen playlist...\tCtrl+O"));
		mnuFileSavePlaylist=new MGMenuItem(mnuFile, _T("&Save playlist...\tCtrl+S"));
		mnuFile->addChildMenuSeparator();
		mnuFileExit       = (new MGMenuItem(mnuFile, _T("E&xit\tEsc"),				MGCB(LMainWindow::OnFileExit)))   ->setShortcut(false, false, false, MGVK_ESC);
	mnuEdit           = new MGMenuItem(this, _T("&Edit"));
		mnuEditProperties = new MGMenuItem(mnuEdit, _T("&Properties\tF2"));
		mnuEditRemove     = new MGMenuItem(mnuEdit, _T("&Remove\tDel"));
		mnuEditTools      = new MGMenuItem(mnuEdit, _T("&Tools"));
			mnuTSelectCurrent = new MGMenuItem(mnuEditTools, _T("Select &playing item\t~"), MGCB(LMainWindow::OnToolsSelectCurrent));
			mnuTSelectCurrent->setShortcut(false, false, false, MGVK_TILDE);
			mnuTClearAll      = new MGMenuItem(mnuEditTools, _T("C&lear list\tCtrl+N"));
		mnuEdit->addChildMenuSeparator();
		mnuEditFind       = new MGMenuItem(mnuEdit, _T("&Find...\tCtrl+F"));
	mnuPlay           = new MGMenuItem(this, _T("&Play"));
		mnuPlayPlay       = (new MGMenuItem(mnuPlay, _T("&Play\tEnter"),			MGCB(LMainWindow::OnPlayPlay)))           ->setShortcut(false, false, false, MGVK_ENTER);
		mnuPlayPause      = (new MGMenuItem(mnuPlay, _T("Pa&use\tSpace"),			MGCB(LMainWindow::OnPlayPause)))      ->setShortcut(false, false, false, MGVK_SPACE);
		mnuPlayStop       = (new MGMenuItem(mnuPlay, _T("&Stop\t0"),               MGCB(LMainWindow::OnPlayStop)))         ->setShortcut(false, false, false, MGVK_0);
		mnuPlay->addChildMenuSeparator();
		mnuPlayNext       = (new MGMenuItem(mnuPlay, _T("&Next\tTab"),				MGCB(LMainWindow::playNext)))         ->setShortcut(false, false, false, MGVK_TAB);
		mnuPlayPrevious   = (new MGMenuItem(mnuPlay, _T("P&revious\tShift+Tab")))                                          ->setShortcut(false, false, true,  MGVK_TAB);
		mnuPlayQueue	  = (new MGMenuItem(mnuPlay, _T("&Queue\tIns"),            MGCB(LMainWindow::OnPlayQueue)))        ->setShortcut(false, false, false, MGVK_INS);
	mnuFavorites      = new MGMenuItem(this, _T("F&avorites"));
		mnuFavsAdd        = new MGMenuItem(mnuFavorites, _T("&Add playlist to favorites...\tCtrl+D"));
	mnuOptions        = new MGMenuItem(this, _T("&Options"));
		mnuOptSettings    = (new MGMenuItem(mnuOptions, _T("&Settings...\tF6"),    MGCB(LMainWindow::OnOptionsSettings)))  ->setShortcut(false, false, false, MGVK_F6);
		mnuOptions->addChildMenuSeparator();
		mnuOptRepeatList  = (new MGMenuItem(mnuOptions, _T("&Repeat list\tAlt+N"), MGCB(LMainWindow::OnOptionsRepeatList)))->setShortcut(true, false, false, MGVK_N);
		mnuOptRepeatFile  = (new MGMenuItem(mnuOptions, _T("R&epeat file\tAlt+E"), MGCB(LMainWindow::OnOptionsRepeatFile)))->setShortcut(true, false, false, MGVK_E);
		mnuOptPlayList    = (new MGMenuItem(mnuOptions, _T("&Play list\tAlt+L"),   MGCB(LMainWindow::OnOptionsPlayList)))  ->setShortcut(true, false, false, MGVK_L);
		mnuOptPlayFile    = (new MGMenuItem(mnuOptions, _T("P&lay file\tAlt+F"),   MGCB(LMainWindow::OnOptionsPlayFile)))  ->setShortcut(true, false, false, MGVK_F);
		mnuOptShuffle     = (new MGMenuItem(mnuOptions, _T("S&huffle\tAlt+S"),     MGCB(LMainWindow::OnOptionsShuffle)))   ->setShortcut(true, false, false, MGVK_S);
		mnuOptToggle      = (new MGMenuItem(mnuOptions, _T("&Toggle\tF9"),         MGCB(LMainWindow::OnOptionsToggle)))    ->setShortcut(false, false, false, MGVK_F9);
	mnuView           = new MGMenuItem(this, _T("&View"));
		mnuViewFilenames  = new MGMenuItem(mnuView, _T("View as &filename"),		MGCB(LMainWindow::OnViewFilenames));
		mnuViewFullpath   = new MGMenuItem(mnuView, _T("View as full &path"),		MGCB(LMainWindow::OnViewFullpath));
		mnuViewRelpath    = new MGMenuItem(mnuView, _T("View as &relative path"));
		mnuViewToggle     = new MGMenuItem(mnuView, _T("&Toggle\tF8"),              MGCB(LMainWindow::OnViewToggle));
		mnuView->addChildMenuSeparator();
		mnuViewControls   = new MGMenuItem(mnuView, _T("&Controls"));
		mnuViewProgress   = new MGMenuItem(mnuView, _T("Pr&ogress"));
	mnuHelp           = new MGMenuItem(this, _T("&Help"));
		mnuHelpOverview   = new MGMenuItem(mnuHelp, _T("&Overview...\tF1"));		
		mnuHelpHomepage   = new MGMenuItem(mnuHelp, _T("Visit &homepage...\tCtrl+F1"));		
		mnuHelp->addChildMenuSeparator();
		mnuHelpAbout      = new MGMenuItem(mnuHelp, _T("&About..."),				MGCB(LMainWindow::OnHelpAbout));
	this->updateMenu();
	
	mnuFileAddFiles->setEnabled(false);
	mnuEditRemove->setEnabled(false);
	mnuPlayPrevious->setEnabled(false);
	mnuViewRelpath->setEnabled(false);
	mnuViewProgress->setChecked(true);
	mnuViewControls->setEnabled(false);
	mnuHelpOverview->setEnabled(false);
	mnuHelpHomepage->setEnabled(false);

	// Audio initialisation

	hCurrent = 0;
	lBitrate = 0;
	setShuffleMode(MODE_SHUFFLE);
	setDisplayMode(DISPLAY_NAMEONLY); // Redraws all text, so call before first playlist load
	tmrBASS = new MGTimer(this, 20);
	tmrBASS->onTick.assign(this, MGCB(LMainWindow::OnBASSTimer));
	OnBASSTimer();

	// Showtime
	this->setVisible(true);
}

void LMainWindow::OnWindowClosing() {
	// Called on WM_DESTROY; Our window still exists, but it won't for much longer.

	// Save last window position
	SettingsManager::set(SM_WINDOWWIDTH,  this->getWidth());
	SettingsManager::set(SM_WINDOWHEIGHT, this->getHeight());
	SettingsManager::set(SM_WINDOWXPOS,   this->getX());
	SettingsManager::set(SM_WINDOWYPOS,   this->getY());
}

LMainWindow::~LMainWindow() {
	
	// Free all UI controls
	// TODO: put all ui controls in smart pointers?
	// Freeing all of these is going to be long and tedious and possibly error-prone.
	// It's not strictly necessary since the process space is all disappearing anyway, 
	//   but we may as well try not to leak anything
	if (lbMain) { delete lbMain; lbMain = NULL; }
	// TODO: etc
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PUBLIC FUNCTIONS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void CALLBACK LMainWindow::EventProc(HSYNC handle, DWORD channel, DWORD data, void* user) {
	((LMainWindow*)user)->playNext();
}

void LMainWindow::doChildWindowEvents() {
	if (wndSettings) wndSettings->doEvents();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// PRIVATE FUNCTIONS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void LMainWindow::addFolder(TSTRING &path) {
	WIN32_FIND_DATA wfd = {0};
	TSTRING searchMask = path;
	HANDLE hFind = FindFirstFile(searchMask.append(_T("\\*.*")).c_str(), &wfd);

	// File types to add;

	// TODO: Load from ini
	static const TCHAR* extensions[] = {
		_T(".mp3"), _T(".m4a"), _T(".aac"), _T(".ogg"),
		_T(".ogm"), _T(".flac"), _T(".wav"), _T(".wma")
	};

	// TODO: Faster allocations with placement new

	lbMain->setVisible(false);
	do {
		if (wfd.dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY) {
			for( int i=0, e=COUNTOF(extensions); i < e; i++) {
				if ( _tcsstr(wfd.cFileName, extensions[i]) ) {
					int idx = lbMain->addItem(wfd.cFileName);
					lbMain->setItemData(idx, (void*) new listItemData(path, wfd.cFileName));
					continue;
				}
			}
		}		
	} while (FindNextFile(hFind, &wfd));
	lbMain->setVisible(true);

	repopulateStatusBar();
}

void LMainWindow::playFile(int listIndex) {
	// Close existing file
	bool bWasPaused = (BASS_ChannelIsActive(hCurrent) == BASS_ACTIVE_PAUSED);
	if (hCurrent) BASS_StreamFree(hCurrent);

	// Retrieve file data
	listItemData* d = (listItemData*) lbMain->getItemData(listIndex);	
	d->m_bAlreadyPlayed = true;
	formatListItemColour(listIndex, d);
	curListIndex = listIndex;
	lbMain->setSelected(listIndex);
	lbMain->focus();	

	// Cosmetic: Set window title
	TSTRING windowTitle = d->m_fname;
	windowTitle.append(_T(" - "));
	windowTitle.append(_T(APPLICATION_TITLE));
	this->setCaption(windowTitle.c_str());

	// Point BASS toward the file (note: we might still be paused)	
	TSTRING filepath = d->getFullPath();
	hCurrent = BASS_StreamCreateFile(
		FALSE,
		static_cast<const void*>(filepath.c_str()), 0, 0, BASS_UNICODE
	);
	BASS_ChannelSetSync(hCurrent, BASS_SYNC_END | BASS_SYNC_ONETIME, 0, &EventProc, this);
	BASS_ChannelPlay(hCurrent, true);
	if (bWasPaused) BASS_ChannelPause(hCurrent);

	// Cosmetic: Calculate and store file bitrate
	qwChannelLength = BASS_ChannelGetLength(hCurrent, BASS_POS_BYTE);
	double time = BASS_ChannelBytes2Seconds( hCurrent, qwChannelLength);
	QWORD len  = BASS_StreamGetFilePosition(hCurrent, BASS_FILEPOS_END);
	lBitrate   = (long) (len / (125*time)+0.5);

	// Cosmetic: Cache file type
	BASS_CHANNELINFO cinf;
	BASS_ChannelGetInfo(hCurrent, &cinf);
	if (false) {
	} else if (cinf.ctype == BASS_CTYPE_STREAM_MP3) {		tFormat = _T("MP3");
	} else if (cinf.ctype == BASS_CTYPE_STREAM_AAC) {		tFormat = _T("AAC");
	} else if (cinf.ctype == BASS_CTYPE_STREAM_MP4) {		tFormat = _T("MP4");
	} else if (cinf.ctype == BASS_CTYPE_STREAM_FLAC) {		tFormat = _T("FLAC");
	} else if (cinf.ctype == BASS_CTYPE_STREAM_FLAC_OGG) {	tFormat = _T("FLAC");
	} else if (cinf.ctype == BASS_CTYPE_STREAM_OGG) {		tFormat = _T("VORBIS");	
	} else if (cinf.ctype == BASS_CTYPE_STREAM_ALAC) {		tFormat = _T("ALAC");	
	} else if (cinf.ctype == BASS_CTYPE_STREAM_AC3) {		tFormat = _T("AC3");
	} else if (cinf.ctype |= BASS_CTYPE_STREAM_WAV) {		tFormat = _T("WAV");
	} else {
		tFormat = _T("UNKNOWN");
	}

	// Cosmetic: Update display with bitrate + file type
	repopulateStatusBar();
	
}

void LMainWindow::playNext() {	
	if (qIndexes.size()) {

		// Work through queue
		int newIdx = qIndexes.front();
		qIndexes.pop_front();
		formatQueuedListItems();
		listItemData* d = static_cast<listItemData*>(lbMain->getItemData(newIdx));
		d->m_bIsQueued = false;
		formatListItem(newIdx, d);
		playFile(newIdx);

	} else {

		// No items in queue, look at playback mode
		int count = lbMain->getItemCount();
		switch (currentMode) {
			case MODE_REPEATLIST: {
				int newIdx = (curListIndex+1) % count;
				playFile(newIdx);
			} break;
			case MODE_REPEATFILE: {
				playFile(curListIndex);
			} break;
			case MODE_PLAYLIST: {
				if (curListIndex == lbMain->getItemCount()) {
					BASS_ChannelStop(hCurrent);
				} else {
					playFile(curListIndex + 1);
				}
			} break;
			case MODE_PLAYFILE: {
				BASS_ChannelStop(hCurrent);
			} break;
			case MODE_SHUFFLE: {
				int newIdx = rand() % count;			
				playFile(newIdx);
			} break;
			default: {
				// Unknown mode!
				// Stop the music and put the system into a known state
				BASS_ChannelStop(hCurrent);
				setShuffleMode(MODE_REPEATLIST);
			} break;
		}
	}
}

void LMainWindow::setShuffleMode(int newMode) {
	currentMode = newMode;
	mnuOptRepeatList->setChecked(false);
	mnuOptShuffle->setChecked(false);
	switch (currentMode) {
		case MODE_REPEATLIST: {
			tCurrentMode = _T(""); // Repeat list: Intentionally blank
			mnuOptRepeatList->setChecked(true);
		} break;
		case MODE_REPEATFILE: {
			tCurrentMode = _T("Repeat file");
			mnuOptRepeatFile->setChecked(true);
		} break;
		case MODE_PLAYLIST: {
			tCurrentMode = _T("Play list");
			mnuOptPlayList->setChecked(true);
		} break;
		case MODE_PLAYFILE: {
			tCurrentMode = _T("Play file");
			mnuOptPlayFile->setChecked(true);
		} break;
		case MODE_SHUFFLE: {
			tCurrentMode = _T("Shuffle");
			mnuOptShuffle->setChecked(true);
		} break;
		default: {
			tCurrentMode = _T("Unknown mode");
		} break;
	}
	repopulateStatusBar();
}

void LMainWindow::setDisplayMode(int newMode) {
	// Updating the display mode changes the text, but not the colour of any item.
	displayMode = newMode;

	mnuViewFilenames->setChecked(false);
	mnuViewFullpath->setChecked(false);
	switch(newMode) {
		case DISPLAY_FULLPATH: {
			mnuViewFullpath->setChecked(true);			
		} break;
		case DISPLAY_NAMEONLY: {			
			mnuViewFilenames->setChecked(true);
		} break;
	}

	lbMain->setVisible(false);
	for (int i=0, e = lbMain->getItemCount(); i < e; i++) {
		listItemData* d = static_cast<listItemData*>(lbMain->getItemData(i));
		formatListItem(i, d);
	}
	lbMain->setVisible(true);
}

void LMainWindow::formatListItem(int idx, listItemData* d) {	
	lbMain->setItemText(idx, (TCHAR*) d->format(displayMode).c_str());
}

void LMainWindow::formatQueuedListItem(int idx, listItemData* d, int queue_pos) {
	TSTRING strItemName = format(_T("%d > %s"), queue_pos, d->format(displayMode).c_str() );
	lbMain->setItemText(idx, (TCHAR*) strItemName.c_str());
}

void LMainWindow::formatListItemColour(int idx, listItemData* d) {
	if (d->m_bIsQueued) {
		lbMain->setItemColor(idx, 0xFF, 0x00, 0x00); // Red
	} else if (d->m_bAlreadyPlayed) {
		lbMain->setItemColor(idx, 0x80, 0x80, 0x80); // Grey
	} else {
		lbMain->setItemColor(idx, 0x00, 0x00, 0x00); // Black
	}
}

void LMainWindow::formatQueuedListItems() {
	TSTRING strItemName;
	int j = 1;
	for(
		std::deque<int>::iterator i = qIndexes.begin(), e = qIndexes.end();
		i != e; i++, j++
	) {
		listItemData* q_item_data = static_cast<listItemData*>(lbMain->getItemData(*i));
		formatQueuedListItem(*i, q_item_data, j);
		formatListItemColour(*i, q_item_data);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// COSMETIC FUNCTIONS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void LMainWindow::repopulateStatusBar() {	
	TSTRING tsStatus;
	if (hCurrent) {
		if (BASS_ChannelIsActive(hCurrent) == BASS_ACTIVE_PAUSED) {
			tsStatus = format(
				_T("%d Files        %s        Paused"),
				lbMain->getItemCount(),
				tCurrentMode
			);
		} else {
			tsStatus = format(
				_T("%d Files        %s        %s %d kbps"),
				lbMain->getItemCount(),
				tCurrentMode,
				tFormat, lBitrate
			);
		}
	} else {
		tsStatus = format(
			_T("%d Files        %s"),
			lbMain->getItemCount(),
			tCurrentMode
		);
	}
	
	this->setStatusBar(tsStatus.c_str());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EVENT CALLBACKS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void LMainWindow::OnBASSTimer() {
	// Volume
	float volume = BASS_GetVolume();
	TSTRING tsVolume = format(_T("%d%%"), (int)(volume * 100));
	llVolume->setText((TCHAR*) tsVolume.c_str() );
	//pbVolume->setValue(volume); //?? FIXME

	// Level sliders
	DWORD curLevel = BASS_ChannelGetLevel(hCurrent);
	pbLevelLeft ->setValue( LOWORD(curLevel) / 32768.0f );
	pbLevelRight->setValue( HIWORD(curLevel) / 32768.0f );

	// Play state
	if (hCurrent == 0) {
		llProgress->setText(_T("Stopped"));
		pbProgress->setValue(0);		
	} else {
		QWORD pos = BASS_ChannelGetPosition(hCurrent, BASS_POS_BYTE);
		long len_t = (long)BASS_ChannelBytes2Seconds(hCurrent, qwChannelLength);
		long pos_t = (long)BASS_ChannelBytes2Seconds(hCurrent, pos);

		TSTRING tsProgress = format(
			_T("%d:%02d / %d:%02d / -%d:%02d"),
			pos_t / 60, pos_t % 60,
			len_t / 60, len_t % 60,
			(len_t-pos_t)/60, (len_t-pos_t)%60
		);
		llProgress->setText((TCHAR*) tsProgress.c_str() );
		pbProgress->setValue((float)pos / (float)qwChannelLength);
	}
}

void LMainWindow::OnListDoubleClick() {
	int newIdx = lbMain->getSelected();
	if (newIdx != -1) playFile(newIdx);
}

void LMainWindow::OnProgressDragged() {
	if (! hCurrent) return;

	float xMouseCur = (float) LOWORD(pbProgress->onDrag.data.asLong);
	float PBWidth   = (float) pbProgress->getInnerWidth();
	float posFraction = xMouseCur / PBWidth;
	BASS_ChannelSetPosition(hCurrent, (QWORD)(posFraction*qwChannelLength), BASS_POS_BYTE);
	pbProgress->setValue(posFraction);
}

void LMainWindow::OnProgressMDown() {
	pbProgress->onDrag.data = pbProgress->onMouseDown.data;
	OnProgressDragged();
}

void LMainWindow::OnWindowResize() {
	int h = this->getInnerHeight();
	int w = this->getInnerWidth();

	pbProgress->resize(2, 0,  w - 24, 16);
	llProgress->resize(4, 18, w - 64, 16);

	//pbVolume->resize(w - 20, 0,  18, 34);
	pbLevelLeft->resize(w - 20, 0,  9, 34);
	pbLevelRight->resize(w - 11, 0,  9, 34);
	llVolume->resize(w - 64, 18, 40, 16);

	lbMain->resize(0, 36, w, h - 36);
}

void LMainWindow::OnHotkeyFocusWindow() {
	this->focus();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MENU COMMANDS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void LMainWindow::OnFileOpenFolder() {
	TSTRING response;
	if (!this->dlgFolderSelect(_T("Select a folder to open."), response)) return;
	lbMain->clear();
	addFolder(response);	
}

void LMainWindow::OnFileAddFolder() {
	TSTRING response;
	if (!this->dlgFolderSelect(_T("Select a folder to open."), response)) return;
	addFolder(response);
}

void LMainWindow::OnFileAddFiles() {
	// TODO
}

void LMainWindow::OnFileExit() {
	this->close();
}

void LMainWindow::OnToolsSelectCurrent() {
	if (hCurrent) {
		lbMain->setSelected(curListIndex);
		lbMain->focus();
	}
}

void LMainWindow::OnPlayPlay() {
	int selIdx = lbMain->getSelected();
	if (selIdx == -1) {
		playNext();
	} else {
		playFile(selIdx);
	}
}

void LMainWindow::OnPlayPause() {
	long status = BASS_ChannelIsActive(hCurrent);
	switch (status) {
		case BASS_ACTIVE_STOPPED: {
			// Either audio stopped, or hCurrent is null/invalid. "Super Unpause"
			playNext();
		} break;
		case BASS_ACTIVE_PAUSED: {
			BASS_ChannelPlay(hCurrent, false);
		} break;
		default: {
			BASS_ChannelPause(hCurrent);
		} break;
	}
	repopulateStatusBar();
}

void LMainWindow::OnPlayQueue() {
	int sel_idx = lbMain->getSelected();

	// Check if it already exists in the queue..
	bool bFound = false;
	std::deque<int>::iterator i = qIndexes.begin(), e = qIndexes.end();
	for (; i != e; i++) {
		if (sel_idx == *i) {
			bFound = true;
			break;
		}
	}
	
	// ..and if so, remove it
	listItemData* item_data = static_cast<listItemData*>(lbMain->getItemData(sel_idx));
	if (bFound) {		
		item_data->m_bIsQueued = false;
		formatListItem(*i, item_data);
		formatListItemColour(*i, item_data);
		qIndexes.erase(i);
	} else {		
		item_data->m_bIsQueued = true;
		qIndexes.push_back(sel_idx);
	}

	// Redraw queued items in list
	formatQueuedListItems();

	// Select the following item (for queue slides)
	int max_idx = lbMain->getItemCount() - 1;
	lbMain->setSelected(sel_idx >= max_idx ? sel_idx : sel_idx + 1);
}

void LMainWindow::OnPlayStop() {
	long status = BASS_ChannelIsActive(hCurrent);
	switch (status) {
		case BASS_ACTIVE_PLAYING: {
			BASS_ChannelPause(hCurrent);
		} break;
		default: {
			BASS_ChannelStop(hCurrent);
		} break;
	}
	repopulateStatusBar();
}

void LMainWindow::OnViewFilenames() {
	setDisplayMode(DISPLAY_NAMEONLY);
}

void LMainWindow::OnViewFullpath() {
	setDisplayMode(DISPLAY_FULLPATH);	
}

void LMainWindow::OnViewToggle() {
	setDisplayMode((displayMode + 1) % DISPLAY_COUNT_MODES);
}

void LMainWindow::OnOptionsSettings() {
	if (! wndSettings) wndSettings = new LSettingsWindow();
	if (! wndSettings->isActive()) {
		delete wndSettings;
		wndSettings = new LSettingsWindow();
	} else {
		wndSettings->setVisible(true);
	}
}

void LMainWindow::OnOptionsRepeatList() {
	setShuffleMode(MODE_REPEATLIST);
}

void LMainWindow::OnOptionsRepeatFile() {
	setShuffleMode(MODE_REPEATFILE);
}

void LMainWindow::OnOptionsPlayList() {
	setShuffleMode(MODE_PLAYLIST);
}

void LMainWindow::OnOptionsPlayFile() {
	setShuffleMode(MODE_PLAYFILE);
}

void LMainWindow::OnOptionsShuffle() {
	setShuffleMode(MODE_SHUFFLE);
}

void LMainWindow::OnOptionsToggle() {
	setShuffleMode((currentMode + 1) % MODE_COUNT_MODES);
}

void LMainWindow::OnHelpAbout() {
	TSTRING msg =
		_T("Lilly is a fast no nonsense music player.\n") \
		_T("Learn the shortcuts and go!\n\n") \
		_T("Build date: ") + getLinkTime_WS()
	;
	this->dlgAlert((TCHAR*) msg.c_str());
}