/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.micromanager.acquisition;

import ij.ImagePlus;
import ij.io.FileSaver;
import ij.io.Opener;
import ij.process.ByteProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedQueue;
import mmcorej.Metadata;
import mmcorej.StrVector;
import org.micromanager.utils.JavaUtils;
import org.micromanager.utils.ReportingUtils;

/**
 *
 * @author arthur
 */

public class MMImageCache {

   private boolean firstElement_;
   private BufferedWriter metadataStream_;
   private final String dir_;
   private ConcurrentLinkedQueue<MMImageBuffer> imgBufQueue_;
   private int imgBufQueueSize_ = 50;

   
   MMImageCache(String pathOnDisk) {
      dir_ = pathOnDisk;

      try {
         JavaUtils.createDirectory(dir_);
         metadataStream_ = new BufferedWriter(new FileWriter(dir_ + "/metadata.txt"));
         metadataStream_.write("{" + "\r\n");
         firstElement_ = true;
         imgBufQueue_ = new ConcurrentLinkedQueue<MMImageBuffer>();
      } catch (Exception e) {
         ReportingUtils.showError(e);
      }
   }

   public void cleanup() {
      try {
         metadataStream_.write("\r\n}\r\n");
         metadataStream_.close();
      } catch (IOException ex) {
         ReportingUtils.logError(ex);
      }
   }

   public String putImage(Object img, Metadata md) {
      return putImage(new MMImageBuffer(img, md));
   }

   public String putImage(MMImageBuffer imgBuf) {
      Metadata md = imgBuf.md;
      Object img = imgBuf.img;
      String tiffFileName = createFileName(md);
      saveImageFile(img, md, dir_, tiffFileName);
      writeFrameMetadata(md, tiffFileName);
      cacheImage(tiffFileName, img, md);
      return tiffFileName;
   }

   public MMImageBuffer getImage(String filename) {
      for (MMImageBuffer imgBuf:imgBufQueue_) {
         if (imgBuf.filename.equals(filename)) {
            return imgBuf;
         }
      }

      ImagePlus imp = new Opener().openImage(dir_ + "/" + filename);
      Object img = imp.getProcessor().getPixels();
      Metadata md = null;
      MMImageBuffer imgBuf = new MMImageBuffer(filename, img, md);
      cacheImage(imgBuf);
      return imgBuf;
   }

   private void cacheImage(MMImageBuffer imgBuf) {
      imgBufQueue_.add(imgBuf);
      if (imgBufQueue_.size() > imgBufQueueSize_) { // If the queue is full,
         imgBufQueue_.poll();                       // remove the oldest image.
      }
   }

   private void cacheImage(String filename, Object img, Metadata md) {
      MMImageBuffer imgBuf = new MMImageBuffer(filename, img, md);
      cacheImage(imgBuf);
   }

   private void saveImageFile(Object img, Metadata md, String path, String tiffFileName) {
      ImageProcessor ip = null;
      int width = md.getWidth();
      int height = md.getHeight();
      if (img instanceof byte[]) {
         ip = new ByteProcessor(width, height);
         ip.setPixels((byte[]) img);
      } else if (img instanceof short[]) {
         ip = new ShortProcessor(width, height);
         ip.setPixels((short[]) img);
      }

      if (ip != null) {
         ImagePlus imp = new ImagePlus(path + "/" + tiffFileName, ip);
         FileSaver fs = new FileSaver(imp);
         fs.saveAsTiff(path + "/" + tiffFileName);
      }
   }

   public void writeMetadata(Metadata md, String title) {
      try {
         if (!firstElement_) {
            metadataStream_.write(",\r\n");
         }
         metadataStream_.write("\t\"" + title + "\": {\r\n");

         StrVector keys = md.getFrameKeys();
         long n = keys.size();

         for (int i = 0; i < n; ++i) {
            metadataStream_.write("\t\t\"" + keys.get(i) + "\": \"" + md.get(keys.get(i)) + "\"");
            if (i < (n - 1)) {
               metadataStream_.write(",\r\n");
            } else {
               metadataStream_.write("\r\n");
            }
         }

         metadataStream_.write("\t}");
         metadataStream_.flush();
         firstElement_ = false;
      } catch (Exception e) {
         ReportingUtils.logError(e);
      }
   }

   private void writeFrameMetadata(Metadata md, String fileName) {
      String title = "FrameKey-" + md.getFrame()
              + "-" + md.getChannelIndex() + "-" + md.getSlice();
      md.put("Filename", fileName);
      writeMetadata(md, title);
   }

   private String createFileName(Metadata md) {
      return "img_"
              + String.format("%09d", md.getFrame())
              + "_"
              + md.getChannel()
              + "_"
              + String.format("%03d", md.getSlice())
              + ".tif";
   }
}

@@ -2254,20 +2254,64 @@
 ta md = 
-null
+yamlToMetadata((String) imp.getProperty(%22Info%22))
 ;%0A      
@@ -3389,24 +3389,78 @@
 eName, ip);%0A
+         imp.setProperty(%22Info%22, metadataToYaml(md));%0A
          Fil
@@ -4873,15 +4873,504 @@
 %22.tif%22;%0A
-
    %7D%0A
+%0A   private static String metadataToYaml(Metadata md) %7B%0A      String yaml = %22%22;%0A      for (String key:md.getFrameKeys()) %7B%0A         yaml += key + %22: %22 +md.get(key) + %22%5Cr%5Cn%22;%0A      %7D%0A      return yaml;%0A   %7D%0A%0A   private static Metadata yamlToMetadata(String yaml) %7B%0A      Metadata md = new Metadata();%0A      String %5B%5D lines = yaml.split(%22%5Cr%5Cn%22);%0A      for (String line:lines) %7B%0A         String %5B%5D parts = line.split(%22: %22);%0A         md.put(parts%5B0%5D, parts%5B1%5D);%0A      %7D%0A      return md;%0A   %7D%0A%0A
 %7D%0A

Read and store metadata in TIFF.