/*
   Copyright (c) 2000 Stefan Schimanski <1Stein@gmx.de>

   This program 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.

   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.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   aint with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <unistd.h>

#include <noatun/effects.h>
#include <noatun/app.h>
#include <noatun/player.h>
#include <noatun/pref.h>
#include "userinterface.h"
#include "pref.h"

#include <tqcursor.h>
#include <tqpixmap.h>
#include <tqbitmap.h>
#include <tqimage.h>
#include <tqdropsite.h>
#include <tqdragobject.h>
#include <tqtimer.h>

#include <tdefiledialog.h>
#include <kdebug.h>
#include <tdeapplication.h>
#include <twin.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <tdeconfig.h>
#include <tdepopupmenu.h>
#include <tdestandarddirs.h>
#include <tdemessagebox.h>
#include <tdeio/netaccess.h>
#include <kurldrag.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>

Kaiman* Kaiman::kaiman=0;
const char Kaiman::DEFAULT_SKIN[]="car-preset";

Kaiman::Kaiman()
	: TDEMainWindow(0, "NoatunKaiman"), UserInterface()
{
	NOATUNPLUGINC(Kaiman);
	kaiman=this;

	//setCaption( i18n("Kaiman") );
	KWin::setType( this->winId(), NET::Override );
	setBackgroundMode( NoBackground );
	setAcceptDrops(true);

	_style = 0;
	_seeking = false;
	_altSkin = false;

	// init config
	TDEConfig *config=TDEGlobal::config();
	config->setGroup("Kaiman");

	// load skin
	TQString skinName = config->readEntry( "SkinResource", DEFAULT_SKIN );

	if ( !changeStyle(skinName, "skindata") )
	{
		KMessageBox::sorry( this, i18n("Cannot load skin %1. Switching to default skin.").arg(skinName) );
		if ( !changeStyle( DEFAULT_SKIN, "skindata" ) )
		{
			KMessageBox::error( this, i18n("Cannot load default skin %1.").arg(DEFAULT_SKIN) );
			TQTimer::singleShot( 0, this, TQ_SLOT(close()) );
			return;
		}
	}

	// global connects
	connect( napp, TQ_SIGNAL(hideYourself()), this, TQ_SLOT(hide()) );
	connect( napp, TQ_SIGNAL(showYourself()), this, TQ_SLOT(show()) );

	connect( napp->player(), TQ_SIGNAL(playing()), this, TQ_SLOT(updateMode()));
	connect( napp->player(), TQ_SIGNAL(stopped()), this, TQ_SLOT(updateMode()));
	connect( napp->player(), TQ_SIGNAL(paused()), this, TQ_SLOT(updateMode()));
	connect( napp->player(), TQ_SIGNAL(timeout()), this, TQ_SLOT(timeout()));
	connect( napp->player(), TQ_SIGNAL(loopTypeChange(int)), this, TQ_SLOT(loopTypeChange(int)));
	connect( napp->player(), TQ_SIGNAL(newSongLen(int,int)), this, TQ_SLOT(newSongLen(int,int)));
	connect( napp->player(), TQ_SIGNAL(newSong()), this, TQ_SLOT(newSong()));

	if( napp->player()->isPlaying() )
		newSong();

	new KaimanPrefDlg(this);

	show();
}


Kaiman::~Kaiman()
{
}


bool Kaiman::changeStyle( const TQString &style, const TQString &desc )
{
	TQString ldesc = desc;
	if ( ldesc.isEmpty() )
	ldesc = _altSkin ? "alt_skindata" : "skindata";

	bool vis = isVisible();
	if ( vis )
		hide();

	bool ret = loadStyle( style, ldesc );

	newSongLen(0,0);
	timeout();
	loopTypeChange(0);
	updateMode();

	if ( vis )
		show();
	return ret;
}


bool Kaiman::loadStyle( const TQString &style, const TQString &desc )
{
	if ( _style ) delete _style;
	_style = new KaimanStyle(this);
	if ( !_style->loadStyle( style, desc ) )
	{
		delete _style;
		_style = 0;
		return false;
	}

	if ( _style->Mask() != 0 )
	{
		// Set the shaped window form
		XShapeCombineMask( tqt_xdisplay(), winId(), ShapeBounding, 0,0,
		                   _style->Mask()->handle(), ShapeSet );
	}

	KaimanStyleElement* item = _style->find("Background");
	setBackgroundMode(TQWidget::NoBackground);
	if ( item!=0 )
	{
		_style->resize( item->width(), item->height());
		resize( item->width(), item->height());
		setFixedSize( item->width(), item->height());
	}

	item = _style->find("Playlist_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(toggleListView()) );

	item = _style->find("Play_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(playpause()) );

	item = _style->find("Pause_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(playpause()) );

	item = _style->find("Stop_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(stop()) );

	item = _style->find("Next_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(forward()) );

	item = _style->find("Prev_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp->player(), TQ_SLOT(back()) );

	item = _style->find("Exit_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(close()) );

	item = _style->find("Mixer_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(execMixer()) );

	item = _style->find("Iconify_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(showMinimized()) );

	item = _style->find("Alt_Skin_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(toggleSkin()) );

	item = _style->find("Repeat_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(toggleLoop()) );

	item = _style->find("Shuffle_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this, TQ_SLOT(toggleShuffle()) );

	item = _style->find("Config_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), napp, TQ_SLOT(preferences()) );

	item = _style->find("Volume_Up_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this,TQ_SLOT(volumeUp()));

	item = _style->find("Volume_Down_Button");
	if( item!=0 ) connect( item, TQ_SIGNAL(clicked()), this,TQ_SLOT(volumeDown()));

	KaimanStyleSlider* slider =
		static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
	if( slider!=0 )
	{
		connect( slider, TQ_SIGNAL(newValueDrag(int)), this, TQ_SLOT(seekStart(int)) );
		connect( slider, TQ_SIGNAL(newValue(int)), this, TQ_SLOT(seekDrag(int)) );
		connect( slider, TQ_SIGNAL(newValueDrop(int)), this, TQ_SLOT(seekStop(int)) );
		slider->setValue( 0, 0, 1000 );
	}

	slider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
	if ( slider!=0 )
	{
		connect(slider, TQ_SIGNAL(newValue(int)), this, TQ_SLOT(setVolume(int)));
		slider->setValue( napp->player()->volume(), 0, 100 );
	}

	KaimanStyleValue* volItem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
	if ( volItem )
		volItem->setValue( napp->player()->volume(), 0, 100 );

	KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
	if ( titleItem )
		titleItem->startAnimation( 300 );

	return true;
}


void Kaiman::closeEvent(TQCloseEvent*)
{
	unload();
}


void Kaiman::dragEnterEvent( TQDragEnterEvent *event )
{
	event->accept( KURLDrag::canDecode(event) );
}


void Kaiman::dropEvent( TQDropEvent *event )
{
	doDropEvent(event);
}


void Kaiman::doDropEvent(TQDropEvent *event)
{
	KURL::List uri;
	if (KURLDrag::decode(event, uri))
	{
		for (KURL::List::Iterator i = uri.begin(); i != uri.end(); ++i)
			napp->player()->openFile(*i, false);
	}
}


void Kaiman::seekStart( int )
{
	_seeking = true;
}


void Kaiman::seekDrag( int value )
{
	int length = napp->player()->getLength()/1000;
	if ( length < 0)
		length = 0;

	if ( !_style ) return;

	KaimanStyleValue* posItem =
		static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
	if ( posItem )
		posItem->setValue( value, 0, length );

	KaimanStyleSlider* posSlider =
		static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
	if ( posSlider )
		posSlider->setValue( value, 0, length );

	// update time

	KaimanStyleNumber* numItem =
		static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
	if ( numItem )
		numItem->setValue( value/60%60 );

	numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
	if ( numItem )
		numItem->setValue( value % 60 );
}


void Kaiman::seekStop( int value )
{
	seek( value );
	_seeking = false;
}


void Kaiman::seek( int value )
{
	napp->player()->skipTo( value*1000 ); // skipTo() takes milliseconds
}


void Kaiman::toggleSkin()
{
	_altSkin = !_altSkin;

	TQString skinName = _style->skinName();

	TQString oldDesc, newDesc;
	if ( _altSkin )
	{
		oldDesc = TQString::fromLatin1("skindata");
		newDesc = TQString::fromLatin1("alt_skindata");
	}
	else
	{
		newDesc = TQString::fromLatin1("skindata");
		oldDesc = TQString::fromLatin1("alt_skindata");
	}

	if ( !changeStyle(skinName, newDesc) )
		changeStyle(skinName, oldDesc);
}


void Kaiman::setVolume( int vol )
{
	if ( vol<0 ) vol=0;
	if ( vol>=100 ) vol=100;

	napp->player()->setVolume( vol );
}


void Kaiman::volumeUp()
{
	setVolume( napp->player()->volume()+10 );
}


void Kaiman::volumeDown()
{
	setVolume( napp->player()->volume()-10 );
}


void Kaiman::execMixer()
{
	tdeApp->startServiceByDesktopName ( TQString::fromLatin1("kmix"), TQString() );
}


void Kaiman::timeout()
{
	if ( !_style ) return;

	if (!napp->player()->current())
		return;

	// update volume
	KaimanStyleSlider* l_elem_volslider = static_cast<KaimanStyleSlider*>(_style->find("Volume_Slider"));
	KaimanStyleValue* l_elem_volitem = static_cast<KaimanStyleValue*>(_style->find("Volume_Item"));
	if ( l_elem_volslider!=0 )
		l_elem_volslider->setValue( napp->player()->volume(), 0, 100 );
	if ( l_elem_volitem!=0 )
		l_elem_volitem->setValue( napp->player()->volume(), 0, 100 );

	// update position
	if ( !_seeking )
	{
		int sec = napp->player()->getTime()/1000;
		if ( sec < 0 )
			sec = 0;

		KaimanStyleValue* posItem =
			static_cast<KaimanStyleValue*>(_style->find("Position_Item"));
		if ( posItem ) posItem->setValue( sec, 0, napp->player()->getLength()/1000 );

		KaimanStyleSlider* posSlider =
			static_cast<KaimanStyleSlider*>(_style->find("Position_Slider"));
		if ( posSlider ) posSlider->setValue( sec, 0, napp->player()->getLength()/1000 );

		// update time
		KaimanStyleNumber* numItem =
			static_cast<KaimanStyleNumber*>(_style->find("Minute_Number"));
		if ( numItem )
			numItem->setValue( sec/60%60 );

		numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Number"));
		if ( numItem )
			numItem->setValue( sec%60 );
	}

	const PlaylistItem &item = napp->playlist()->current();
	KaimanStyleNumber* numItem = 0;

	numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Rate_Number"));
	if ( numItem )
		numItem->setValue(item.property("bitrate").toInt());

	TQString hzString = item.property("samplerate");
	hzString.truncate(2);

	numItem = static_cast<KaimanStyleNumber*>(_style->find("In_Hz_Number"));
	if ( numItem )
		numItem->setValue(hzString.toInt());
}


void Kaiman::updateMode()
{
	if ( !_style ) return;

	KaimanStyleButton* pause =
		static_cast<KaimanStyleButton*>(_style->find("Pause_Button"));
	KaimanStyleButton* play =
		static_cast<KaimanStyleButton*>(_style->find("Play_Button"));
	KaimanStyleState* status =
		static_cast<KaimanStyleState*>(_style->find("Status_Item"));

	if (napp->player()->isStopped() )
	{
		if ( pause ) pause->setLit( false );
		if ( play ) play->setLit( false );
		if ( status ) status->setValue( 0 );
	}
	else if ( napp->player()->isPlaying() )
	{
		if ( pause ) pause->setLit( false );
		if ( play ) play->setLit( true );
		if ( status ) status->setValue( 2 );
	}
	else if ( napp->player()->isPaused() )
	{
		if ( pause ) pause->setLit( true );
		if ( play ) play->setLit( false );
		if ( status ) status->setValue( 1 );
	}
}


void Kaiman::loopTypeChange( int )
{
	if ( !_style ) return;

	KaimanStyleState* rep = static_cast<KaimanStyleState*>(_style->find("Repeat_Item"));
	if ( rep )
		rep->setValue( napp->player()->loopStyle() );
}


void Kaiman::newSongLen( int, int )
{
	if ( !_style )
		return;

	int len = napp->player()->getLength()/1000; // convert milliseconds -> seconds
	if ( len < 0 ) // getLength returns -1 if there's no Playobject
		len = 0;

	// update time
	KaimanStyleNumber* numItem =
		static_cast<KaimanStyleNumber*>(_style->find("Minute_Total_Number"));
	if ( numItem )
		numItem->setValue( len/60%60 );

	numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Total_Number"));
	if ( numItem )
		numItem->setValue( len%60 );

	numItem = static_cast<KaimanStyleNumber*>(_style->find("Song_Minute_Number"));
	if ( numItem )
		numItem->setValue( len/60%60 );

	numItem = static_cast<KaimanStyleNumber*>(_style->find("Second_Minute_Number"));
	if ( numItem )
		numItem->setValue( len%60 );

	newSong();
}


void Kaiman::newSong()
{
	if ( !_style ) return;

	KaimanStyleText* titleItem = static_cast<KaimanStyleText*>(_style->find("Title"));
	if ( titleItem )
	{
		TQString title = i18n("Noatun");
		if ( napp->player()->current() )
		{
			title = napp->player()->current().title();
			if ( title.isEmpty() )
				title = napp->player()->current().file();

			title = i18n("TITLE (LENGTH)", "%1 (%2)").arg(title,
				napp->player()->current().lengthString());
		}
		titleItem->setValue( title );
	}
}


#undef None
void Kaiman::toggleLoop()
{
	TDEPopupMenu *loopMenu = new TDEPopupMenu(this, "loopMenu");
	int selectedItem = 0;

	loopMenu->setCheckable(true);
	loopMenu->insertTitle(i18n("Loop Style"));
	loopMenu->insertItem(i18n("&None"),
		(int)Player::None);
	loopMenu->insertItem(i18n("&Song"),
		(int)Player::Song);
	loopMenu->insertItem(i18n("&Playlist"),
	 (int)(Player::Playlist));
	loopMenu->insertItem(i18n("&Random"),
	(int)(Player::Random));

	loopMenu->setItemChecked((int)napp->player()->loopStyle(), true); // select current loopstyle in menu
	selectedItem = loopMenu->exec(TQCursor::pos());
	if (selectedItem != -1)
		napp->player()->loop(selectedItem); // set new loopstyle

	delete loopMenu;
}


void Kaiman::toggleShuffle()
{
	napp->player()->loop(Player::Random); // set loopstyle to be random
}

#include "userinterface.moc"
