/***************************************************************************
                          FITSViewer.cpp  -  A FITSViewer for KStars
                             -------------------
    begin                : Thu Jan 22 2004
    copyright            : (C) 2004 by Jasem Mutlaq
    email                : mutlaqja@ikarustech.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   Some code fragments were adapted from Peter Kirchgessner's FITS plugin*
 *   See http://members.aol.com/pkirchg for more details.                  *
 ***************************************************************************/


#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdefiledialog.h>
#include <tdeaction.h>
#include <tdeaccel.h>
#include <kdebug.h>
#include <tdetoolbar.h>
#include <tdeapplication.h>
#include <kpixmap.h> 
#include <tdetempfile.h>
#include <kimageeffect.h> 
#include <tdemenubar.h>
#include <kprogress.h>
#include <kstatusbar.h>
#include <kcommand.h>
#include <klineedit.h>
#include <tdelistview.h>

#include <tqfile.h>
#include <tqvbox.h>
#include <tqcursor.h>
#include <tqstringlist.h>
#include <tqlistview.h>
#include <tqradiobutton.h>
#include <tqclipboard.h>
#include <tqimage.h>

#include <math.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>

#include "fitsviewer.h"
#include "fitsimage.h"
#include "fitsprocess.h"
#include "fitshistogram.h"
#include "conbridlg.h"
#include "statform.h"
#include "imagereductiondlg.h"
#include "fitsheaderdialog.h"
#include "ksutils.h"
#include "Options.h"

extern int fits_ieee32_intel;
extern int fits_ieee32_motorola;
extern int fits_ieee64_intel;
extern int fits_ieee64_motorola;

#define FITS_GETBITPIX16(p,val) val = ((p[0] << 8) | (p[1]))
#define FITS_GETBITPIX32(p,val) val = \
          ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
	  
#define FITS_GETBITPIXM32(p,val) \
 { if (fits_ieee32_intel) {unsigned char uc[4]; \
   uc[0] = p[3]; uc[1] = p[2]; uc[2] = p[1]; uc[3] = p[0]; \
   val = *(FITS_BITPIXM32 *)uc; } \
   else if (fits_ieee32_motorola) { val = *(FITS_BITPIXM32 *)p; } \
   else if (fits_ieee64_motorola) {FITS_BITPIXM64 m64; \
   unsigned char *uc= (unsigned char *)&m64; \
   uc[0]=p[0]; uc[1]=p[1]; uc[2]=p[2]; uc[3]=p[3]; uc[4]=uc[5]=uc[6]=uc[7]=0; \
   val = (FITS_BITPIXM32)m64; } \
   else if (fits_ieee64_intel) {FITS_BITPIXM64 i64; \
   unsigned char *uc= (unsigned char *)&i64; \
   uc[0]=uc[1]=uc[2]=uc[3]=0; uc[7]=p[3]; uc[6]=p[2]; uc[5]=p[1]; uc[4]=p[0]; \
   val = (FITS_BITPIXM32)i64;}\
}
	  
#define FITS_GETBITPIXM64(p,val) \
 { if (fits_ieee64_intel) {unsigned char uc[8]; \
   uc[0] = p[7]; uc[1] = p[6]; uc[2] = p[5]; uc[3] = p[4]; \
   uc[4] = p[3]; uc[5] = p[2]; uc[6] = p[1]; uc[7] = p[0]; \
   val = *(FITS_BITPIXM64 *)uc; } else val = *(FITS_BITPIXM64 *)p; }	  

FITSViewer::FITSViewer (const KURL *url, TQWidget *parent, const char *name)
	: TDEMainWindow (parent, name)
{
    image      = NULL;
    currentURL = *url;
    imgBuffer  = NULL;
    histo      = NULL;
    Dirty      = 0;
    
    /* Initiliaze menu actions */
    history = new KCommandHistory(actionCollection());
    history->setUndoLimit(10);
    history->setRedoLimit(10);
    history->documentSaved();
    connect(history, TQ_SIGNAL(documentRestored()), this, TQ_SLOT(fitsRestore()));
    
    /* Setup image widget */    
    image = new FITSImage(this);
    setCentralWidget(image);
   
    statusBar()->insertItem("", 0);
    statusBar()->setItemFixed(0, 100);
    statusBar()->insertItem("", 1);
    statusBar()->setItemFixed(1, 100);
    statusBar()->insertItem("", 2);
    statusBar()->setItemFixed(2, 100);
    statusBar()->insertItem(i18n("Welcome to KStars FITS Viewer"), 3, 1, true);
    statusBar()->setItemAlignment(3 , TQt::AlignLeft);
    
    /* FITS initializations */
    if (!initFITS())
    {
     close();
     return;
    }
     
    TQFile tempFile;
    
    if (KSUtils::openDataFile( tempFile, "imgreduction.png" ) )
    {
    	new TDEAction( i18n("Image Reduction"), tempFile.name(), TDEShortcut( "Ctrl+R" ), this, TQ_SLOT( imageReduction()), actionCollection(), "image_reduce");
	tempFile.close();
    }
    else
    	new TDEAction( i18n("Image Reduction"), "blend", TDEShortcut( "Ctrl+R" ), this, TQ_SLOT( imageReduction()), actionCollection(), "image_reduce");
	
    /*if (KSUtils::openDataFile( tempFile, "bricon.png" ) )
    {
    	new TDEAction( i18n("Brightness/Contrast"), tempFile.name(), TDEShortcut( "Ctrl+T" ), this, TQ_SLOT( BrightContrastDlg()), actionCollection(), "image_brightness_contrast");
	tempFile.close();
    }
    else*/
       	new TDEAction( i18n("Brightness/Contrast"), "contrast+", TDEShortcut( "Ctrl+T" ), this, TQ_SLOT( BrightContrastDlg()), actionCollection(), "image_brightness_contrast");
	
    if (KSUtils::openDataFile( tempFile, "histogram.png" ) )
    {
    	new TDEAction ( i18n("Histogram"), tempFile.name(), TDEShortcut("Ctrl+H"), this, TQ_SLOT (imageHistogram()), actionCollection(), "image_histogram");
	tempFile.close();
    }
    else
        new TDEAction ( i18n("Histogram"), "wizard", TDEShortcut("Ctrl+H"), this, TQ_SLOT (imageHistogram()), actionCollection(), "image_histogram");
    
    KStdAction::open(this, TQ_SLOT(fileOpen()), actionCollection());   
    KStdAction::save(this, TQ_SLOT(fileSave()), actionCollection());
    KStdAction::saveAs(this, TQ_SLOT(fileSaveAs()), actionCollection());
    KStdAction::close(this, TQ_SLOT(slotClose()), actionCollection());
    KStdAction::copy(this, TQ_SLOT(fitsCOPY()), actionCollection());
    KStdAction::zoomIn(image, TQ_SLOT(fitsZoomIn()), actionCollection());
    KStdAction::zoomOut(image, TQ_SLOT(fitsZoomOut()), actionCollection());
    new TDEAction( i18n( "&Default Zoom" ), "viewmagfit.png", TDEShortcut( "Ctrl+D" ),
		image, TQ_SLOT(fitsZoomDefault()), actionCollection(), "zoom_default" );
    new TDEAction( i18n( "Statistics"), "sum", 0, this, TQ_SLOT(fitsStatistics()), actionCollection(), "image_stats");
    new TDEAction( i18n( "FITS Header"), "frame_spreadsheet.png", 0, this, TQ_SLOT(fitsHeader()), actionCollection(), "fits_editor");
    
   /* Create GUI */
   createGUI("fitsviewer.rc");
     
   /* initially resize in accord with KDE rules */
   resize(640, 480); 
}

FITSViewer::~FITSViewer()
{
   free(imgBuffer);
}

bool  FITSViewer::initFITS()
{

    free(imgBuffer);
    imgBuffer = NULL;
    image->clearMem();
    
  /* Load image into buffer */
    if ( (imgBuffer = loadData (currentURL.path().ascii(), imgBuffer)) == NULL)  { close(); return false; }
    /* Display image in the central widget */
    if (image->loadFits(currentURL.path().ascii()) == -1) { close(); return false; }

    /* Clear history */
    history->clear();
    
    /* Set new file caption */
    setCaption(currentURL.fileName());
    
    /* Get initial statistics */
    calculateStats();
    
    image->viewport()->resize(image->viewport()->width() + 5, image->viewport()->height());
    image->viewportResizeEvent(NULL);
    
    return true;

}

void FITSViewer::slotClose()
{

  if (Dirty)
  {
    
  TQString caption = i18n( "Save Changes to FITS?" );
		TQString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
		int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStdGuiItem::save(), KStdGuiItem::discard() );
		if ( ans == KMessageBox::Yes )
			fileSave();	
		else if ( ans == KMessageBox::No ) 
			fitsRestore();
   }
   
   if (Dirty == 0)
    close();
}

void FITSViewer::closeEvent(TQCloseEvent *ev)
{

  if (Dirty)
  {
    
  TQString caption = i18n( "Save Changes to FITS?" );
		TQString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
		int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStdGuiItem::save(), KStdGuiItem::discard() );
		if ( ans == KMessageBox::Yes )
			fileSave();	
		else if ( ans == KMessageBox::No ) 
			fitsRestore();
   }
   
   if (Dirty == 0)
    ev->accept();
   else 
    ev->ignore();

}

void FITSViewer::show_fits_errors()
{
  char *msg;
  /* Write out error messages of FITS-Library */
  while ((msg = fits_get_error ()) != NULL)
     KMessageBox::error(0, msg);
}

float * FITSViewer::loadData(const char *filename, float *buffer)
{
  FILE *fp;
  FITS_FILE *ifp;
  FITS_HDU_LIST *hdulist;
  unsigned char *tempData, *tempDataPtr;
  FITS_BITPIX16  pixval_16  =0;
  FITS_BITPIX32  pixval_32  =0;
  FITS_BITPIXM32 pixval_m32 =0;
  FITS_BITPIXM64 pixval_m64 =0;
  int totalCount;
  int width, height, bpp, bitpix;
 
 fp = fopen (filename, "rb");
 if (!fp)
 {
   KMessageBox::error(0, i18n("Cannot open file for reading"));
   return (NULL);
 }
 fclose (fp);

 ifp = fits_open (filename, "r");
 if (ifp == NULL)
 {
   KMessageBox::error(0, i18n("Error during open of FITS file"));
   return (NULL);
 }
 if (ifp->n_pic <= 0)
 {
   KMessageBox::error(0, i18n("FITS file keeps no displayable images"));
   fits_close (ifp);
   return (NULL);
 }
  
  // We only deal with 1 image in a FITS for now.
  hdulist = fits_seek_image (ifp, 1);
  if (hdulist == NULL) return (NULL);
  
  width  = hdulist->naxisn[0]; 
  height = hdulist->naxisn[1];
  
  totalCount = width * height;
  
  bpp    = hdulist->bpp;
  bitpix = hdulist->bitpix;
  
  buffer           = (float          *) malloc (height * width * sizeof(float)); 
  tempData         = (unsigned char  *) malloc (height * width * bpp * sizeof(unsigned char));
  if (buffer == NULL || tempData == NULL)
  {
    KMessageBox::error(0, i18n("Not enough memory to load FITS."));
    return (NULL);
  }
  tempDataPtr      = tempData;
  
  if (fread(tempData, 1, width * height * bpp, ifp->fp) != (unsigned int) (width * height * bpp))
  {
    KMessageBox::error(0, i18n("Unable to read FITS data from file. %1.\n").arg(strerror(errno)));
    return (NULL);
  }
  
  switch (bitpix)
  {
   case 8:
    for (int i=0; i < totalCount; i++)
      buffer[i] = tempData[i];
    break;
    
   case 16:
    for (int i=0; i < totalCount ; i++)
    {
       FITS_GETBITPIX16(tempData, pixval_16);
       buffer[i] = pixval_16;//ntohs(pixval_16);
       tempData+=2;
     }
     break;
      
   case 32:
   for (int i=0; i < totalCount ; i++)
    {
    //pixval_32 = ntohl(pixval_32);
    if (fits_nan_32(tempData)) {
        pixval_32 = 0;
    }
    else {
        FITS_GETBITPIX32(tempData, pixval_32);
    }
    buffer[i] = pixval_32;
    tempData+=4;
   }
    break;
    
   case -32:
   for (int i=0; i < totalCount ; i++)
    {
    if (fits_nan_32 (tempData))
     pixval_m32 = 0;
    else
     FITS_GETBITPIXM32(tempData, pixval_m32);
    buffer[i] = pixval_m32;    
    tempData+=4;
    }
    break;
    
    case -64:
    for (int i=0; i < totalCount ; i++)
    {
    if (fits_nan_64 (tempData))
     pixval_m64 = 0;
    else
     FITS_GETBITPIXM64(tempData, pixval_m64);
    buffer[i] = pixval_m64;
    tempData+=8;
   }
    break;
  }
 
  fits_close(ifp);
  free(tempDataPtr); 
  return buffer;                                              

}

void FITSViewer::calculateStats()
{
  /*kdDebug() << "Calculating statistics..." << endl;*/
  stats.min 	= min(stats.minAt);
  stats.max 	= max(stats.maxAt);
  stats.average = average();
  stats.stddev  = stddev();
  stats.bitpix  = image->bitpix;
  stats.width   = image->width;
  stats.height  = image->height;
  
  /*kdDebug() << "Min: " << stats.min << " - Max: " << stats.max << endl;
  kdDebug() << "Average: " << stats.average << " - stddev: " << stats.stddev << endl;
  kdDebug() << "Width: " << stats.width << " - Height " << stats.height << " - bitpix " << stats.bitpix << endl;*/
  
  statusBar()->changeItem( TQString("%1 x %2").arg( (int) stats.width).arg( (int) stats.height), 2);

}

double FITSViewer::min(int & minIndex)
{
  if (!imgBuffer) return -1;
  int width   = image->currentRect.width();
  int height  = image->currentRect.height();
  double lmin = imgBuffer[image->currentRect.y() * width + image->currentRect.x()];
  int index=0; 
  
  for (int i= image->currentRect.y() ; i < height; i++)
    for (int j= image->currentRect.x(); j < width; j++)
    {
       index = (i * width) + j;
       if (imgBuffer[index] < lmin)
       {
         minIndex = index;
         lmin = imgBuffer[index];
       }
       
    }
    
    return lmin;
}

double FITSViewer::max(int & maxIndex)
{
  if (!imgBuffer) return -1;
  int width   = image->currentRect.width();
  int height  = image->currentRect.height();
  double lmax = imgBuffer[image->currentRect.y() * width + image->currentRect.x()];
  int index=0;
  
  for (int i= image->currentRect.y() ; i < height; i++)
    for (int j= image->currentRect.x(); j < width; j++)
    {
       index = (i * width) + j;
       if ( imgBuffer[index] > lmax)
       {
        maxIndex = index;
        lmax = imgBuffer[index];
       }
    }
    
    return lmax;
}

double FITSViewer::average()
{
  int index=0;
  double sum=0;  
  int width  = image->currentRect.width();
  int height = image->currentRect.height();
  if (!imgBuffer) return -1;
  
  for (int i= image->currentRect.y() ; i < height; i++)
    for (int j= image->currentRect.x(); j < width; j++)
    {
       index = (i * width) + j;
       sum += imgBuffer[index];
    }
    
    return (sum / (width * height ));
}

double FITSViewer::stddev()
{
  int index=0;
  double lsum=0;
  int width  = image->currentRect.width();
  int height = image->currentRect.height();
  if (!imgBuffer) return -1;
  
  for (int i= image->currentRect.y() ; i < height; i++)
    for (int j= image->currentRect.x(); j < width; j++)
    {
       index = (i * width) + j;
       lsum += (imgBuffer[index] - stats.average) * (imgBuffer[index] - stats.average);
    }
    
  return (sqrt(lsum/(width * height - 1)));

}

void FITSViewer::keyPressEvent (TQKeyEvent *ev)
{
        //TQImage Tempimage = imageList.at(undo+1)->copy();
	
	ev->accept();  //make sure key press events are captured.
	switch (ev->key())
	{
		//case Key_H    : KImageEffect::contrastHSV(image); break;
		//case Key_S    : KImageEffect::sharpen(image); break;
		//case Key_B    : KImageEffect::blur(image); break;
	
		default : ev->ignore();
	}
	
}

void FITSViewer::fileOpen()
{

  if (Dirty)
  {
    
  TQString caption = i18n( "Save Changes to FITS?" );
		TQString message = i18n( "The current FITS file has unsaved changes.  Would you like to save before closing it?" );
		int ans = KMessageBox::warningYesNoCancel( 0, message, caption, KStdGuiItem::save(), KStdGuiItem::discard() );
		if ( ans == KMessageBox::Yes )
			fileSave();	
		else if ( ans == KMessageBox::No ) 
			fitsRestore();
   }
   
   KURL fileURL = KFileDialog::getOpenURL( TQDir::homeDirPath(), "*.fits *.fit *.fts|Flexible Image Transport System");
  
  if (fileURL.isEmpty())
    return;

  
  currentURL = fileURL;
  
  initFITS();

}

void FITSViewer::fileSave()
{
  
  FITS_FILE *ifp;
  TQString recordList;
  KURL backupCurrent = currentURL;
  TQString bitpixRec;
  FITS_BITPIX16  pixval_16  =0;
  FITS_BITPIX32  pixval_32  =0;
  FITS_BITPIXM32 pixval_m32 =0;
  FITS_BITPIXM64 pixval_m64 =0;
  unsigned char *transData;
  int index=0, i=0, transCount = 0, totalCount= image->width * image->height;
  
  TQString currentDir = Options::fitsSaveDirectory();
  
  //kdDebug() << "We doing stats BEFORE we save!! " << endl;
  //calculateStats();
  
  // If no changes made, return.
  if (Dirty == 0 && !currentURL.isEmpty())
    return;
  
  if (currentURL.isEmpty())
  {
  	currentURL = KFileDialog::getSaveURL( currentDir, "*.fits |Flexible Image Transport System");
	// if user presses cancel
	if (currentURL.isEmpty())
	{
	  currentURL = backupCurrent;
	  return;
	}
	if (currentURL.path().contains('.') == 0) currentURL.setPath(currentURL.path() + ".fits");
	
	if (TQFile::exists(currentURL.path()))
        {
            int r=KMessageBox::warningContinueCancel(static_cast<TQWidget*>(parent()),
            i18n( "A file named \"%1\" already exists. "
                  "Overwrite it?" ).arg(currentURL.fileName()),
            i18n( "Overwrite File?" ),
            i18n( "&Overwrite" ) );
  
             if(r==KMessageBox::Cancel) return;
         }
   }

  if ( currentURL.isValid() )
  {
        transData = (unsigned char *) malloc (sizeof(unsigned char) * totalCount * image->bpp);
	if (transData == NULL)
	{
  		KMessageBox::error(0, i18n("Error: Low memory. Saving is aborted."));
		return;
  	}
  
  	ifp = fits_open (currentURL.path().ascii(), "w");
        if (ifp == NULL)
        {
          KMessageBox::error(0, i18n("Error during open of FITS file."));
          return;
        }
    	
	setbuf(ifp->fp, NULL);
	
	bitpixRec.sprintf("BITPIX  =                    %d /Modified by KStars                               ", image->bitpix);
	bitpixRec.truncate(80);
	
	for (unsigned int j=0; j < record.count(); j++)
	{
	  recordList = record[j];
	  
	  if ( (index = recordList.find("BITPIX")) != -1)
	  	recordList.replace(index, FITS_CARD_SIZE, bitpixRec);
	
	  fwrite(recordList.ascii(), 1, FITS_RECORD_SIZE, ifp->fp);
        }
	
	switch (image->bitpix)
	{
	   case 8:
		for (i= image->height - 1; i >= 0; i--)
		fwrite(image->displayImage->scanLine(i), 1, image->width, ifp->fp);
		break;
		
		
	   case 16:
    		for (i= 0, transCount = 0 ; i < totalCount ; i++, transCount += 2)
    		{
       			pixval_16 = (unsigned short) imgBuffer[i];
			transData[transCount]   = ((unsigned char*) &pixval_16)[1];
			transData[transCount+1] = ((unsigned char*) &pixval_16)[0];
		}
		// Now we need to write all uchars to file. We have 2 bytes per pixel
		transCount = 0;
		totalCount *= 2;
		
		for (i=0, transCount = 0; i < totalCount; i += transCount)
		   transCount = fwrite( transData + i , 1, totalCount - i, ifp->fp);
       			
     		break;
      
   	  case 32:
	        for (i=0, transCount = 0 ; i < totalCount ; i++, transCount += 4)
    		{
       			pixval_32 = (unsigned int) imgBuffer[i];
			transData[transCount]       = ((unsigned char*) &pixval_32)[3];
			transData[transCount+1]     = ((unsigned char*) &pixval_32)[2];
			transData[transCount+2]     = ((unsigned char*) &pixval_32)[1];
			transData[transCount+3]     = ((unsigned char*) &pixval_32)[0];
		}
		
		// Now we need to write all uchars to file. We have 4 bytes per pixel
		transCount = 0;
		totalCount *= 4;
		
		for (i=0, transCount = 0; i < totalCount; i += transCount)
		   transCount = fwrite( transData + i , 1, totalCount - i, ifp->fp);
    		break;
    
   	case -32:
		for (i=0, transCount = 0 ; i < totalCount ; i++, transCount += 4)
    		{
       			pixval_m32 = imgBuffer[i];
			transData[transCount]       = ((unsigned char*) &pixval_m32)[3];
			transData[transCount+1]     = ((unsigned char*) &pixval_m32)[2];
			transData[transCount+2]     = ((unsigned char*) &pixval_m32)[1];
			transData[transCount+3]     = ((unsigned char*) &pixval_m32)[0];
			
		}
		
		// Now we need to write all uchars to file. We have 4 bytes per pixel
		transCount = 0;
		totalCount *= 4;
		
		for (i=0, transCount = 0; i < totalCount; i += transCount)
		   transCount = fwrite( transData + i , 1, totalCount - i, ifp->fp);
   		
    		break;
    
    	case -64:
    		for (i=0, transCount = 0 ; i < totalCount ; i++, transCount += 8)
    		{
       			pixval_m64 = imgBuffer[i];
			transData[transCount]   = 0;
			transData[transCount+1] = 0;
			transData[transCount+2] = 0;
			transData[transCount+3] = 0;
			transData[transCount+4] = ((unsigned char*) &pixval_m32)[3];
			transData[transCount+5] = ((unsigned char*) &pixval_m32)[2];
			transData[transCount+6] = ((unsigned char*) &pixval_m32)[1];
			transData[transCount+7] = ((unsigned char*) &pixval_m32)[0];
			
		}
		
		// Now we need to write all uchars to file. We have 4 bytes per pixel
		transCount = 0;
		totalCount *= 8;
		
		for (i=0, transCount = 0; i < totalCount; i += transCount)
		   transCount = fwrite( transData + i , 1, totalCount - i, ifp->fp);
	        break;	
	}
		
	fits_close(ifp);
	
	statusBar()->changeItem(i18n("File saved."), 3);
	
	free(transData);
	Dirty = 0;
	history->clear();
	fitsRestore();
	//updateImgBuffer();
  }
  else
  {
		TQString message = i18n( "Invalid URL: %1" ).arg( currentURL.url() );
		KMessageBox::sorry( 0, message, i18n( "Invalid URL" ) );
  }
	

}

void FITSViewer::fileSaveAs()
{
  
  currentURL = "";
  fileSave();
}

void FITSViewer::fitsCOPY()
{
   kapp->clipboard()->setImage(*image->displayImage);
}

void FITSViewer::updateImgBuffer()
{
  int width = image->width;
  int height = image->height;
  
  for (int i=0; i < height; i++)
    for (int j=0; j < width; j++)
       imgBuffer[i * width + j] = (int) *(image->displayImage->scanLine(height - i - 1) + j);
       //image->reducedImgBuffer[i * width + j];

   calculateStats();
}

void FITSViewer::imageReduction()
{
  FITSProcessCommand *cbc;
  FITSHistogramCommand *hbc;
  TQStringList darkFiles, flatFiles, darkflatFiles;
  int darkCombineMode = 0 , flatCombineMode = 0, darkflatCombineMode =0;
  TQListViewItem *file;
  
  image->saveTemplateImage();
  ImageReductionDlg irDialog(this);
     
  if (irDialog.exec() == TQDialog::Accepted)
  {
    if (irDialog.darkListView->childCount() == 0 && 
        irDialog.flatListView->childCount() == 0)
	{
	 image->destroyTemplateImage();
	 return;
	}
     
    darkCombineMode    = irDialog.darkAverageB->isChecked() ? 0 : 1;
    flatCombineMode    = irDialog.flatAverageB->isChecked() ? 0 : 1;
    darkflatCombineMode= irDialog.darkflatAverageB->isChecked() ? 0 : 1;
     
    file = irDialog.darkListView->firstChild();
    while (file)
    {
      darkFiles << file->text(0);
      file = file->nextSibling();
    }
    
    file = irDialog.flatListView->firstChild();
    while (file)
    {
      flatFiles << file->text(0);
      file = file->nextSibling();
    }
    
    file = irDialog.darkflatListView->firstChild();
    while (file)
    {
      darkflatFiles << file->text(0);
      file = file->nextSibling();
    }
      
      cbc = new FITSProcessCommand(this);
      FITSProcess reduc(this, darkFiles, flatFiles, darkflatFiles, darkCombineMode, flatCombineMode, darkflatCombineMode);
      reduc.reduce();
      history->addCommand(cbc, false);
      calculateStats();
      hbc = new FITSHistogramCommand(this, NULL, FITSImage::FITSLinear, (int) stats.min, (int) stats.max);
      history->addCommand(hbc);
      fitsChange();
  }
  
  image->destroyTemplateImage();

}

void FITSViewer::BrightContrastDlg()
{
  FITSChangeCommand *cbc;
  image->saveTemplateImage();
  ContrastBrightnessDlg conbriDlg(this);
    
  if (conbriDlg.exec() == TQDialog::Rejected)
  {
    image->reLoadTemplateImage();
    image->zoomToCurrent();
  }
  else
  {
    memcpy(imgBuffer , conbriDlg.localImgBuffer, stats.width * stats.height * 4);
    free(conbriDlg.localImgBuffer);
    fitsChange();
    image->update();
    cbc = new FITSChangeCommand(this, CONTRAST_BRIGHTNESS, image->displayImage, image->templateImage);
    history->addCommand(cbc, false);

  }
  
  image->destroyTemplateImage();
    
}

void FITSViewer::imageHistogram()
{

  /*FITSHistogramCommand *histC;
  unsigned int * backupBuf = (unsigned int *) malloc (image->width * image->height * sizeof(unsigned int));
  if (backBuf == NULL)
  {
       KMessageBox::error(0, i18n("Not enough memory to complete the operation."));
       return;
  }
  memcpy(backupBuf, imgBuffer, width * height);*/
   
  //image->saveTemplateImage();
  
  if (histo == NULL)
  {
    histo = new FITSHistogram(this);
    histo->show();
  }
  else
  {
    histo->constructHistogram(imgBuffer);
    histo->updateBoxes();
    histo->show();
  }
    
  /*if (hist.exec() == TQDialog::Rejected)
  {
    if (hist.napply > 0)
      for (int i=0; i < hist.napply; i++)
        history->undo();
    else
    {
    	image->reLoadTemplateImage();
    	image->zoomToCurrent();
    }
    //free (backupBuf);
  }
  else
  {
    if (hist.napply > 0) fitsChange();
    //histC = new FITSHistogramCommand(this, hist.type, backupBuf, image->displayImage, image->templateImage);
    //history->addCommand(histC, false);

  }
  
  image->destroyTemplateImage();*/
  
}

void FITSViewer::fitsRestore() 
{
 
 Dirty = 0;
 setCaption(currentURL.fileName());
 }

void FITSViewer::fitsChange() 
{
 
 Dirty = 1;
 
 setCaption(currentURL.fileName() + i18n(" [modified]"));
}

void FITSViewer::fitsStatistics()
{
  statForm stat(this);
  
  calculateStats();
  
  stat.widthOUT->setText(TQString("%1").arg(stats.width));
  stat.heightOUT->setText(TQString("%1").arg(stats.height));
  stat.bitpixOUT->setText(TQString("%1").arg(stats.bitpix));
  stat.maxOUT->setText(TQString("%1").arg(stats.max));
  stat.minOUT->setText(TQString("%1").arg(stats.min));
  stat.atMaxOUT->setText(TQString("%1").arg(stats.maxAt));
  stat.atMinOUT->setText(TQString("%1").arg(stats.minAt));
  stat.meanOUT->setText(TQString("%1").arg(stats.average));
  stat.stddevOUT->setText(TQString("%1").arg(stats.stddev));
  
  stat.exec();

}

void FITSViewer::fitsHeader()
{
   TQStringList cards;
   TQString recordList;
   TQString property;
   int equal, slash;

   fitsHeaderDialog header(this);
   header.headerView->setSorting(-1);
   header.headerView->setColumnAlignment(1, TQt::AlignHCenter);
   
   for (unsigned int i=0; i < record.count(); i++)
   {
     recordList = record[i];
     //recordList = TQString((char *) record);
   
   	for (int j=0; j < FITS_RECORD_SIZE / FITS_CARD_SIZE; j++)
   	{
     	       property = recordList.left(FITS_CARD_SIZE);
	       
	       equal = property.find('=');
	       
	       if (equal == -1)
	       {
	        if (property.contains(" ") != FITS_CARD_SIZE)
	         	cards << property << "" << "";
		 recordList.remove(0, FITS_CARD_SIZE);
		 if (property.find("END") != -1)
		  break;
		 else
		  continue;
	       }
	       
	       
     	       cards << property.left(equal);
     	       slash = property.find("'");
     	       if (slash != -1)
       		slash = property.find("'", slash + 1) + 1;
     	       else
       		slash = property.find('/') - 1;
       
     		cards << property.mid(equal + 2, slash - (equal + 2)).simplifyWhiteSpace().remove("'");
     		cards << property.mid(slash + 1, FITS_CARD_SIZE - (slash + 1)).simplifyWhiteSpace();
		recordList.remove(0, FITS_CARD_SIZE);
		
	}
	
   }
   
   for (int k= cards.count() - 3; k >=0 ; k-=3)
       		   new TQListViewItem( header.headerView, cards[k], cards[k+1], cards[k+2]);
  
   
   header.exec();

}


FITSChangeCommand::FITSChangeCommand(TQWidget * parent, int inType, TQImage* newIMG, TQImage *oldIMG)
{
  viewer    = (FITSViewer *) parent;
  newImage  = new TQImage();
  oldImage  = new TQImage();
  *newImage = newIMG->copy();
  *oldImage = oldIMG->copy();
  type = inType;
}

FITSChangeCommand::~FITSChangeCommand() {}
            
void FITSChangeCommand::execute()
{

  viewer->image->displayImage = newImage;
  viewer->image->zoomToCurrent();
  viewer->fitsChange();

}

void FITSChangeCommand::unexecute()
{

  viewer->image->displayImage = oldImage;
  viewer->image->zoomToCurrent();

}

TQString FITSChangeCommand::name() const
{
   switch (type)
   {
     case FITSViewer::CONTRAST_BRIGHTNESS:
            return i18n("Brightness/Contrast");
	    break;
     case FITSViewer::IMAGE_REDUCTION:
            return i18n("Image Reduction");
	    break;
     case FITSViewer::IMAGE_FILTER:
            return i18n("Image Filter");
	    break;
     default:
            return i18n("unknown");
	    break;
   }
}



#include "fitsviewer.moc"
