//**************************************************************************
//*                     This file is part of the                           *
//*                MMC - Mpxplay Multimedia Commander                      *
//*                   The source code of MMC is                            *
//*        (C) copyright 1998-2014 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: display drive select combo box and dir select buttons

//#define MPXPLAY_USE_DEBUGF 1
#define DISPQT_DEBUG_OUTPUT NULL //stdout
#define DISPQT_DEBUGOUT_KEYEVENT stdout

#include <QtWidgets>
#include <QBoxLayout>
#include <QPushButton>
#include <stdint.h>
#include "disp_qt.h"
#include "moc_editor.h"
#include "moc_driveline.h"
#include "moc_mainwindow.h"
#include "mpxplay.h"

#if defined(DISPQT_DRIVELINE_USE_QTDRIVES) && (QT_VERSION >= 0x050400)
#include <QStorageInfo>
#endif

extern "C" {
 extern struct mainvars mvps;
 extern keyconfig kb[];
}

static const char *driveline_button_texts[DISPQT_DRIVELINE_NB_BUTTONS][2] = {
	{"\\", "Root dir/list (ctrl-'\\')"},
	{"<",  "Prev parallel dir/list (ctrl-gray-'/')"},
	{">",  "Next parallel dir/list (ctrl-gray-'*')"},
	{"..", "Up dir/list (ctrl-'up')"}
};

EditorDriveline::EditorDriveline(MainWindow *main_window, QWidget *parent, unsigned int sidenum) : QWidget(parent)
{
	setFocusPolicy(Qt::NoFocus);
	QHBoxLayout *layout = new QHBoxLayout;
	this->driveline_layout = layout;
	layout->setMargin(0);
	layout->setSpacing(4);
	this->drive_space_label = new EditorDrivespaceInfo(main_window, this);
	this->drive_combobox = new DriveComboBox(main_window, this, sidenum);
	layout->addWidget(this->drive_combobox, 0, Qt::AlignLeft);
	layout->addWidget(this->drive_space_label, 1, Qt::AlignLeft);

	for(int i = 0; i < DISPQT_DRIVELINE_NB_BUTTONS; i++) {
		this->driveline_buttons[i] = new EditorlinePushbutton(QString::fromUtf8(driveline_button_texts[i][0]), this, sidenum, i, main_window);
		this->driveline_buttons[i]->setToolTip(QString::fromUtf8(driveline_button_texts[i][1]));
		layout->addWidget(this->driveline_buttons[i], 0, Qt::AlignRight);
	}

	this->setLayout(this->driveline_layout);
}

void EditorDriveline::driveline_config_style_apply(bool lucent_enable)
{
	this->drive_space_label->drivespace_style_apply(lucent_enable);
	for(int i = 0; i < DISPQT_DRIVELINE_NB_BUTTONS; i++)
		this->driveline_buttons[i]->button_config_style_apply(lucent_enable);
}

//--------------------------------------------------------------------------------------------

EditorDrivespaceInfo::EditorDrivespaceInfo(MainWindow *main_window, QWidget *parent) : QLabel(parent)
{
	this->setMinimumWidth(1);
	if(main_window->mainwin_is_translucent_enabled())
		this->drivespace_style_apply(true);
}

void EditorDrivespaceInfo::drivespace_style_apply(bool lucent_enable)
{
	this->mutex_spaceinfo.tryLock(DISPQT_MUTEX_TIMEOUT);
	if(lucent_enable)
		this->setStyleSheet("QLabel {color: white;}");
	else
		this->setStyleSheet(QString());
	this->mutex_spaceinfo.unlock();
}

void EditorDrivespaceInfo::drive_space_refresh(struct dispqt_playlisteditor_model_config_s *pmc, const QString drv_combo_line)
{
	this->mutex_spaceinfo.tryLock(DISPQT_MUTEX_TIMEOUT);
	mpxp_filesize_t free_space, total_space;
	if(pmc) {
		free_space = pmc->drive_free_space;
		total_space = pmc->drive_total_space;
		this->space_text = tr(" ");
	} else {
		this->space_text = drv_combo_line;
		QByteArray ascii_ba = this->space_text.toLatin1();
		char astr_drv[sizeof(PDS_DIRECTORY_ROOTDIR_STR)];
		pds_strncpy(astr_drv, ascii_ba.data(), sizeof(astr_drv));
		this->space_text = tr(" ");

		astr_drv[sizeof(PDS_DIRECTORY_ROOTDIR_STR) - 1] = 0;
		if(astr_drv[sizeof(PDS_DIRECTORY_ROOTDIR_STR) - 2] != PDS_DIRECTORY_SEPARATOR_CHAR)
			astr_drv[sizeof(PDS_DIRECTORY_ROOTDIR_STR) - 2] = PDS_DIRECTORY_SEPARATOR_CHAR;
		free_space = 0; total_space = 0;
		pds_drive_getspace(astr_drv, &free_space, &total_space);
	}
	if(total_space) {
		free_space /= (1024*1024*1024);
		total_space /= (1024*1024*1024);
		this->space_text += QString::number(free_space) + tr(" GB of ") + QString::number(total_space) + tr(" GB free");
	}
	this->setText(this->space_text);
	this->mutex_spaceinfo.unlock();
}

//----------------------------------------------------------------------------------------------

EditorlinePushbutton::EditorlinePushbutton(const QString &text_str, QWidget *parent, unsigned int sidenum, unsigned int button_id, MainWindow *main_window) : QPushButton(text_str, parent)
{
	this->side_num = sidenum;
	this->button_id = button_id;
	//this->setIcon(QIcon(":/images/player_play.png"));
	//setFlat(true);
	this->setFixedSize(22,22);
	setFocusPolicy(Qt::NoFocus);
	if(main_window->mainwin_is_translucent_enabled())
		this->button_config_style_apply(true);
	connect(this, SIGNAL(signal_style_apply(bool)), SLOT(button_config_style_apply(bool)));
	connect(this, SIGNAL(pressed()), SLOT(click()));
}

void EditorlinePushbutton::button_config_style_apply(bool lucent_enable)
{
	if(lucent_enable) {
		this->setStyleSheet("QPushButton { font: bold; background: rgba( 255, 255, 255, 20% ); color:white; border-style: solid; border-width: 1px;border-color: rgb(220,220,220,40%); border-radius: 3px}"
		"QPushButton:hover { color:white; background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
		"stop: 0 rgb(255,255,255,54%), stop: 0.28 rgb(255,255,255,15%), stop: 0.41 rgb(255,255,255,15%), stop: 0.411 rgb(255,255,255,8%), stop: 0.9 rgb(255,255,255,8%), stop: 1 rgb(255,255,255,6%));"
		"border-style: solid; border-width: 1px; border-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgb(250,250,250,70%), stop: 1 rgb(250,250,250,60%)); border-radius: 3px}"
		);
	} else
		this->setStyleSheet(QString());
}

static void editorlinepushbutton_click_cb(void *passdata) // called from Mpxplay's main thread timer
{
	struct dispqt_callbackdata_s *dci = (struct dispqt_callbackdata_s *)passdata;
	struct mainvars *mvp;

	if(!dci)
		return;

	mvp = &mvps;
	struct playlist_side_info *psi = playlist_editlist_tabp_get(mvp, dci->sidenum);
	if(!psi || !(psi->editsidetype & PLT_ENABLED))
		goto err_out_eventcb;

	switch(dci->linenum) {
		case 0:
			if((psi->editsidetype & PLT_DIRECTORY) && !psi->sublistlevel)  // '\'
		    	playlist_change_sublist_or_directory(psi, DFT_ROOTDIR);
			else if(!(psi->editloadtype & PLL_JUKEBOX))
				playlist_change_sublist_or_directory(psi, DFT_ROOTLIST);
			break;
		case 1:
			if((psi->editsidetype & PLT_DIRECTORY) && !psi->sublistlevel) // '<'
				playlist_loaddir_search_paralell_dir(psi, -1);
			else
				playlist_loadsub_search_paralell_list(psi, -1);
			break;
		case 2:
			if((psi->editsidetype & PLT_DIRECTORY) && !psi->sublistlevel) // '>'
				playlist_loaddir_search_paralell_dir(psi, +1);
			else
				playlist_loadsub_search_paralell_list(psi, +1);
			break;
		case 3:
			if((psi->editsidetype & PLT_DIRECTORY) && !psi->sublistlevel)  // '..'
				playlist_change_sublist_or_directory(psi, DFT_UPDIR);
			else
				playlist_change_sublist_or_directory(psi, DFT_UPLIST);
			break;
	}

err_out_eventcb:
	delete dci;
}

void EditorlinePushbutton::click()
{
	struct dispqt_callbackdata_s *dci = new struct dispqt_callbackdata_s;
	dci->sidenum = this->side_num;
	dci->linenum = this->button_id;
	mpxplay_dispqt_mainthread_callback_init((void *)editorlinepushbutton_click_cb, (void *)dci, 0);
}

//----------------------------------------------------------------------------------------------
// DriveComboBox class

DriveComboBox::DriveComboBox(MainWindow *main_window, QWidget *parent, unsigned int sidenum) : QComboBox(parent)
{
	this->main_window = main_window;
	this->parent_widget = parent;
	this->side_num = sidenum;
	this->selected_line = 0;
	this->setFocusPolicy(Qt::NoFocus);
	this->setFixedSize(100,20);
	this->setMaxVisibleItems(15);
	connect(this,SIGNAL(activated(int)),this,SLOT(handle_Activated(int)));
	connect(this,SIGNAL(signal_show_popup()),this,SLOT(handle_show_popup()));
}

void DriveComboBox::call_drivespace_refresh(struct dispqt_playlisteditor_model_config_s *pmc)
{
	EditorDrivespaceInfo *edi = ((EditorDriveline *)this->parent_widget)->drive_space_label;
	if(edi)
		edi->drive_space_refresh(pmc, this->currentText());
}

void DriveComboBox::drive_list_refresh(struct dispqt_playlisteditor_model_config_s *pmc)
{
	struct playlist_entry_info *pei;
	struct playlist_side_info psi;
	int i;

	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::drive_list_refresh BEGIN");

	if(!pmc || !pmc->nb_drives){
		memset((void *)&psi, 0, sizeof(psi));
		psi.editsidetype=PLT_DIRECTORY;
		psi.mvp = &mvps;

#ifdef DISPQT_DRIVELINE_USE_QTDRIVES
		playlist_loaddir_browser_get_drives(&psi, LOADDIR_FIRSTDRV_VIRTUAL, -1); // FIXME: direct call
#else
		playlist_loaddir_browser_get_drives(&psi, 0, -1); // FIXME: direct call
#endif
		if(!psi.firstentry){
			playlist_editlist_tab_clear(&psi);
			return;
		}
	}

	clear();
#ifdef DISPQT_DRIVELINE_USE_QTDRIVES
	QFileInfoList drive_list = QDir::drives();
	for (i = 0; i < drive_list.size(); ++i) {
		QFileInfo fileInfo = drive_list.at(i);
		QString tdrv = fileInfo.absolutePath();
		tdrv.truncate(2);
#if (QT_VERSION >= 0x050400)
		tdrv += tr(" ");
		tdrv += QStorageInfo::name(fileInfo.absolutePath());
#endif
		addItem(tdrv);
	}
#endif
	if(pmc && pmc->nb_drives){
		for(i = 0; i < pmc->nb_drives; i++)
			addItem(QString::fromLatin1(pmc->drivenames[i]));
	} else {
		for(pei = psi.firstentry; pei && pei <= psi.lastentry; pei++) {
			QString dname = QString::fromLatin1(pei->filename);
			if(dname.size() > 2) {
				addItem(dname);
			} else {
				QString vname = QString::fromLatin1(pei->id3info[I3I_DFT_STORE]);
				addItem(dname + " " + vname);
			}
		}
		playlist_editlist_tab_clear(&psi);
	}
	addItem("8:  Jukebox");
	addItem("9:  Playlist");

	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::drive_list_refresh END");
}

bool DriveComboBox::drive_list_select_by_tabpath(struct dispqt_playlisteditor_model_config_s *pmc, QString tab_path)
{
	bool retval = false;
	char d;

	if(pmc && (!(pmc->editsidetype & PLT_DIRECTORY) || (pmc->editloadtype & PLL_JUKEBOX)))
		d = (pmc->editloadtype & PLL_JUKEBOX)? '8' : '9';
	else if(tab_path.size() > 0)
		d = tab_path.at(0).toUpper().toLatin1();
	else
		return false;
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::drive_list_select_by_tabpath \"%c\" = %d pmc:%8.8X", d, (int)d, (unsigned int)pmc);
	if(!d)
		d = '9';

	this->mutex_drivecombo.tryLock(DISPQT_MUTEX_TIMEOUT);
	setUpdatesEnabled(false);

	drive_list_refresh(pmc);  // TODO: optimize number of callings

	int i, list_lines = this->count();
	for(i = 0; i < list_lines; i++) {
		if(itemText(i).at(0).toUpper().toLatin1() == d){
			this->setCurrentIndex(i);
			this->selected_line = i;
			call_drivespace_refresh(pmc);
			retval = true;
			break;
		}
	}

	setUpdatesEnabled(true);
	this->mutex_drivecombo.unlock();

	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::drive_list_select_by_tabpath END");

	return retval;
}

static void drivecombobox_selectevent_cb(void *passdata) // called from Mpxplay's main thread timer
{
	struct dispqt_callbackdata_s *dci = (struct dispqt_callbackdata_s *)passdata;
	if(!dci)
		return;
	struct playlist_side_info *psi = playlist_editlist_tabp_get(&mvps, dci->sidenum);
	if(psi && !funcbit_test(psi->editloadtype, PLL_LOCKTAB))
		playlist_loaddir_selectdrive_execute(psi, (unsigned char)dci->linenum);
	delete dci;
}

void DriveComboBox::handle_Activated(int index)
{
    mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::handle_Activated %d", index);
    this->releaseKeyboard();
	this->mutex_drivecombo.tryLock(DISPQT_MUTEX_TIMEOUT);
	this->selected_line = index;
	struct dispqt_callbackdata_s *dci = new struct dispqt_callbackdata_s;
	dci->sidenum = this->side_num;
	dci->linenum = (unsigned int)itemText(index).at(0).toLatin1(); // !!! gets the first char of drive list
	mpxplay_dispqt_mainthread_callback_init((void *)drivecombobox_selectevent_cb, (void *)dci, 0);
	call_drivespace_refresh(NULL);
	this->mutex_drivecombo.unlock();
	QComboBox::hidePopup();
}

void DriveComboBox::handle_show_popup(void)
{
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::handle_show_popup");
	this->showPopup();
}

void DriveComboBox::showPopup()
{
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::showPopup");
	PlaylistEditorTabs *pet;
	struct dispqt_playlisteditor_model_config_s *pmc = this->main_window->PlaylistEditor->tab_get_pmc(&pet, this->side_num, -1);
	if(pmc && funcbit_test(pmc->editloadtype, PLL_LOCKTAB)){
		emit pet->signal_tab_head_lockedfuncmsg();
		return;
	}
	drive_list_select_by_tabpath(NULL, this->currentText());
	this->grabKeyboard();
	QComboBox::showPopup();
}

void DriveComboBox::hidePopup()
{
	mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::hidePopup");
    this->releaseKeyboard();
    this->mutex_drivecombo.tryLock(DISPQT_MUTEX_TIMEOUT);
	call_drivespace_refresh(NULL);
	this->mutex_drivecombo.unlock();
	QComboBox::hidePopup();
}

void DriveComboBox::keyPressEvent(QKeyEvent *event)
{
	int keynum = event->key();
	mpxplay_debugf(DISPQT_DEBUGOUT_KEYEVENT,"DriveComboBox::keyPressEvent k:%8.8X %c", keynum, event->text().at(0).toUpper().toLatin1());
	if(keynum == Qt::Key_Escape){
		this->hidePopup();
	}else if((keynum == Qt::Key_Return) || (keynum == Qt::Key_Enter)){
		emit this->activated(this->selected_line);
	}else if(keynum == Qt::Key_Up){
	    if(this->selected_line > 0){
	        this->selected_line--;
	        this->setCurrentIndex(this->selected_line);
	        view()->setCurrentIndex(view()->model()->index(this->selected_line,0));
	    }
	}else if(keynum == Qt::Key_Down){
	    if(this->selected_line < (this->count() - 1)){
	        this->selected_line++;
	        this->setCurrentIndex(this->selected_line);
	        view()->setCurrentIndex(view()->model()->index(this->selected_line,0));
	    }
	} else if(!(event->modifiers() & (Qt::ControlModifier | Qt::AltModifier))){
		if(drive_list_select_by_tabpath(NULL, event->text()) == true){
		    mpxplay_debugf(DISPQT_DEBUG_OUTPUT,"DriveComboBox::keyPressEvent activated");
			emit this->activated(this->selected_line);
		}
	}
	event->accept();
}
