/*  INDI STD
    Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)

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

    2004-01-18: This class handles INDI Standard properties.
 */

 #include "indistd.h"
 #include "Options.h"
 #include "indielement.h"
 #include "indiproperty.h"
 #include "indigroup.h"
 #include "indidevice.h"
 #include "indidriver.h"
 #include "kstars.h"
 #include "kstarsdata.h"
 #include "skymap.h"
 #include "skyobject.h"
 #include "simclock.h"
 #include "devicemanager.h"
 #include "timedialog.h"
 #include "streamwg.h"
 #include "ccdpreviewwg.h"
 #include "fitsviewer.h"

 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <ctype.h>
 #include <zlib.h>
 #include <stdlib.h>

 #include <tqtimer.h>
 #include <tqlabel.h>
 #include <tqfont.h>
 #include <tqeventloop.h>
 #include <tqsocketnotifier.h>

 #include <tdelocale.h>
 #include <kdebug.h>
 #include <kpushbutton.h>
 #include <klineedit.h>
 #include <kstatusbar.h>
 #include <tdemessagebox.h>
 #include <tdeapplication.h>
 #include <kprogress.h>
 #include <kurl.h>
 #include <kdirlister.h>
 #include <tdeaction.h>


 #define STD_BUFFER_SIZ		1024000
 #define FRAME_ILEN		1024

 INDIStdDevice::INDIStdDevice(INDI_D *associatedDevice, KStars * kswPtr)
 {

   dp			= associatedDevice;
   ksw  		= kswPtr;
   initDevCounter	= 0;
   setCount		= 0;
   batchMode 		= false;
   ISOMode   		= false;

   currentObject  	= NULL;
   streamWindow   	= new StreamWG(this, ksw);
   CCDPreviewWindow   	= new CCDPreviewWG(this, ksw);

   devTimer 		= new TQTimer(this);
   seqLister		= new KDirLister();

   telescopeSkyObject   = new SkyObject(0, 0, 0, 0, i18n("Telescope"));
   ksw->data()->appendTelescopeObject(telescopeSkyObject);

   connect( devTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(timerDone()) );
   connect( seqLister, TQ_SIGNAL(newItems (const KFileItemList & )), this, TQ_SLOT(checkSeqBoundary(const KFileItemList &)));

   downloadDialog = new KProgressDialog(NULL, 0, i18n("INDI"), i18n("Downloading Data..."));
   downloadDialog->cancel();

   parser		= newLilXML();
 }

 INDIStdDevice::~INDIStdDevice()
 {
   streamWindow->enableStream(false);
   streamWindow->close();
   CCDPreviewWindow->enableStream(false);
   CCDPreviewWindow->close();
   streamDisabled();
   delete (seqLister);
 }

void INDIStdDevice::handleBLOB(unsigned char *buffer, int bufferSize, TQString dataFormat)
{

  if (dataFormat == ".fits") dataType = DATA_FITS;
  else if (dataFormat == ".stream") dataType = DATA_STREAM;
  else if (dataFormat == ".ccdpreview") dataType = DATA_CCDPREVIEW;
  else dataType = DATA_OTHER;

  if (dataType == DATA_STREAM)
  {
    if (!streamWindow->processStream)
      return;

    streamWindow->show();
    streamWindow->streamFrame->newFrame( buffer, bufferSize, streamWindow->streamWidth, streamWindow->streamHeight);
  }
  else if (dataType == DATA_CCDPREVIEW)
  {
    if (!CCDPreviewWindow->processStream)
      return;
    CCDPreviewWindow->show();
    CCDPreviewWindow->streamFrame->newFrame( buffer, bufferSize, CCDPreviewWindow->streamWidth, CCDPreviewWindow->streamHeight);
  }
  else if (dataType == DATA_FITS || dataType == DATA_OTHER)
  {
    char filename[256];
    FILE *fitsTempFile;
    int fd, nr, n=0;
    TQString currentDir = Options::fitsSaveDirectory();

    streamWindow->close();

    if (dataType == DATA_FITS && !batchMode && Options::indiFITSDisplay())
    {
      strcpy(filename, "/tmp/fitsXXXXXX");
      if ((fd = mkstemp(filename)) < 0)
      {
	KMessageBox::error(NULL, "Error making temporary filename.");
	return;
      }
      close(fd);
    }
    else
    {
      char ts[32];
      struct tm *tp;
      time_t t;
      time (&t);
      tp = gmtime (&t);

      if (currentDir[currentDir.length() -1] == '/')
	currentDir.truncate(currentDir.length() - 1);

      strncpy(filename, currentDir.ascii(), currentDir.length());
      filename[currentDir.length()] = '\0';

      if (dataType == DATA_FITS)
      {
	char tempFileStr[256];
	strncpy(tempFileStr, filename, 256);

	if ( batchMode && !ISOMode)
	  snprintf(filename, sizeof(filename), "%s/%s_%02d.fits", tempFileStr, seqPrefix.ascii(), setCount);
	else if (!batchMode && !Options::indiFITSDisplay())
	{
	  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
	  snprintf(filename, sizeof(filename), "%s/file_%s.fits", tempFileStr, ts);
	}
	else
	{
	  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
	  snprintf(filename, sizeof(filename), "%s/%s_%02d_%s.fits", tempFileStr, seqPrefix.ascii(), setCount, ts);
	}

	setCount++;
      }
      else
      {
	strftime (ts, sizeof(ts), "/file-%Y-%m-%dT%H:%M:%S.", tp);
	strncat(filename, ts, sizeof(ts));
	strncat(filename, dataFormat.ascii(), 10);
      }
    }

    fitsTempFile = fopen(filename, "w");

    if (fitsTempFile == NULL) return;

    for (nr=0; nr < (int) bufferSize; nr += n)
      n = fwrite( ((unsigned char *) buffer) + nr, 1, bufferSize - nr, fitsTempFile);

    fclose(fitsTempFile);

       // We're done if we have DATA_OTHER
    if (dataType == DATA_OTHER)
    {
      ksw->statusBar()->changeItem( i18n("Data file saved to %1").arg(filename), 0);
      return;
    }
    else if (dataType == DATA_FITS && (batchMode || !Options::indiFITSDisplay()))
    {
      ksw->statusBar()->changeItem( i18n("FITS file saved to %1").arg(filename), 0);
      emit FITSReceived(dp->label);
      return;
    }

    KURL fileURL(filename);

    FITSViewer * fv = new FITSViewer(&fileURL, ksw);
    fv->fitsChange();
    fv->show();
  }

}

 /* Process standard Text and Number properties arrives from the driver */
 void INDIStdDevice::setTextValue(INDI_P *pp)
 {
   INDI_E *el;
   int wd, ht, bpp, bo, mu;
   long mgd;
   double fwhm;
   int d, m, y, min, sec, hour;
   ExtDate indiDate;
   TQTime indiTime;
   KStarsDateTime indiDateTime;

  switch (pp->stdID)
  {

    case TIME:
      if ( Options::indiAutoTime() )
       handleDevCounter();

       // Update KStars time once we receive update from INDI
       el = pp->findElement("UTC");
       if (!el) return;

       sscanf(el->text.ascii(), "%d%*[^0-9]%d%*[^0-9]%dT%d%*[^0-9]%d%*[^0-9]%d", &y, &m, &d, &hour, &min, &sec);
       indiDate.setYMD(y, m, d);
       indiTime.setHMS(hour, min, sec);
       indiDateTime.setDate(indiDate);
       indiDateTime.setTime(indiTime);

       ksw->data()->changeDateTime(indiDateTime);
       ksw->data()->syncLST();

      break;

    case SDTIME:
      if ( Options::indiAutoTime())
       handleDevCounter();
      break;

    case GEOGRAPHIC_COORD:
      if ( Options::indiAutoGeo() )
       handleDevCounter();
      break;

    case CCD_EXPOSE_DURATION:
       if (pp->state == PS_IDLE || pp->state == PS_OK)
         pp->set_w->setText(i18n("Start"));
       break;

    case CCD_FRAME:
         el = pp->findElement("WIDTH");
	 if (!el) return;
	 wd = (int) el->value;
	 el = pp->findElement("HEIGHT");
	 if (!el) return;
	 ht = (int) el->value;

	 streamWindow->setSize(wd, ht);
	 //streamWindow->allocateStreamBuffer();
	 break;
    case CCDPREVIEW_CTRL:
         el = pp->findElement("WIDTH");
	 if (!el) return;
	 wd = (int) el->value;
	 el = pp->findElement("HEIGHT");
	 if (!el) return;
	 ht = (int) el->value;
	 el = pp->findElement("BYTEORDER");
	 if (!el) return;
	 bo = (int) el->value;
	 el = pp->findElement("BYTESPERPIXEL");
	 if (!el) return;
	 bpp = (int) el->value;
	 el = pp->findElement("MAXGOODDATA");
	 if (!el) return;
	 mgd = (long) el->value;
	 CCDPreviewWindow->setCtrl(wd, ht, bo ,bpp,mgd);

	 break;

   case CCD_INFO:
	 el = pp->findElement("CCD_FWHM_PIXEL");
	 if (!el) return;
	 fwhm = el->value;
	 el = pp->findElement("CCD_PIXEL_SIZE");
	 if (!el) return;
	 mu = (int) el->value;
	 CCDPreviewWindow->setCCDInfo(fwhm, mu);
	 break;

       case EQUATORIAL_COORD:
       case EQUATORIAL_EOD_COORD:
	if (!dp->isOn()) break;
	el = pp->findElement("RA");
	if (!el) return;
	telescopeSkyObject->setRA(el->value);
	el = pp->findElement("DEC");
	if (!el) return;
	telescopeSkyObject->setDec(el->value);
	telescopeSkyObject->EquatorialToHorizontal(ksw->LST(), ksw->geo()->lat());
	// Force immediate update of skymap if the focus object is our telescope.
	if (ksw->map()->focusObject() == telescopeSkyObject)
		ksw->map()->updateFocus();
	else
		ksw->map()->update();
	break;

	case HORIZONTAL_COORD:
        if (!dp->isOn()) break;
	el = pp->findElement("ALT");
	if (!el) return;
	telescopeSkyObject->setAlt(el->value);
	el = pp->findElement("AZ");
	if (!el) return;
	telescopeSkyObject->setAz(el->value);
	telescopeSkyObject->HorizontalToEquatorial(ksw->LST(), ksw->geo()->lat());
	// Force immediate update of skymap if the focus object is our telescope.
	if (ksw->map()->focusObject() == telescopeSkyObject)
	      ksw->map()->updateFocus();
	else
	      ksw->map()->update();
	break;

    default:
        break;

  }

 }

 void INDIStdDevice::setLabelState(INDI_P *pp)
 {
    INDI_E *lp;
    INDI_P *imgProp;
    TDEAction *tmpAction;
    INDIDriver *drivers = ksw->getINDIDriver();
    TQFont buttonFont;

    switch (pp->stdID)
    {
      case CONNECTION:
      lp = pp->findElement("CONNECT");
      if (!lp) return;

      if (lp->state == PS_ON)
      {
        initDeviceOptions();
	emit linkAccepted();

        imgProp = dp->findProp("CCD_EXPOSE_DURATION");
	if (imgProp)
	{
        	tmpAction = ksw->actionCollection()->action("capture_sequence");
  		if (!tmpAction)
	  		kdDebug() << "Warning: capture_sequence action not found" << endl;
  		else
	  		tmpAction->setEnabled(true);
        }
      }
      else
      {
        if (streamWindow)
	{
	  //sNotifier->disconnect();
	  //dp->parentMgr->sNotifier->disconnect();
	  streamWindow->enableStream(false);
	  streamWindow->close();

	    //close(streamFD);
	}

	if (ksw->map()->focusObject() == telescopeSkyObject)
	{
		ksw->map()->stopTracking();
		ksw->map()->setFocusObject(NULL);
	}

	drivers->updateMenuActions();
       	ksw->map()->forceUpdateNow();
        emit linkRejected();
      }
      break;

      case VIDEO_STREAM:
       lp = pp->findElement("ON");
       if (!lp) return;
       if (lp->state == PS_ON)
          streamWindow->enableStream(true);
       else
          streamWindow->enableStream(false);
       break;

       case CCDPREVIEW_STREAM:
       lp = pp->findElement("ON");
       if (!lp) return;
       if (lp->state == PS_ON)
          CCDPreviewWindow->enableStream(true);
       else
          CCDPreviewWindow->enableStream(false);
       break;

    default:
      break;
    }

 }

 void INDIStdDevice::streamDisabled()
 {
    INDI_P *pp;
    INDI_E *el;

    //pp = dp->findProp("CONNECTION");
    //if (!pp) return;
    //if (pp->state == PS_OFF) return;

    pp = dp->findProp("VIDEO_STREAM");
    if (!pp) return;

    el = pp->findElement("OFF");
    if (!el) return;

    if (el->state == PS_ON)
     return;

    // Turn stream off
    pp->newSwitch(1);

}

 void INDIStdDevice::updateSequencePrefix(TQString newPrefix)
 {
    seqPrefix = newPrefix;

    seqLister->setNameFilter(TQString("%1_*.fits").arg(seqPrefix));

    setCount = 0;

    if (ISOMode) return;

    seqLister->openURL(Options::fitsSaveDirectory());

    checkSeqBoundary(seqLister->items());

 }

 void INDIStdDevice::checkSeqBoundary(const KFileItemList & items)
 {
    int newFileIndex;
    TQString tempName;
    char *tempPrefix = new char[64];

    // No need to check when in ISO mode
    if (ISOMode)
     return;

    for ( KFileItemListIterator it( items ) ; it.current() ; ++it )
    {
       tempName = it.current()->name();

       // find the prefix first
       if (tempName.find(seqPrefix) == -1)
         continue;

       strncpy(tempPrefix, tempName.ascii(), 64);
       tempPrefix[63] = '\0';

       char * t = tempPrefix;

       // skip chars
       while (*t) { if (isdigit(*t)) break; t++; }
       //tempPrefix = t;

       newFileIndex = strtol(t, NULL, 10);

        if (newFileIndex >= setCount)
	  setCount = newFileIndex + 1;
   }

   delete [] (tempPrefix);

 }

 void INDIStdDevice::updateTime()
{
  INDI_P *pp;
  INDI_E *lp;

  pp = dp->findProp("TIME");
  if (!pp) return;

  lp = pp->findElement("UTC");

  if (!lp) return;

  TQTime newTime( ksw->data()->ut().time());
  ExtDate newDate( ksw->data()->ut().date());

  lp->write_w->setText(TQString("%1-%2-%3T%4:%5:%6").arg(newDate.year()).arg(newDate.month())
					.arg(newDate.day()).arg(newTime.hour())
					.arg(newTime.minute()).arg(newTime.second()));
  pp->newText();

  pp  = dp->findProp("SDTIME");
  if (!pp) return;
  lp = pp->findElement("LST");
  if (!lp) return;

  lp->write_w->setText(ksw->LST()->toHMSString());
  pp->newText();
}

void INDIStdDevice::updateLocation()
{
   INDI_P *pp;
   INDI_E * latEle, * longEle;
   GeoLocation *geo = ksw->geo();

   pp = dp->findProp("GEOGRAPHIC_COORD");
   if (!pp) return;

  dms tempLong (geo->lng()->degree(), geo->lng()->arcmin(), geo->lng()->arcsec());
  dms fullCir(360,0,0);

  if (tempLong.degree() < 0)
     tempLong.setD ( fullCir.Degrees() + tempLong.Degrees());

    latEle  = pp->findElement("LAT");
    if (!latEle) return;
    longEle = pp->findElement("LONG");
    if (!longEle) return;

    longEle->write_w->setText(TQString("%1:%2:%3").arg(tempLong.degree()).arg(tempLong.arcmin()).arg(tempLong.arcsec()));
    latEle->write_w->setText(TQString("%1:%2:%3").arg(geo->lat()->degree()).arg(geo->lat()->arcmin()).arg(geo->lat()->arcsec()));

    pp->newText();
}


 void INDIStdDevice::registerProperty(INDI_P *pp)
 {
   INDI_E * portEle;
   INDIDriver *drivers = ksw->getINDIDriver();
   TQString str;

   switch (pp->stdID)
   {
     case DEVICE_PORT:
     portEle = pp->findElement("PORT");
     if (!portEle) return;

     if (drivers)
     {
       for (unsigned int i=0; i < drivers->devices.size(); i++)
       {
         if (drivers->devices[i]->mgrID == dp->parentMgr->mgrID)
	 {
	        if (drivers->devices[i]->deviceType == KSTARS_TELESCOPE)
		{
     		   portEle->read_w->setText( Options::indiTelescopePort() );
     		   portEle->write_w->setText( Options::indiTelescopePort() );
     		   portEle->text = Options::indiTelescopePort();
		   break;
		}
		else if (drivers->devices[i]->deviceType == KSTARS_VIDEO)
		{
		   portEle->read_w->setText( Options::indiVideoPort() );
     		   portEle->write_w->setText( Options::indiVideoPort() );
     		   portEle->text = Options::indiVideoPort();
		   break;
		}
	}
      }
     }
     break;

   }

}

void INDIStdDevice::initDeviceOptions()
{

  INDI_P *prop;

  initDevCounter = 0;

  if ( Options::indiAutoTime() )
  {
  	prop = dp->findProp("TIME");
  	  if (prop)
	  {
            updateTime();
	    initDevCounter += 5;
	  }
  }

  if ( Options::indiAutoGeo() )
  {
  	prop = dp->findProp("GEOGRAPHIC_COORD");
   	if (prop)
	{
   		updateLocation();
         	initDevCounter += 2;
	}
  }

  if ( Options::indiMessages() )
    ksw->statusBar()->changeItem( i18n("%1 is online.").arg(dp->name), 0);

  ksw->map()->forceUpdateNow();
}

 void INDIStdDevice::handleDevCounter()
{

  if (initDevCounter <= 0)
   return;

  initDevCounter--;

  if ( initDevCounter == 0 && Options::indiMessages() )
    ksw->statusBar()->changeItem( i18n("%1 is online and ready.").arg(dp->name), 0);

}

bool INDIStdDevice::handleNonSidereal()
{
   if (!currentObject)
      return false;

    int trackIndex=0;
    INDI_E *nameEle;

    kdDebug() << "Object of type " << currentObject->typeName() << endl;
  //TODO Meade claims that the library access is available to
  // all telescopes, which is unture. Only classic meade support
  // that. They claim that library funcion will be available to all
  // in "later" firmware revisions for the autostar and GPS.
  // As a temprory solution, I'm going to explicity check for the
  // device name which ideally I'm not supposed to do since all properties
  // should be defined from the INDI driver, but since the INDI autostar
  // and gps define the library functions (based on Meade's future claims)
  // I will check the device name until Meade's respondes to my query.

  // Update: Solution
  // Only Meade Classic will offer an explicit SOLAR_SYSTEM property. If such a property exists
  // then we take advantage of it. Otherwise, we send RA/DEC to the telescope and start a timer
  // based on the object type. Objects with high proper motions will require faster updates.
  // handle Non Sideral is ONLY called when tracking an object, not slewing.
  INDI_P *prop = dp->findProp(TQString("SOLAR_SYSTEM"));
  INDI_P *setMode = dp->findProp(TQString("ON_COORD_SET"));

  // If the device support it
  if (prop && setMode)
  {
    for (unsigned int i=0; i < setMode->el.count(); i++)
      if (setMode->el.at(i)->name == "TRACK")
      { trackIndex = i; break; }

    kdDebug() << "Device supports SOLAR_SYSTEM property" << endl;

    for (unsigned int i=0; i < prop->el.count(); i++)
     if (currentObject->name().lower() == prop->el.at(i)->label.lower())
     {
       prop->newSwitch(i);
       setMode->newSwitch(trackIndex);

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

       return true;
     }
  }

   kdDebug() << "Device doesn't support SOLAR_SYSTEM property, issuing a timer" << endl;
   kdDebug() << "Starting timer for object of type " << currentObject->typeName() << endl;


   switch (currentObject->type())
   {
    // Planet/Moon
    case 2:
       kdDebug() << "Initiating pulse tracking for " << currentObject->name() << endl;
       devTimer->start(INDI_PULSE_TRACKING);
       break;
    // Comet/Asteroid
    case 9:
    case 10:
      kdDebug() << "Initiating pulse tracking for " << currentObject->name() << endl;
      devTimer->start(INDI_PULSE_TRACKING);
      break;
    default:
      break;
    }

   return false;
}


void INDIStdDevice::timerDone()
{
      INDI_P *prop;
      INDI_E *RAEle, *DecEle;
      INDI_E *el;
      bool useJ2000 = false;

       if (!dp->isOn())
       {
        devTimer->stop();
	return;
       }

       prop = dp->findProp("ON_COORD_SET");
       if (prop == NULL || !currentObject)
        return;

       el   = prop->findElement("TRACK");
       if (!el) return;

       if (el->state != PS_ON)
       {
        devTimer->stop();
        return;
       }

       prop = dp->findProp("EQUATORIAL_EOD_COORD");

       if (prop == NULL)
       {
         prop = dp->findProp("EQUATORIAL_COORD");
	 if (prop) useJ2000 = true;
       }

       if (prop == NULL || !currentObject)
        return;

       // wait until slew is done
       if (prop->state == PS_BUSY)
        return;

       kdDebug() << "Timer called, starting processing" << endl;

	SkyPoint sp(currentObject->ra(), currentObject->dec());

	kdDebug() << "RA: " << currentObject->ra()->toHMSString() << " - DEC: " << currentObject->dec()->toDMSString() << endl;
	kdDebug() << "Az: " << currentObject->az()->toHMSString() << " - Alt " << currentObject->alt()->toDMSString() << endl;

	if (useJ2000)
	{
		sp.set(currentObject->ra(), currentObject->dec());
        	sp.apparentCoord( ksw->data()->ut().djd() , (long double) J2000);
        }

       // We need to get from JNow (Skypoint) to J2000
       // The ra0() of a skyPoint is the same as its JNow ra() without this process

       // Use J2000 coordinate as required by INDI
       RAEle  = prop->findElement("RA");
       if (!RAEle) return;
       DecEle = prop->findElement("DEC");
       if (!DecEle) return;

       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()));
	prop->newText();

}

INDIStdProperty::INDIStdProperty(INDI_P *associatedProperty, KStars * kswPtr, INDIStdDevice *stdDevPtr)
{
   pp     = associatedProperty;
   ksw    = kswPtr;
   stdDev = stdDevPtr;
}

 INDIStdProperty::~INDIStdProperty()
 {

 }

 void INDIStdProperty::newText()
 {
   INDI_E *lp;
   INDIDriver *drivers = ksw->getINDIDriver();

   switch (pp->stdID)
   {
     /* Set expose duration button to 'cancel' when busy */
     case CCD_EXPOSE_DURATION:
       pp->set_w->setText(i18n("Cancel"));
       break;

      /* Save Port name in KStars options */
     case DEVICE_PORT:
        lp = pp->findElement("PORT");

        if (lp && drivers)
	{
          for (unsigned int i=0; i < drivers->devices.size(); i++)
          {
              if (drivers->devices[i]->mgrID == stdDev->dp->parentMgr->mgrID)
	      {
	        if (drivers->devices[i]->deviceType == KSTARS_TELESCOPE)
		{
		   Options::setIndiTelescopePort( lp->text );
		   kdDebug() << "Setting telescope port " << lp->text << endl;
		}
		else if (drivers->devices[i]->deviceType == KSTARS_VIDEO)
		{
		   Options::setIndiVideoPort( lp->text );
		   kdDebug() << "Setting video port " << lp->text << endl;
		}
		break;
	      }
	  }
        }

	break;
   }

 }

 bool INDIStdProperty::convertSwitch(int switchIndex, INDI_E *lp)
 {

  INDI_E *RAEle(NULL), *DecEle(NULL), *AzEle(NULL), *AltEle(NULL), *nameEle(NULL);
  INDI_P * prop;
  SkyPoint sp;
  int selectedCoord=0;				/* 0 for Equatorial, 1 for Horizontal */
  bool useJ2000 (false);

  switch (pp->stdID)
  {
    /* Handle Slew/Track/Sync */
    case ON_COORD_SET:
    	// #1 set current object to NULL
    	stdDev->currentObject = NULL;
   	// #2 Deactivate timer if present
   	if (stdDev->devTimer->isActive())
		 	stdDev->devTimer->stop();

   	prop = pp->pg->dp->findProp("EQUATORIAL_EOD_COORD");
       	if (prop == NULL)
	{
		  prop = pp->pg->dp->findProp("EQUATORIAL_COORD");
		  if (prop == NULL)
                  {
                    prop = pp->pg->dp->findProp("HORIZONTAL_COORD");
                    if (prop == NULL)
        		return false;
                    else
                        selectedCoord = 1;		/* Select horizontal */
                  }
		  else
		        useJ2000 = true;
	}

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

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

        stdDev->currentObject = ksw->map()->clickedObject();
  	// Track is similar to slew, except that for non-sidereal objects
	// it tracks the objects automatically via a timer.
   	if ((lp->name == "TRACK"))
       		if (stdDev->handleNonSidereal())
	        	return true;

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

       switch (selectedCoord)
       {
         case 0:
            if (stdDev->currentObject)
		sp.set (stdDev->currentObject->ra(), 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 (stdDev->currentObject)
         {
           sp.setAz(*stdDev->currentObject->az());
           sp.setAlt(*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;
       }

       pp->newSwitch(switchIndex);
       prop->newText();

	return true;
	break;

   /* Handle Abort */
   case ABORT_MOTION:
         kdDebug() << "Stopping timer." << endl;
	 stdDev->devTimer->stop();
 	 pp->newSwitch(switchIndex);
	 return true;
	 break;

   case MOVEMENT:
      pp->newSwitch(switchIndex);
      break;

   default:
         return false;
	 break;
   }

  return false;

 }

// Return true if the complete operation is done here, or false if the operation is to be completed in the properties newSwitch()
bool INDIStdProperty::newSwitch(int id, INDI_E* el)
{
  INDI_P *prop;
  el=el;

  switch (pp->stdID)
  {
    case CONNECTION:
      if (id == 1)
        stdDev->streamDisabled();
      else
      {
       prop = pp->pg->dp->findProp("DEVICE_PORT");
       if (prop)
       prop->newText();
      }
      break;
    case ABORT_MOTION:
    case PARK:
    case MOVEMENT:
       //TODO add text in the status bar "Slew aborted."
       stdDev->devTimer->stop();
       break;
    default:
       break;
   }

   return false;

}

void INDIStdProperty::newTime()
{
   INDI_E * timeEle;
   INDI_P *SDProp;

   timeEle = pp->findElement("UTC");
   if (!timeEle) return;

   TimeDialog timedialog ( ksw->data()->ut(), ksw );

	if ( timedialog.exec() == TQDialog::Accepted )
	{
		TQTime newTime( timedialog.selectedTime() );
		ExtDate newDate( timedialog.selectedDate() );

                timeEle->write_w->setText(TQString("%1-%2-%3T%4:%5:%6")
					.arg(newDate.year()).arg(newDate.month())
					.arg(newDate.day()).arg(newTime.hour())
					.arg(newTime.minute()).arg(newTime.second()));
	        pp->newText();
	}
	else return;

   SDProp  = pp->pg->dp->findProp("SDTIME");
   if (!SDProp) return;
   timeEle = SDProp->findElement("LST");
   if (!timeEle) return;

   timeEle->write_w->setText(ksw->LST()->toHMSString());
   SDProp->newText();
}

#include "indistd.moc"
