/*

   GeCardPetOp_.java
   (c) GC - 27/08/2002

   Questo plugin carica in ImageJ uno stack di immagini Dicom ottenuto da uno studio
   cardiologico effettuato sulla PET GE.

   Il plugin e' stato sviluppato lavorando su uno studio composto da una serie di sequenze
   temporali. La PET GE estrae i dati in formato DICOM.

   E' direttamente derivato da "GePetOp" ((c) GC & SM, 2001).

*/

import java.awt.*;
import javax.swing.*;
import java.io.*;

import ij.*;
import ij.io.*;
import ij.gui.*;
import ij.process.*;
import ij.plugin.*;

// ---------------------------------------------

public class GeCardPetOp_ implements PlugIn {

	public void run(String arg) {

//    Si seleziona un file qualsiasi in una normale dialog box "Open ..." e 
//    si passa la directory al metodo "reorder()" ...

      boolean bRetVal;

		OpenDialog od = new OpenDialog("Open GE Cardiologic Pet stack...", arg);
		String directory = od.getDirectory();
		String name = od.getFileName();
		if (name==null)
			return;

      bRetVal = reorder(directory);

      if (! bRetVal)
         return;

//    Dopo aver rinominato i file con il metodo "reorder()", si inseriscono i nomi in un 
//    vettore di stringhe ...

		String[] list = new File(directory).list();
		if (list==null)
			return;

//    Il vettore viene sortato ...

		ij.util.StringSorter.sort(list);
		if (IJ.debugMode) IJ.write("FolderOpener: "+directory+" ("+list.length+" files)");
		int width=0,height=0,type=0;
		ImageStack stack = null;
		int n = 0;

//    Una ad una, le slices vengono caricate in una "ImagePlus" e, successivamente,
//    aggiunte allo stack "stack" creato - vuoto - in precedenza ...

		try {
			for (int i=0; i<list.length; i++) {
				ImagePlus imp = new Opener().openImage(directory, list[i]);
				if (imp!=null && stack==null) {
					stack = imp.createEmptyStack();
					width = imp.getWidth();
					height = imp.getHeight();
					type = imp.getType();
				}
				if (stack!=null)
					n = stack.getSize()+1;

				IJ.showStatus(n+"/"+list.length);
				IJ.showProgress((double)n/list.length);

				if (imp==null)
					IJ.write(list[i] + ": unable to open");
				else if (imp.getWidth()!=width || imp.getHeight()!=height)
					IJ.write(list[i] + ": wrong dimensions");
				else if (imp.getType()!=type)
					IJ.write(list[i] + ": wrong type");
				else
					stack.addSlice(imp.getTitle(), imp.getProcessor());

				System.gc();
			}

		} catch(OutOfMemoryError e) {
			IJ.outOfMemory("GE Pet Opener");
			stack.trim();
		}

//    Lo stack, purche' non vuoto, viene convertito in "ImagePlus" e visualizzato per
//    ulteriori elaborazioni ...

		if (stack.getSize()>0)
			new ImagePlus("Stack", stack).show();

		IJ.showProgress(1.0);

//    Ogni plugin fornito con ImageJ a titolo di esempio "registra" la propria classe:
//    cosi' facciamo anche noi ...

		IJ.register(GeCardPetOp_.class);
	}

// ---------------------------------------------

   public static boolean reorder (String dirName) {

//    "Reorder()" rinomina, quando necessario, i file della directory "directory", in
//    modo che la successiva funzione di caricamento crei uno stack in cui le slice si
//    presentano nell'ordine appropriato ...

      int iii;
      int jjj;
      int kkk;

      int nTotFiles = 0;

      boolean retVal, bFound, toRename;
      boolean notCorrectExecution = false;
      boolean bFirstDotFound = false;        
      boolean bSecondDotFound = false;        
      boolean bUnderscoreFound = false;
      boolean bErrorCondition = false;

      boolean bOrderReversal = false;

      int nFirstDotPos = -1;
      int nSecondDotPos = -1;
      int nUnderscorePos = -1;
      int pos1 = 0;
      int pos2 = 0;
      int nSlice;
      int digitStart = 0;
      int digitEnd = 0;

      String sub1, sub2;
      String subStr1;
      String subStr2;
      String subStr1Tmp;
      String subStr2Tmp;
      String cSlice, cSliceTmp;
      String nfName;

//    Si crea un oggetto di classe "File" ...

      File d = new File (dirName);

//    La directory deve esistere ed essere abilitata alla scrittura. Si tratta di controlli
//    di errore sostanzialmente superflui, residuo della precedente versione che chiedeva
//    di inserire manualmente il nome della directory da riordinare ...

      if (! d.exists()) {
         IJ.showMessage ("GE Pet Opener", "No such file or directory: " + dirName + ".");
         return (false);
      }

      if (! d.canWrite()) {
         IJ.showMessage ("GE Pet Opener", "Write protected: " + dirName + ".");
         return (false);
      }

      if (! d.isDirectory()) {
         IJ.showMessage ("GE Pet Opener", "Not a directory: " + dirName + ".");
         return (false);
      }

//    In un vettore di stringhe si inseriscono i nomi dei file della directory ...

      String[] files = d.list();

      if (files.length <= 0) {
         IJ.showMessage ("GE Pet Opener", "Directory is empty: " + dirName + ".");
         return (false);
      }

      GenericDialog gd = new GenericDialog("GeCardPetOp", IJ.getInstance());
      gd.addCheckbox("Time/space order reversal", false);
      gd.showDialog();
      if (gd.wasCanceled())
         return (false);

      bOrderReversal = gd.getNextBoolean();

      IJ.write ("Processing image files in " + dirName + " ... ");

//    Si processano ad uno ad uno i file della directory ...

      for (iii = 0; iii < files.length; iii++) {

         String currFile = files[iii];

//    Gestione di un errore interno che non dovrebbe verificarsi in condizioni 
//    normali ...

         File f = new File (dirName + currFile);
         if (! f.exists()) {
            IJ.showMessage ("GE Pet Opener", "INTERNAL ERROR. No such file: " + currFile + ".");
            return (false);
         }

//    Tra i nomi non corrispondenti allo standard GE PET, vengono qui saltati quelli
//    di meno di 10 caratteri e quelli che non cominciano per "Image" ...

         if (currFile.length() < 10) {
            IJ.write ("Not a Dicom image file: " + currFile + " (name length less than 10).");
            notCorrectExecution = true;
            continue;
         }

         sub1 = currFile.substring(0, 5);

         if (!sub1.equals("Image") && !sub1.equals("IMAGE") &&
             !sub1.equals("image")) {
            IJ.write ("Not a Dicom image file: " + currFile + " (name doesn't begin with 'Image').");
            notCorrectExecution = true;
            continue;
         }

         bFirstDotFound = false;        
         bSecondDotFound = false;        
         bUnderscoreFound = false;
         nFirstDotPos = -1;
         nSecondDotPos = -1;
         nUnderscorePos = -1;

//       Si presuppone che il nome sia del tipo "Image.d_D.dcm", dove "d" sta per un numero di cifre
//       variabile da una a tre ed indica la profondita' relativa della fetta in mm (arrotondata) e
//       "D" sta per un numero di cifre compreso da una a sei ed indica il tempo in millisecondi.

//       Scopo del codice che segue e' quello di rinominare ciascun file sostituendo "d" e "D" con
//       due stringhe lunghe sempre 6 caratteri, con padding di zero a sinistra,
//       in modo che il caricamento in ImageJ avvenga correttamente seguendo l'ordine alfabetico. Il
//       formato finale sara' dunque "Image.dddddd_DDDDDD.dcm".

//       Vengono gestiti (si spera!) tutti i possibili errori. Quando un file non risponde al formato
//       indicato, viene segnalato l'errore e l'elaborazione prosegue con il file successivo. Al ter-
//       mine dell'esecuzione viene segnalata la presenza di file presumibilmente spuri, affinche' 
//       l'utente possa cancellarli o spostarli, e ripetere l'esecuzione.

         for (jjj = 0; jjj<currFile.length(); ++jjj) {

            char cCurr = currFile.charAt(jjj);

            if (cCurr == '.') {

               if (! bFirstDotFound){
                  bFirstDotFound = true;
                  nFirstDotPos = jjj;
               }               
               else if (! bSecondDotFound)
               {
                  bSecondDotFound = true;
                  nSecondDotPos = jjj;
               }
               else {
                  IJ.write ("Not a Dicom image file: " + currFile + " (more than two dots in name).");
                  notCorrectExecution = true;
                  continue;
               }
            }

            else if (cCurr == '_'){               
               if (! bUnderscoreFound){
                  bUnderscoreFound = true;
                  nUnderscorePos = jjj;
               }
               else {
                  IJ.write ("Not a Dicom image file: " + currFile + " (more than one underscore in name).");
                  notCorrectExecution = true;
                  continue;                   
               }
            }
         }

         if (! bFirstDotFound || ! bSecondDotFound || ! bUnderscoreFound){
            IJ.write ("Not a Dicom image file: " + currFile + " (incorrect number of dots or underscores).");
            notCorrectExecution = true;
            continue;
         }

         if ((nFirstDotPos >= (nUnderscorePos-1)) || (nUnderscorePos >= (nSecondDotPos-1))){         
            IJ.write ("Not a Dicom image file: " + currFile + " (incorrect position of dots or underscores).");
            notCorrectExecution = true;
            continue;
         }

         subStr1 = currFile.substring(nFirstDotPos + 1, nUnderscorePos);
         bErrorCondition = false;

         for (kkk = 0; kkk < subStr1.length(); ++kkk)
         {
            if (! Character.isDigit(subStr1.charAt(kkk)))
            {
               IJ.write ("Not a Dicom image file: " + currFile + " (not only digits in first substring).");
               notCorrectExecution = true;
               bErrorCondition = true;
               break;
            }
         }

         subStr2 = currFile.substring(nUnderscorePos + 1, nSecondDotPos);

         for (kkk = 0; kkk < subStr1.length(); ++kkk)
         {
            if (! Character.isDigit(subStr1.charAt(kkk)))
            {
               IJ.write ("Not a Dicom image file: " + currFile + " (not only digits in second substring).");
               notCorrectExecution = true;
               bErrorCondition = true;
               break;
            }
         }
        
         if (bErrorCondition){
            continue;
         }

         if (subStr1.length() > 6 || subStr2.length() > 6)
         {
            IJ.write ("Not a Dicom image file: " + currFile + " (not only digits in second substring).");
            notCorrectExecution = true;
            continue;            
         }

         subStr1Tmp = "000000" + subStr1;
         subStr2Tmp = "000000" + subStr2;

         subStr1 = subStr1Tmp.substring(subStr1Tmp.length() - 6, subStr1Tmp.length());
         subStr2 = subStr2Tmp.substring(subStr2Tmp.length() - 6, subStr2Tmp.length());

//    Modifica del nome del file ...

         if (! bOrderReversal)
            nfName = dirName + "Image." + subStr1 + "_" + subStr2 + ".dcm";
         else
            nfName = dirName + "Image." + subStr2 + "_" + subStr1 + ".dcm";

         File nf = new File (nfName);
         retVal = f.renameTo (nf);

//    Si emette un messaggio di avvertimento qualora il nuovo nome di file
//    corrisponda a un file gia' esistente ...

         if (retVal)
            IJ.showStatus ("File " + (++nTotFiles) + " (\"" + currFile + 
                           "\") renamed to \"" + nfName + "\" ... ");
         else
            IJ.showStatus ("File \"" + currFile + "\" exists.");
      }

//    Viene emesso un messaggio riassuntivo sul numero di file rinominati. Qualora
//    fossero stati incontrati file fuori standard, ne viene data comunicazione: in 
//    tal caso, infatti, tali file devono essere spostati o cancellati prima di 
//    caricare lo stack in modo corretto ...

      if (nTotFiles != 0)
         IJ.write ("Directory " + dirName + " correctly reordered (" +
                   nTotFiles + " image files).\n");
      else
         IJ.write ("Directory " + dirName + ": no image files to reorder.\n");

      if (notCorrectExecution)
         IJ.write ("Directory " + dirName + ": found non-Dicom image files.\n");

      return (true);
   }
}

/*** EOF ***/

