/*
 * #%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 com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Label;
import java.awt.event.ItemEvent;
import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Vector;

import javax.swing.JPanel;

import ij.IJ;
import ij.gui.GenericDialog;

import loci.common.Location;
import loci.formats.FilePattern;
import loci.formats.FilePatternBlock;

/**
 * Bio-Formats Importer file pattern dialog box.
 */
public class FilePatternDialog extends ImporterDialog {

  // -- Fields --

  private FilePattern fp;
  private String originalID;
  private int[] paddingZeros;

  // -- Constructor --

  /** Creates a file pattern dialog for the Bio-Formats Importer. */
  public FilePatternDialog(ImportProcess process) {
    super(process);
  }

  // -- ImporterDialog methods --

  @Override
  protected boolean needPrompt() {
    return !process.isWindowless() && options.isGroupFiles();
  }

  @Override
  protected GenericDialog constructDialog() {
    // CTR - CHECK
    Location idLoc = new Location(options.getId());
    String id = FilePattern.findPattern(idLoc);
    if (id == null) {
      if (!options.isQuiet()) {
        IJ.showMessage("Bio-Formats",
          "Warning: Bio-Formats was unable to determine a grouping that\n" +
          "includes the file you chose. The most common reason for this\n" +
          "situation is that the folder contains extraneous files with\n" +
          "similar names and numbers that confuse the detection algorithm.\n" +
          " \n" +
          "For example, if you have multiple datasets in the same folder\n" +
          "named series1_z*_c*.tif, series2_z*_c*.tif, etc., Bio-Formats\n" +
          "may try to group all such files into a single series.\n" +
          " \n" +
          "For best results, put each image series's files in their own\n" +
          "folder, or type in a file pattern manually.\n");
      }
      id = idLoc.getAbsolutePath();
    }

    // construct dialog
    GenericDialog gd = new GenericDialog("Bio-Formats File Stitching") {
      @Override
      public void itemStateChanged(ItemEvent e) {
        super.itemStateChanged(e);

        Object source = e.getSource();

        if (!(source instanceof Checkbox)) {
          return;
        }

        boolean selected = e.getStateChange() == ItemEvent.SELECTED;

        Vector checkboxes = getCheckboxes();

        for (Object checkbox : checkboxes) {
          if (!checkbox.equals(source)) {
            if (selected) {
              ((Checkbox) checkbox).setState(false);
            }
          }
          else if (!selected && checkbox.equals(source)) {
            ((Checkbox) checkbox).setState(true);
          }
        }
      }
    };
    gd.addMessage(
      "The list of files to be grouped can be specified in one of the following ways:");

    // option one

    int len = id.length() + 1;
    if (len > 80) len = 80;

    originalID = id;
    fp = new FilePattern(id);

    String[] prefixes = fp.getPrefixes();

    if (prefixes.length > 0) {
      gd.addCheckbox("Dimensions", true);

      int[] counts = fp.getCount();
      paddingZeros = new int[counts.length];
      String[][] elements = fp.getElements();

      BigInteger[] first = fp.getFirst();
      BigInteger[] step = fp.getStep();

      for (int i=0; i<prefixes.length; i++) {
        String prefix = "Axis_" + (i + 1);
        gd.addStringField(prefix + "_number_of_images", "" + counts[i]);
        gd.addStringField(prefix + "_axis_first_image", first[i].toString());
        gd.addStringField(prefix + "_axis_increment", step[i].toString());

        try {
          paddingZeros[i] = elements[i][0].length() -
            String.valueOf(Integer.parseInt(elements[i][0])).length();
        }
        catch (NumberFormatException e) { }
      }
    }

    // option two

    gd.addCheckbox("File_name", false);
    gd.addStringField("contains", "");

    // option three

    gd.addCheckbox("Pattern", false);
    gd.addStringField("name", id, len);

    rebuild(gd);

    return gd;
  }

  @Override
  protected boolean harvestResults(GenericDialog gd) {
    String[] counts = new String[fp.getPrefixes().length];
    String[] firsts = new String[counts.length];
    String[] increments = new String[counts.length];
    int[] count = fp.getCount();

    boolean useRanges = counts.length > 0 ? gd.getNextBoolean() : false;
    boolean useRegex = gd.getNextBoolean();

    for (int i=0; i<counts.length; i++) {
      counts[i] = gd.getNextString();
      firsts[i] = gd.getNextString();
      increments[i] = gd.getNextString();
    }

    String contains = gd.getNextString();
    String id = gd.getNextString();

    if (useRegex) {
      if (contains.trim().length() > 0) {
        String dir =
          originalID.substring(0, originalID.lastIndexOf(File.separator) + 1);
        id = dir + ".*" + contains + ".*";
      }
    }
    else if (useRanges) {
      StringBuilder pattern = new StringBuilder();
      for (int i=0; i<counts.length; i++) {
        BigInteger first = new BigInteger(firsts[i]);
        BigInteger fileCount = new BigInteger(counts[i]);
        BigInteger increment = new BigInteger(increments[i]);

        FilePatternBlock block = new FilePatternBlock(fp.getBlock(i));

        // Last image of each axis is defined as
        // (number of images - 1) * axis increment + axis first image
        fileCount = fileCount.subtract(BigInteger.ONE).multiply(increment).add(first);

        pattern.append(fp.getPrefix(i));
        pattern.append('<');
        int firstPadding = paddingZeros[i] - first.toString().length() + 1;
        for (int zero=0; zero<firstPadding; zero++) {
          pattern.append('0');
        }
        pattern.append(first);
        pattern.append('-');
        int lastPadding = paddingZeros[i] - fileCount.toString().length() + 1;
        for (int zero=0; zero<lastPadding; zero++) {
          pattern.append('0');
        }
        pattern.append(fileCount);
        pattern.append(':');
        pattern.append(increment);
        pattern.append('>');
      }
      pattern.append(fp.getSuffix());
      id = pattern.toString();
    }

    options.setId(id);
    return true;
  }

  private void rebuild(GenericDialog gd) {
    // rebuild dialog to organize things more nicely

    Vector checkboxes = gd.getCheckboxes();
    Vector fields = gd.getStringFields();
    final ArrayList<Label> labels = new ArrayList<Label>();

    for (Component c : gd.getComponents()) {
      if (c instanceof Label) {
        labels.add((Label) c);
      }
    }

    final String cols = "pref, 3dlu, pref, 3dlu, pref";

    final StringBuilder sb = new StringBuilder("pref, 3dlu, pref");
    for (int s=1; s<fields.size(); s++) {
      sb.append(", 3dlu, pref");
    }
    final String rows = sb.toString();

    final PanelBuilder builder = new PanelBuilder(new FormLayout(cols, rows));
    final CellConstraints cc = new CellConstraints();

    int row = 1;

    if (checkboxes.size() > 2 && fields.size() > 2) {
      builder.add((Component) labels.get(0), cc.xyw(1, row, 5));
      row += 2;
      builder.add((Component) checkboxes.get(0), cc.xy(1, row));

      for (int i=0; i<fields.size()-2; i++) {
        builder.add((Component) labels.get(i + 1), cc.xy(3, row));
        builder.add((Component) fields.get(i), cc.xy(5, row));
        row += 2;
      }
    }

    builder.add((Component) checkboxes.get(checkboxes.size() - 2), cc.xy(1, row));
    builder.add((Component) labels.get(labels.size() - 2), cc.xy(3, row));
    builder.add((Component) fields.get(fields.size() - 2), cc.xy(5, row));
    row += 2;

    builder.add((Component) checkboxes.get(checkboxes.size() - 1), cc.xy(1, row));
    builder.add((Component) labels.get(labels.size() - 1), cc.xy(3, row));
    builder.add((Component) fields.get(fields.size() - 1), cc.xy(5, row));
    row += 2;

    final JPanel masterPanel = builder.getPanel();

    gd.removeAll();

    GridBagLayout gdl = (GridBagLayout) gd.getLayout();
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = 0;
    gbc.gridy = 0;
    gbc.gridwidth = 3;
    gbc.gridheight = fields.size();
    gdl.setConstraints(masterPanel, gbc);

    gd.add(masterPanel);

    gd.setBackground(Color.white); // HACK: workaround for JPanel in a Dialog
  }

}
