/*
 * #%L
 * Bio-Formats Plugins for ImageJ: a collection of ImageJ plugins including the
 * Bio-Formats Importer, Bio-Formats Exporter, Bio-Formats Macro Extensions,
 * Data Browser and Stack Slicer.
 * %%
 * Copyright (C) 2006 - 2017 Open Microscopy Environment:
 *   - Board of Regents of the University of Wisconsin-Madison
 *   - Glencoe Software, Inc.
 *   - University of Dundee
 * %%
 * 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.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

package loci.plugins.in;

import ij.ImagePlus;
import ij.ImageStack;

import java.util.ArrayList;
import java.util.List;

/**
 * Logic for concatenating multiple images together.
 *
 * @author Curtis Rueden ctrueden at wisc.edu
 * @author Melissa Linkert melissa at glencoesoftware.com
 */
public class Concatenator {

  /** Concatenates the list of images as appropriate. */
  public List<ImagePlus> concatenate(List<ImagePlus> imps) {

    /* TODO : fix this to work in windowlessly
    GenericDialog axisDialog = new GenericDialog("Choose concatenation axis");
    String[] options = new String[] {"T", "Z", "C"};
    axisDialog.addMessage(
      "Choose the axis along which to concatenate image planes:");
    axisDialog.addChoice("Axis", options, options[0]);
    axisDialog.showDialog();

    if (axisDialog.wasCanceled()) {
      return imps;
    }

    String axis = axisDialog.getNextChoice();
    boolean doAppendT = axis.equals("T");
    boolean doAppendZ = axis.equals("Z");
    boolean doAppendC = axis.equals("C");
    */
    boolean doAppendT = true;
    boolean doAppendZ = true;
    boolean doAppendC = true;

    // list of output (possibly concatenated) images
    final List<ImagePlus> outputImps = new ArrayList<ImagePlus>();

    for (ImagePlus imp : imps) {
      final int width = imp.getWidth();
      final int height = imp.getHeight();
      final int type = imp.getType();
      final int cSize = imp.getNChannels();
      final int zSize = imp.getNSlices();
      final int tSize = imp.getNFrames();

      boolean append = false;
      for (int k=0; k<outputImps.size(); k++) {
        final ImagePlus outputImp = outputImps.get(k);
        final int w = outputImp.getWidth();
        final int h = outputImp.getHeight();
        final int outType = outputImp.getType();
        final int c = outputImp.getNChannels();
        final int z = outputImp.getNSlices();
        final int t = outputImp.getNFrames();

        // verify that images are compatible
        if (width != w || height != h) continue; // different XY resolution
        if (type != outType) continue; // different processor type
        final boolean canAppendT = cSize == c && zSize == z;
        final boolean canAppendZ = cSize == c && tSize == t;
        final boolean canAppendC = zSize == z && tSize == t;
        if (!canAppendT && !canAppendZ && !canAppendC) {
          // incompatible dimensions
          continue;
        }

        // concatenate planes onto this output image
        final ImageStack outputStack = outputImp.getStack();
        final ImageStack inputStack = imp.getStack();
        for (int s=0; s<inputStack.getSize(); s++) {
          outputStack.addSlice(inputStack.getSliceLabel(s + 1),
            inputStack.getProcessor(s + 1));
        }
        outputImp.setStack(outputImp.getTitle(), outputStack);

        // update image dimensions

        if (doAppendT && canAppendT) {
          outputImp.setDimensions(c, z, t + tSize);
        }
        else if (doAppendZ && canAppendZ) {
          outputImp.setDimensions(c, z + zSize, t);
        }
        else if (doAppendC && canAppendC) {
          outputImp.setDimensions(c + cSize, z, t);
        }

        append = true;
        break;
      }
      if (!append) {
        // could not concatenate input image to any existing output;
        // append it to the list of outputs directly instead
        outputImps.add(imp);
      }
    }

    return outputImps;
  }

}
