/***************************************************************************
                          detaildialog.cpp  -  description
                             -------------------
    begin                : Sun May 5 2002
    copyright            : (C) 2002 by Jason Harris and Jasem Mutlaq
    email                : kstars@30doradus.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <tqstring.h>
#include <tqlayout.h> //still needed for secondary dialogs
#include <tqlineedit.h>
#include <tqimage.h>
#include <tqregexp.h>

#include <tdeapplication.h>
#include <kstandarddirs.h>
#include <tdemessagebox.h>
#include <kactivelabel.h>
#include <kpushbutton.h>
#include <tdelistview.h>
#include <klineedit.h>

#include "detaildialog.h"
//UI headers
#include "details_data.h"
#include "details_position.h"
#include "details_links.h"
#include "details_database.h"
#include "details_log.h"

#include "kstars.h"
#include "kstarsdata.h"
#include "kstarsdatetime.h"
#include "geolocation.h"
#include "ksutils.h"
#include "skymap.h"
#include "skyobject.h"
#include "starobject.h"
#include "deepskyobject.h"
#include "ksplanetbase.h"
#include "ksmoon.h"
#include "thumbnailpicker.h"

#include "indielement.h"
#include "indiproperty.h"
#include "indidevice.h"
#include "indimenu.h"
#include "devicemanager.h"
#include "indistd.h"

LogEdit::LogEdit( TQWidget *parent, const char *name ) : KTextEdit( parent, name )
{
	setFrameStyle( TQFrame::StyledPanel );
	setFrameShadow( TQFrame::Plain );
	setLineWidth( 4 );
}

void LogEdit::focusOutEvent( TQFocusEvent *e ) {
	emit focusOut();
	TQWidget::focusOutEvent(e);
}

ClickLabel::ClickLabel( TQWidget *parent, const char *name ) : TQLabel( parent, name )
{}

DetailDialog::DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo,
		TQWidget *parent, const char *name ) :
		KDialogBase( KDialogBase::Tabbed, i18n( "Object Details" ), Close, Close, parent, name ) ,
		selectedObject(o), ksw((KStars*)parent), Data(0), Pos(0), Links(0), Adv(0), Log(0)
{
	//Modify color palette
	setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Base ) );
	setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Text ) );

	//Create thumbnail image
	Thumbnail = new TQPixmap( 200, 200 );

	createGeneralTab();
	createPositionTab( ut, geo );
	createLinksTab();
	createAdvancedTab();
	createLogTab();

	//Connections
	connect( Data->ObsListButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( addToObservingList() ) );
	connect( Data->CenterButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( centerMap() ) );
	connect( Data->ScopeButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( centerTelescope() ) );
	connect( Data->Image, TQ_SIGNAL( clicked() ), this, TQ_SLOT( updateThumbnail() ) );
}

void DetailDialog::createGeneralTab()
{
	TQFrame *DataTab = addPage(i18n("General"));
	Data = new DetailsDataUI( DataTab, "general_data_tab" );

	//Modify colors
	Data->Names->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Data->Names->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::HighlightedText ) );
	Data->DataFrame->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Data->Type->setPalette( palette() );
	Data->Constellation->setPalette( palette() );
	Data->Mag->setPalette( palette() );
	Data->Distance->setPalette( palette() );
	Data->AngSize->setPalette( palette() );
	Data->InLabel->setPalette( palette() );
	Data->MagLabel->setPalette( palette() );
	Data->DistanceLabel->setPalette( palette() );
	Data->AngSizeLabel->setPalette( palette() );

	//Show object thumbnail image
	showThumbnail();

	TQVBoxLayout *vlay = new TQVBoxLayout( DataTab, 0, 0 );
	vlay->addWidget( Data );

	//Fill in the data fields
	//Contents depend on type of object
	StarObject *s = 0L;
	DeepSkyObject *dso = 0L;
	KSPlanetBase *ps = 0L;
	TQString pname(""), oname("");

	switch ( selectedObject->type() ) {
	case 0: //stars
		s = (StarObject *)selectedObject;

		Data->Names->setText( s->longname() );
		Data->Type->setText( s->sptype() + " " + i18n("star") );
		Data->Mag->setText( i18n( "number in magnitudes", "%1 mag" ).arg(
				TDEGlobal::locale()->formatNumber( s->mag(), 1 ) ) );  //show to tenths place

		//distance
		if ( s->distance() > 2000. || s->distance() < 0. )  // parallax < 0.5 mas
			Data->Distance->setText( TQString(i18n("larger than 2000 parsecs", "> 2000 pc") ) );
		else if ( s->distance() > 50.0 ) //show to nearest integer
			Data->Distance->setText( i18n( "number in parsecs", "%1 pc" ).arg(
					TQString::number( int( s->distance() + 0.5 ) ) ) );
		else if ( s->distance() > 10.0 ) //show to tenths place
			Data->Distance->setText( i18n( "number in parsecs", "%1 pc" ).arg(
					TDEGlobal::locale()->formatNumber( s->distance(), 1 ) ) );
		else //show to hundredths place
			Data->Distance->setText( i18n( "number in parsecs", "%1 pc" ).arg(
					TDEGlobal::locale()->formatNumber( s->distance(), 2 ) ) );

		//Note multiplicity/variablility in angular size label
		Data->AngSizeLabel->setText( "" );
		Data->AngSize->setText( "" );
		Data->AngSizeLabel->setFont( Data->AngSize->font() );
		if ( s->isMultiple() && s->isVariable() ) {
			Data->AngSizeLabel->setText( i18n( "the star is a multiple star", "multiple" ) + "," );
			Data->AngSize->setText( i18n( "the star is a variable star", "variable" ) );
		} else if ( s->isMultiple() )
			Data->AngSizeLabel->setText( i18n( "the star is a multiple star", "multiple" ) );
		else if ( s->isVariable() )
			Data->AngSizeLabel->setText( i18n( "the star is a variable star", "variable" ) );

		break; //end of stars case

	case 9:  //asteroids [fall through to planets]
	case 10: //comets [fall through to planets]
	case 2:  //planets (including comets and asteroids)
		ps = (KSPlanetBase *)selectedObject;

		Data->Names->setText( ps->longname() );
		//Type is "G5 star" for Sun
		if ( ps->name() == "Sun" )
			Data->Type->setText( i18n("G5 star") );
		else
			Data->Type->setText( ps->typeName() );

		Data->Constellation->setText( ps->constellation( ksw->data()->csegmentList,
																	ksw->data()->cnameList ) );

		//Magnitude: The moon displays illumination fraction instead
		if ( selectedObject->name() == "Moon" ) {
			Data->MagLabel->setText( i18n("Illumination:") );
			Data->Mag->setText( TQString("%1 %").arg( int( ((KSMoon *)selectedObject)->illum()*100. ) ) );
		} else {
			Data->Mag->setText( i18n( "number in magnitudes", "%1 mag" ).arg(
					TDEGlobal::locale()->formatNumber( ps->mag(), 1 ) ) );  //show to tenths place
		}

		//Distance from Earth.  The moon requires a unit conversion
		if ( ps->name() == "Moon" ) {
			Data->Distance->setText( i18n("distance in kilometers", "%1 km").arg(
						TDEGlobal::locale()->formatNumber( ps->rearth()*AU_KM ) ) );
		} else {
			Data->Distance->setText( i18n("distance in Astronomical Units", "%1 AU").arg(
						TDEGlobal::locale()->formatNumber( ps->rearth() ) ) );
		}

		//Angular size; moon and sun in arcmin, others in arcsec
		if ( ps->angSize() ) {
			if ( ps->name() == "Sun" || ps->name() == "Moon" )
				Data->AngSize->setText( i18n("angular size in arcminutes", "%1 arcmin").arg(
							TDEGlobal::locale()->formatNumber( ps->angSize() ) ) );
			else
				Data->AngSize->setText( i18n("angular size in arcseconds", "%1 arcsec").arg(
							TDEGlobal::locale()->formatNumber( ps->angSize()*60.0 ) ) );
		} else {
			Data->AngSize->setText( "--" );
		}

		break; //end of planets/comets/asteroids case

	default: //deep-sky objects
		dso = (DeepSkyObject *)selectedObject;

		//Show all names recorded for the object
		if ( ! dso->longname().isEmpty() && dso->longname() != dso->name() ) {
			pname = dso->translatedLongName();
			oname = dso->translatedName();
		} else {
			pname = dso->translatedName();
		}

		if ( ! dso->translatedName2().isEmpty() ) {
			if ( oname.isEmpty() ) oname = dso->translatedName2();
			else oname += ", " + dso->translatedName2();
		}

		if ( dso->ugc() != 0 ) {
			if ( ! oname.isEmpty() ) oname += ", ";
			oname += "UGC " + TQString("%1").arg( dso->ugc() );
		}
		if ( dso->pgc() != 0 ) {
			if ( ! oname.isEmpty() ) oname += ", ";
			oname += "PGC " + TQString("%1").arg( dso->pgc() );
		}

		if ( ! oname.isEmpty() ) pname += ", " + oname;
		Data->Names->setText( pname );

		Data->Type->setText( dso->typeName() );

		if ( dso->mag() > 90.0 )
			Data->Mag->setText( "--" );
		else
			Data->Mag->setText( i18n( "number in magnitudes", "%1 mag" ).arg(
					TDEGlobal::locale()->formatNumber( dso->mag(), 1 ) ) );  //show to tenths place

		//No distances at this point...
		Data->Distance->setText( "--" );

		//Only show decimal place for small angular sizes
		if ( dso->a() > 10.0 )
			Data->AngSize->setText( i18n("angular size in arcminutes", "%1 arcmin").arg(
					int( dso->a() ) ) );
		else if ( dso->a() )
			Data->AngSize->setText( i18n("angular size in arcminutes", "%1 arcmin").arg(
					TDEGlobal::locale()->formatNumber( dso->a(), 1 ) ) );
		else
			Data->AngSize->setText( "--" );

		break;
	}

	//Common to all types:
	Data->Constellation->setText( selectedObject->constellation( ksw->data()->csegmentList,
						ksw->data()->cnameList ) );
}

void DetailDialog::createPositionTab( const KStarsDateTime &ut, GeoLocation *geo ) {
	TQFrame *PosTab = addPage( i18n("Position") );
	Pos = new DetailsPositionUI( PosTab, "position_tab" );

	//Modify colors
	Pos->CoordTitle->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Pos->CoordTitle->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::HighlightedText ) );
	Pos->CoordFrame->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Pos->RSTTitle->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Pos->RSTTitle->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::HighlightedText ) );
	Pos->RSTFrame->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Pos->RA->setPalette( palette() );
	Pos->Dec->setPalette( palette() );
	Pos->Az->setPalette( palette() );
	Pos->Alt->setPalette( palette() );
	Pos->HA->setPalette( palette() );
	Pos->Airmass->setPalette( palette() );
	Pos->TimeRise->setPalette( palette() );
	Pos->TimeTransit->setPalette( palette() );
	Pos->TimeSet->setPalette( palette() );
	Pos->AzRise->setPalette( palette() );
	Pos->AltTransit->setPalette( palette() );
	Pos->AzSet->setPalette( palette() );
	Pos->RALabel->setPalette( palette() );
	Pos->DecLabel->setPalette( palette() );
	Pos->AzLabel->setPalette( palette() );
	Pos->AltLabel->setPalette( palette() );
	Pos->HALabel->setPalette( palette() );
	Pos->AirmassLabel->setPalette( palette() );
	Pos->TimeRiseLabel->setPalette( palette() );
	Pos->TimeTransitLabel->setPalette( palette() );
	Pos->TimeSetLabel->setPalette( palette() );
	Pos->AzRiseLabel->setPalette( palette() );
	Pos->AltTransitLabel->setPalette( palette() );
	Pos->AzSetLabel->setPalette( palette() );

	TQVBoxLayout *vlay = new TQVBoxLayout( PosTab, 0, 0 );
	vlay->addWidget( Pos );

	//Coordinates Section:
	//Don't use TDELocale::formatNumber() for the epoch string,
	//because we don't want a thousands-place separator!
	TQString sEpoch = TQString::number( ut.epoch(), 'f', 1 );
	//Replace the decimal point with localized decimal symbol
	sEpoch.replace( ".", TDEGlobal::locale()->decimalSymbol() );

	Pos->RALabel->setText( i18n( "RA (%1):" ).arg( sEpoch ) );
	Pos->DecLabel->setText( i18n( "Dec (%1):" ).arg( sEpoch ) );
	Pos->RA->setText( selectedObject->ra()->toHMSString() );
	Pos->Dec->setText( selectedObject->dec()->toDMSString() );
	Pos->Az->setText( selectedObject->az()->toDMSString() );
	Pos->Alt->setText( selectedObject->alt()->toDMSString() );

	//Hour Angle can be negative, but dms HMS expressions cannot.
	//Here's a kludgy workaround:
	dms lst = geo->GSTtoLST( ut.gst() );
	dms ha( lst.Degrees() - selectedObject->ra()->Degrees() );
	TQChar sgn('+');
	if ( ha.Hours() > 12.0 ) {
		ha.setH( 24.0 - ha.Hours() );
		sgn = '-';
	}
	Pos->HA->setText( TQString("%1%2").arg(sgn).arg( ha.toHMSString() ) );

	//Airmass is approximated as the secant of the zenith distance,
	//equivalent to 1./sin(Alt).  Beware of Inf at Alt=0!
	if ( selectedObject->alt()->Degrees() > 0.0 )
		Pos->Airmass->setText( TDEGlobal::locale()->formatNumber(
				1./sin( selectedObject->alt()->radians() ), 2 ) );
	else
		Pos->Airmass->setText( "--" );

	//Rise/Set/Transit Section:

	//Prepare time/position variables
	TQTime rt = selectedObject->riseSetTime( ut, geo, true ); //true = use rise time
	dms raz = selectedObject->riseSetTimeAz( ut, geo, true ); //true = use rise time

	//If transit time is before rise time, use transit time for tomorrow
	TQTime tt = selectedObject->transitTime( ut, geo );
	dms talt = selectedObject->transitAltitude( ut, geo );
	if ( tt < rt ) {
		tt = selectedObject->transitTime( ut.addDays( 1 ), geo );
		talt = selectedObject->transitAltitude( ut.addDays( 1 ), geo );
	}

	//If set time is before rise time, use set time for tomorrow
	TQTime st = selectedObject->riseSetTime(  ut, geo, false ); //false = use set time
	dms saz = selectedObject->riseSetTimeAz( ut, geo, false ); //false = use set time
	if ( st < rt ) {
		st = selectedObject->riseSetTime( ut.addDays( 1 ), geo, false ); //false = use set time
		saz = selectedObject->riseSetTimeAz( ut.addDays( 1 ), geo, false ); //false = use set time
	}

	if ( rt.isValid() ) {
		Pos->TimeRise->setText( TQString().sprintf( "%02d:%02d", rt.hour(), rt.minute() ) );
		Pos->TimeSet->setText( TQString().sprintf( "%02d:%02d", st.hour(), st.minute() ) );
		Pos->AzRise->setText( raz.toDMSString() );
		Pos->AzSet->setText( saz.toDMSString() );
	} else {
		if ( selectedObject->alt()->Degrees() > 0.0 ) {
			Pos->TimeRise->setText( i18n( "Circumpolar" ) );
			Pos->TimeSet->setText( i18n( "Circumpolar" ) );
		} else {
			Pos->TimeRise->setText( i18n( "Never rises" ) );
			Pos->TimeSet->setText( i18n( "Never rises" ) );
		}

		Pos->AzRise->setText( i18n( "Not Applicable", "N/A" ) );
		Pos->AzSet->setText( i18n( "Not Applicable", "N/A" ) );
	}

	Pos->TimeTransit->setText( TQString().sprintf( "%02d:%02d", tt.hour(), tt.minute() ) );
	Pos->AltTransit->setText( talt.toDMSString() );
}

void DetailDialog::createLinksTab()
{
	// don't create a link tab for an unnamed star
	if (selectedObject->name() == TQString("star"))
		return;

	TQFrame *LinksTab = addPage( i18n( "Links" ) );
	Links = new DetailsLinksUI( LinksTab, "links_tab" );

	//Modify colors
	Links->InfoTitle->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Text ) );
	Links->InfoTitle->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Base ) );
	Links->ImagesTitle->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Text ) );
	Links->ImagesTitle->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Base ) );

	TQPalette p = palette();
	p.setColor( TQPalette::Active, TQColorGroup::Dark, palette().color( TQPalette::Active, TQColorGroup::Highlight ) );
	Links->InfoList->setPalette( p );
	Links->ImagesList->setPalette( p );

	TQVBoxLayout *vlay = new TQVBoxLayout( LinksTab, 0, 0 );
	vlay->addWidget( Links );

	TQStringList::Iterator itList = selectedObject->InfoList.begin();
	TQStringList::Iterator itTitle = selectedObject->InfoTitle.begin();
	TQStringList::Iterator itListEnd = selectedObject->InfoList.end();

	for ( ; itList != itListEnd; ++itList ) {
		Links->InfoList->insertItem(TQString(*itTitle));
		itTitle++;
	}

	Links->InfoList->setSelected(0, true);

	itList  = selectedObject->ImageList.begin();
	itTitle = selectedObject->ImageTitle.begin();
	itListEnd  = selectedObject->ImageList.end();

	for ( ; itList != itListEnd; ++itList ) {
		Links->ImagesList->insertItem(TQString(*itTitle));
		itTitle++;
	}

	if (! Links->InfoList->count() && ! Links->ImagesList->count()) {
		Links->EditLinkButton->setDisabled(true);
		Links->RemoveLinkButton->setDisabled(true);
	}

	// Signals/Slots
	connect( Links->ViewButton, TQ_SIGNAL(clicked()), this, TQ_SLOT( viewLink() ) );
	connect( Links->AddLinkButton, TQ_SIGNAL(clicked()), ksw->map(), TQ_SLOT( addLink() ) );
	connect( Links->EditLinkButton, TQ_SIGNAL(clicked()), this, TQ_SLOT( editLinkDialog() ) );
	connect( Links->RemoveLinkButton, TQ_SIGNAL(clicked()), this, TQ_SLOT( removeLinkDialog() ) );
	connect( Links->InfoList, TQ_SIGNAL(highlighted(int)), this, TQ_SLOT( unselectImagesList() ) );
	connect( Links->ImagesList, TQ_SIGNAL(highlighted(int)), this, TQ_SLOT( unselectInfoList() ) );
	connect( ksw->map(), TQ_SIGNAL(linkAdded()), this, TQ_SLOT( updateLists() ) );
}

void DetailDialog::createAdvancedTab()
{
	// Don't create an adv tab for an unnamed star or if advinterface file failed loading
	// We also don't need adv dialog for solar system objects.
   if (selectedObject->name() == TQString("star") ||
				ksw->data()->ADVtreeList.isEmpty() ||
				selectedObject->type() == SkyObject::PLANET ||
				selectedObject->type() == SkyObject::COMET ||
				selectedObject->type() == SkyObject::ASTEROID )
		return;

	TQFrame *AdvancedTab = addPage(i18n("Advanced"));
	Adv = new DetailsDatabaseUI( AdvancedTab, "database_tab" );
//	Adv->setPaletteBackgroundColor( TQColor( "white" ) );
	TQVBoxLayout *vlay = new TQVBoxLayout( AdvancedTab, 0, 0 );
	vlay->addWidget( Adv );

	treeIt = new TQPtrListIterator<ADVTreeData> (ksw->data()->ADVtreeList);
	connect( Adv->ADVTree, TQ_SIGNAL(doubleClicked(TQListViewItem*)), this, TQ_SLOT(viewADVData()));

	populateADVTree(NULL);
}

void DetailDialog::createLogTab()
{
	//Don't create a a log tab for an unnamed star
	if (selectedObject->name() == TQString("star"))
		return;

	// Log Tab
	TQFrame *LogTab = addPage(i18n("Log"));
	Log = new DetailsLogUI( LogTab, "log_tab" );

	//Modify colors
	Log->LogTitle->setPaletteBackgroundColor( palette().color( TQPalette::Active, TQColorGroup::Text ) );
	Log->LogTitle->setPaletteForegroundColor( palette().color( TQPalette::Active, TQColorGroup::Base ) );

	TQVBoxLayout *vlay = new TQVBoxLayout( LogTab, 0, 0 );
	vlay->addWidget( Log );

	if ( selectedObject->userLog.isEmpty() )
		Log->UserLog->setText(i18n("Record here observation logs and/or data on %1.").arg(selectedObject->translatedName()));
	else
		Log->UserLog->setText(selectedObject->userLog);

	//Automatically save the log contents when the widget loses focus
	connect( Log->UserLog, TQ_SIGNAL( focusOut() ), this, TQ_SLOT( saveLogData() ) );
}


void DetailDialog::unselectInfoList()
{
	Links->InfoList->setSelected( Links->InfoList->currentItem(), false );
}

void DetailDialog::unselectImagesList()
{
	Links->ImagesList->setSelected( Links->ImagesList->currentItem(), false );
}

void DetailDialog::viewLink()
{
	TQString URL;

	if ( Links->InfoList->currentItem() != -1 &&
			Links->InfoList->isSelected( Links->InfoList->currentItem() ) )
		URL = TQString( *selectedObject->InfoList.at( Links->InfoList->currentItem() ) );
	else if ( Links->ImagesList->currentItem() != -1 )
		URL = TQString( *selectedObject->ImageList.at( Links->ImagesList->currentItem() ) );

	if (!URL.isEmpty())
		kapp->invokeBrowser(URL);
}

void DetailDialog::updateLists()
{
	Links->InfoList->clear();
	Links->ImagesList->clear();

	TQStringList::Iterator itList = selectedObject->InfoList.begin();
	TQStringList::Iterator itTitle = selectedObject->InfoTitle.begin();
	TQStringList::Iterator itListEnd = selectedObject->InfoList.end();

	for ( ; itList != itListEnd; ++itList ) {
		Links->InfoList->insertItem(TQString(*itTitle));
		itTitle++;
	}

	Links->InfoList->setSelected(0, true);
	itList  = selectedObject->ImageList.begin();
	itTitle = selectedObject->ImageTitle.begin();
	itListEnd = selectedObject->ImageList.end();

	for ( ; itList != itListEnd; ++itList ) {
		Links->ImagesList->insertItem(TQString(*itTitle));
		itTitle++;
	}
}

void DetailDialog::editLinkDialog()
{
	int type;
	uint i;
	TQString defaultURL , entry;
	TQFile newFile;

	KDialogBase editDialog(KDialogBase::Plain, i18n("Edit Link"), Ok|Cancel, Ok , this, "editlink", false);
	TQFrame *editFrame = editDialog.plainPage();

	editLinkURL = new TQLabel(i18n("URL:"), editFrame);
	editLinkField = new TQLineEdit(editFrame, "lineedit");
	editLinkField->setMinimumWidth(300);
	editLinkField->home(false);
	editLinkLayout = new TQHBoxLayout(editFrame, 6, 6, "editlinklayout");
	editLinkLayout->addWidget(editLinkURL);
	editLinkLayout->addWidget(editLinkField);

	currentItemIndex = Links->InfoList->currentItem();

	if (currentItemIndex != -1 && Links->InfoList->isSelected(currentItemIndex))
	{
		defaultURL = *selectedObject->InfoList.at(currentItemIndex);
		editLinkField->setText(defaultURL);
		type = 1;
		currentItemTitle = Links->InfoList->currentText();
	}
	else if ( (currentItemIndex = Links->ImagesList->currentItem()) != -1)
	{
		defaultURL = *selectedObject->ImageList.at(currentItemIndex);
		editLinkField->setText(defaultURL);
		type = 0;
		currentItemTitle = Links->ImagesList->currentText();
	}
	else return;

	// If user presses cancel then return
	if (!editDialog.exec() == TQDialog::Accepted)
		return;
	// if it wasn't edit, don't do anything
	if (!editLinkField->edited())
		return;

	// Save the URL of the current item
	currentItemURL =  editLinkField->text();
	entry = selectedObject->name() + ":" + currentItemTitle + ":" + currentItemURL;

	//FIXME: usage of verifyUserData() is pretty unclear
	//verifyUserData() returns false if currentItemTitle/currentItemURL
	//are not found in the user's list already.  If they are, then that
	//item is removed.
	switch (type)
	{
		case 0:
			if (!verifyUserData(type))
				return;
				break;
		case 1:
			if (!verifyUserData(type))
				return;
				break;
	}

	// Open a new file with the same name and copy all data along with changes
	newFile.setName(file.name());
	newFile.open(IO_WriteOnly);

	TQTextStream newStream(&newFile);

	for (i=0; i<dataList.count(); i++)
	{
		newStream << dataList[i] << endl;
		continue;
	}

	if (type==0)
	{
		*selectedObject->ImageTitle.at(currentItemIndex) = currentItemTitle;
		*selectedObject->ImageList.at(currentItemIndex) = currentItemURL;
	}
	else
	{
		*selectedObject->InfoTitle.at(currentItemIndex) = currentItemTitle;
		*selectedObject->InfoList.at(currentItemIndex) = currentItemURL;
	}

	newStream << entry << endl;

	newFile.close();
	file.close();
	updateLists();
}

void DetailDialog::removeLinkDialog()
{
	int type;
	uint i;
	TQString defaultURL, entry;
	TQFile newFile;

	currentItemIndex = Links->InfoList->currentItem();

	if (currentItemIndex != -1 && Links->InfoList->isSelected(currentItemIndex))
	{
		defaultURL = *selectedObject->InfoList.at(currentItemIndex);
		type = 1;
		currentItemTitle = Links->InfoList->currentText();
	}
	else
	{
		currentItemIndex = Links->ImagesList->currentItem();
		defaultURL = *selectedObject->ImageList.at(currentItemIndex);
		type = 0;
		currentItemTitle = Links->ImagesList->currentText();
	}

	if (KMessageBox::warningContinueCancel( 0, i18n("Are you sure you want to remove the %1 link?").arg(currentItemTitle), i18n("Delete Confirmation"),KStdGuiItem::del())!=KMessageBox::Continue)
		return;

	switch (type)
	{
		case 0:
			if (!verifyUserData(type))
				return;
			selectedObject->ImageTitle.remove( selectedObject->ImageTitle.at(currentItemIndex));
			selectedObject->ImageList.remove( selectedObject->ImageList.at(currentItemIndex));
			break;

		case 1:
			if (!verifyUserData(type))
				return;
			selectedObject->InfoTitle.remove(selectedObject->InfoTitle.at(currentItemIndex));
			selectedObject->InfoList.remove(selectedObject->InfoList.at(currentItemIndex));
			break;
	}

	// Open a new file with the same name and copy all data along with changes
	newFile.setName(file.name());
	newFile.open(IO_WriteOnly);

	TQTextStream newStream(&newFile);

	for (i=0; i<dataList.count(); i++)
		newStream << dataList[i] << endl;

	newFile.close();
	file.close();
	updateLists();
}

bool DetailDialog::verifyUserData(int type)
{
	TQString line, name, sub, title;
	bool ObjectFound = false;
	uint i;

	switch (type)
	{
		case 0:
			if (!readUserFile(type))
				return false;
			for (i=0; i<dataList.count(); i++)
			{
				line = dataList[i];
				name = line.mid( 0, line.find(':') );
				sub = line.mid( line.find(':')+1 );
				title = sub.mid( 0, sub.find(':') );
				if (name == selectedObject->name() && title == currentItemTitle)
				{
					ObjectFound = true;
					dataList.remove(dataList.at(i));
					break;
				}
			}
			break;
		case 1:
			if (!readUserFile(type))
				return false;
			for (i=0; i<dataList.count(); i++)
			{
				line = dataList[i];
				name = line.mid( 0, line.find(':') );
				sub = line.mid( line.find(':')+1 );
				title = sub.mid( 0, sub.find(':') );
				if (name == selectedObject->name() && title == currentItemTitle)
				{
					ObjectFound = true;
					dataList.remove(dataList.at(i));
					break;
				}
			}
			break;
	}
	return ObjectFound;
}

bool DetailDialog::readUserFile(int type)//, int sourceFileType)
{
	switch (type)
	{
		case 0:
			file.setName( locateLocal( "appdata", "image_url.dat" ) ); //determine filename
			if ( !file.open( IO_ReadOnly) )
			{
				ksw->data()->initError("image_url.dat", false);
				return false;
			}
			break;

		case 1:
			file.setName( locateLocal( "appdata", "info_url.dat" ) );  //determine filename
			if ( !file.open( IO_ReadOnly) )
			{
				ksw->data()->initError("info_url.dat", false);
				return false;
			}
			break;
	}

	// Must reset file
	file.reset();
	TQTextStream stream(&file);

	dataList.clear();

	// read all data into memory
	while (!stream.eof())
		dataList.append(stream.readLine());

	return true;
}

void DetailDialog::populateADVTree(TQListViewItem *parent)
{
	// list done
	if (!treeIt->current())
		return;

	// if relative top level [KSLABEL]
	if (treeIt->current()->Type == 0)
		forkTree(parent);

	while (treeIt->current())
	{
		if (treeIt->current()->Type == 0)
		{
			forkTree(parent);
			continue;
		}
		else if (treeIt->current()->Type == 1)
			break;

		if (parent)
			new TQListViewItem( parent, treeIt->current()->Name);
		else
			new TQListViewItem( Adv->ADVTree, treeIt->current()->Name);

		++(*treeIt);
	}
}

void DetailDialog::forkTree(TQListViewItem *parent)
{
	TQListViewItem *current = 0;
	if (parent)
		current = new TQListViewItem(parent, treeIt->current()->Name);
	else
		current = new TQListViewItem(Adv->ADVTree, treeIt->current()->Name);

	// we need to increment the iterator before and after populating the tree
	++(*treeIt);
	populateADVTree(current);
	++(*treeIt);
}

void  DetailDialog::viewADVData()
{
	TQString link;
	TQListViewItem * current = Adv->ADVTree->currentItem();

	if (!current)  return;

	treeIt->toFirst();
	while (treeIt->current())
	{
		if (treeIt->current()->Name == current->text(0))
		{
			if (treeIt->current()->Type == 2)  break;
			else return;
		}
		++(*treeIt);
	}

	link = treeIt->current()->Link;
	link = parseADVData(link);
	kapp->invokeBrowser(link);
}

TQString DetailDialog::parseADVData(TQString link)
{
	TQString subLink;
	int index;

	if ( (index = link.find("KSOBJ")) != -1)
	{
		link.remove(index, 5);
		link = link.insert(index, selectedObject->name());
	}

	if ( (index = link.find("KSRA")) != -1)
	{
		link.remove(index, 4);
		subLink = TQString().sprintf("%02d%02d%02d", selectedObject->ra0()->hour(), selectedObject->ra0()->minute(), selectedObject->ra0()->second());
		subLink = subLink.insert(2, "%20");
		subLink = subLink.insert(7, "%20");

		link = link.insert(index, subLink);
	}
	if ( (index = link.find("KSDEC")) != -1)
	{
		link.remove(index, 5);
		if (selectedObject->dec()->degree() < 0)
		{
			subLink = TQString().sprintf("%03d%02d%02d", selectedObject->dec0()->degree(), selectedObject->dec0()->arcmin(), selectedObject->dec0()->arcsec());
			subLink = subLink.insert(3, "%20");
			subLink = subLink.insert(8, "%20");
		}
		else
		{
			subLink = TQString().sprintf("%02d%02d%02d", selectedObject->dec0()->degree(), selectedObject->dec0()->arcmin(), selectedObject->dec0()->arcsec());
			subLink = subLink.insert(0, "%2B");
			subLink = subLink.insert(5, "%20");
			subLink = subLink.insert(10, "%20");
		}
		link = link.insert(index, subLink);
	}

	return link;
}

void DetailDialog::saveLogData() {
  selectedObject->saveUserLog( Log->UserLog->text() );
}

void DetailDialog::addToObservingList() {
	ksw->observingList()->slotAddObject( selectedObject );
}

void DetailDialog::centerMap() {
	ksw->map()->setClickedObject( selectedObject );
	ksw->map()->slotCenter();
}

void DetailDialog::centerTelescope()
{

  INDI_D *indidev(NULL);
  INDI_P *prop(NULL), *onset(NULL);
  INDI_E *RAEle(NULL), *DecEle(NULL), *AzEle(NULL), *AltEle(NULL), *ConnectEle(NULL), *nameEle(NULL);
  bool useJ2000( false);
  int selectedCoord(0);
  SkyPoint sp;

  // Find the first device with EQUATORIAL_EOD_COORD or EQUATORIAL_COORD and with SLEW element
  // i.e. the first telescope we find!

  INDIMenu *imenu = ksw->getINDIMenu();


  for (unsigned int i=0; i < imenu->mgr.count() ; i++)
  {
    for (unsigned int j=0; j < imenu->mgr.at(i)->indi_dev.count(); j++)
    {
       indidev = imenu->mgr.at(i)->indi_dev.at(j);
       indidev->stdDev->currentObject = NULL;
       prop = indidev->findProp("EQUATORIAL_EOD_COORD");
       	if (prop == NULL)
	{
		  prop = indidev->findProp("EQUATORIAL_COORD");
		  if (prop == NULL)
                  {
                    prop = indidev->findProp("HORIZONTAL_COORD");
                    if (prop == NULL)
        		continue;
                    else
                        selectedCoord = 1;		/* Select horizontal */
                  }
		  else
		        useJ2000 = true;
	}

       ConnectEle = indidev->findElem("CONNECT");
       if (!ConnectEle) continue;

       if (ConnectEle->state == PS_OFF)
       {
	 KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.").arg(indidev->label));
	 return;
       }

        switch (selectedCoord)
        {
          // Equatorial
          case 0:
           if (prop->perm == PP_RO) continue;
           RAEle  = prop->findElement("RA");
       	   if (!RAEle) continue;
   	   DecEle = prop->findElement("DEC");
   	   if (!DecEle) continue;
           break;

         // Horizontal
         case 1:
          if (prop->perm == PP_RO) continue;
          AzEle = prop->findElement("AZ");
          if (!AzEle) continue;
          AltEle = prop->findElement("ALT");
          if (!AltEle) continue;
          break;
        }

        onset = indidev->findProp("ON_COORD_SET");
        if (!onset) continue;

        onset->activateSwitch("SLEW");

        indidev->stdDev->currentObject = selectedObject;

      /* Send object name if available */
      if (indidev->stdDev->currentObject)
         {
             nameEle = indidev->findElem("OBJECT_NAME");
             if (nameEle && nameEle->pp->perm != PP_RO)
             {
                 nameEle->write_w->setText(indidev->stdDev->currentObject->name());
                 nameEle->pp->newText();
             }
          }

       switch (selectedCoord)
       {
         case 0:
            if (indidev->stdDev->currentObject)
		sp.set (indidev->stdDev->currentObject->ra(), indidev->stdDev->currentObject->dec());
            else
                sp.set (ksw->map()->clickedPoint()->ra(), ksw->map()->clickedPoint()->dec());

      	 if (useJ2000)
	    sp.apparentCoord(ksw->data()->ut().djd(), (long double) J2000);

    	   RAEle->write_w->setText(TQString("%1:%2:%3").arg(sp.ra()->hour()).arg(sp.ra()->minute()).arg(sp.ra()->second()));
	   DecEle->write_w->setText(TQString("%1:%2:%3").arg(sp.dec()->degree()).arg(sp.dec()->arcmin()).arg(sp.dec()->arcsec()));

          break;

       case 1:
         if (indidev->stdDev->currentObject)
         {
           sp.setAz(*indidev->stdDev->currentObject->az());
           sp.setAlt(*indidev->stdDev->currentObject->alt());
         }
         else
         {
           sp.setAz(*ksw->map()->clickedPoint()->az());
           sp.setAlt(*ksw->map()->clickedPoint()->alt());
         }

          AzEle->write_w->setText(TQString("%1:%2:%3").arg(sp.az()->degree()).arg(sp.az()->arcmin()).arg(sp.az()->arcsec()));
          AltEle->write_w->setText(TQString("%1:%2:%3").arg(sp.alt()->degree()).arg(sp.alt()->arcmin()).arg(sp.alt()->arcsec()));

         break;
       }

       prop->newText();

       return;
    }
  }

  // We didn't find any telescopes
  KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));

}

void DetailDialog::showThumbnail() {
	//No image if object is a star
	if ( selectedObject->type() == SkyObject::STAR ||
			selectedObject->type() == SkyObject::CATALOG_STAR ) {
		Thumbnail->resize( Data->Image->width(), Data->Image->height() );
		Thumbnail->fill( Data->paletteBackgroundColor() );
		Data->Image->setPixmap( *Thumbnail );
		return;
	}

	//Try to load the object's image from disk
	//If no image found, load "no image" image
	//If that isn't found, make it blank.
	TQFile file;
	TQString fname = "thumb-" + selectedObject->name().lower().replace( TQRegExp(" "), "" ) + ".png";
	if ( KSUtils::openDataFile( file, fname ) ) {
		file.close();
		Thumbnail->load( file.name(), "PNG" );
	} else if ( KSUtils::openDataFile( file, "noimage.png" ) ) {
		file.close();
		Thumbnail->load( file.name(), "PNG" );
	} else {
		Thumbnail->resize( Data->Image->width(), Data->Image->height() );
		Thumbnail->fill( Data->paletteBackgroundColor() );
	}

	Data->Image->setPixmap( *Thumbnail );
}

void DetailDialog::updateThumbnail() {
	ThumbnailPicker tp( selectedObject, *Thumbnail, this, "thumbnaileditor" );

	if ( tp.exec() == TQDialog::Accepted ) {
		TQString fname = locateLocal( "appdata", "thumb-"
				+ selectedObject->name().lower().replace( TQRegExp(" "), "" ) + ".png" );

		Data->Image->setPixmap( *(tp.image()) );

		//If a real image was set, save it.
		//If the image was unset, delete the old image on disk.
		if ( tp.imageFound() ) {
			Data->Image->pixmap()->save( fname, "PNG" );
			*Thumbnail = *(Data->Image->pixmap());
		} else {
			TQFile f;
			f.setName( fname );
			f.remove();
		}
	}
}

#include "detaildialog.moc"
