/*
 * #%L
 * OME Bio-Formats manual and automated test suite.
 * %%
 * 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.tests;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import loci.common.RandomAccessInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A class for testing {@link loci.common.RandomAccessInputStream}'s
 * efficiency in various cases.
 *
 * @author Curtis Rueden ctrueden at wisc.edu
 */
public class IOTester {

  private static final Logger LOGGER = LoggerFactory.getLogger(IOTester.class);

  private static final String TAG = "<END>";
  private static final int SIZE = 50 * 1024 * 1024; // in bytes
  private static final long NUM_DOTS = 80;

  private static final String ALPHANUM =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";

  public byte[] createData() {
    // create random test data
    byte[] data = new byte[SIZE];
    int middle = (int) ((data.length - TAG.length() - 1) * Math.random());
    int left = data.length - middle - TAG.length();
    int rest = middle + TAG.length();

    long middlePercent = 100L * middle / SIZE;
    long leftPercent = 100L * left / SIZE;

    LOGGER.info("Generating data: {} ({}%) alphanumeric + {} ({}%) binary",
      new Object[] {middle, middlePercent, left, leftPercent});

    // TODO: This should use a specific instance like
    // LOGGER2 = LoggerFactory.getLogger("IOTester.ALPHANUM")
    long progress = 0;
    for (int i=0; i<data.length; i++) {
      // print dots to indicate progress
      long p = NUM_DOTS * i / data.length;
      if (p > progress) {
        LOGGER.info(".");
        progress = p;
      }
      if (i < middle) {
        // write random alphanumeric
        int rnd = (int) (ALPHANUM.length() * Math.random());
        data[i] = (byte) ALPHANUM.charAt(rnd);
      }
      else if (i >= rest) {
        // write random binary
        int rnd = (int) (256 * Math.random());
        data[i] = (byte) rnd;
      }
      else { // middle <= i < rest;
        // write divider tag
        data[i] = (byte) TAG.charAt(i - middle);
      }
    }
    // END USE OF NEW LOGGER2
    LOGGER.info("");

    return data;
  }

  public void saveData(String filename, byte[] data) throws IOException {
    LOGGER.info("Saving {}...", filename);
    FileOutputStream out = new FileOutputStream(filename);
    out.write(data);
    out.close();
  }

  /** Searches for the divider tag using repeated readChar() calls. */
  public long testSequential(String filename) throws IOException {
    LOGGER.info("Searching for divider tag sequentially...");
    long start = System.currentTimeMillis();

    RandomAccessInputStream in = new RandomAccessInputStream(filename);
    int matchIndex = 0;
    char matchChar = TAG.charAt(0);
    long inputLen = in.length();
    for (long i=0; i<inputLen; i++) {
      char c = in.readChar();
      if (c == matchChar) {
        matchIndex++;
        if (matchIndex == TAG.length()) {
          break;
        }
        else {
          matchChar = TAG.charAt(matchIndex);
        }
      }
      else {
        matchIndex = 0;
        matchChar = TAG.charAt(0);
      }
    }
    long offset = in.getFilePointer();
    in.close();

    long end = System.currentTimeMillis();
    LOGGER.info("Search result: {} -- in {} ms", offset, end - start);
    return offset;
  }

  /** Searches for the divider tag in blocks of the given size. */
  public long testBlock(String filename, int blockSize) throws IOException {
    LOGGER.info("Searching for divider in blocks of {}...", blockSize);
    long start = System.currentTimeMillis();

    RandomAccessInputStream in = new RandomAccessInputStream(filename);
    long offset = in.findString(blockSize, TAG).length();
    in.close();

    long end = System.currentTimeMillis();
    LOGGER.info("Search result: {} -- in {} ms", offset, end - start);
    return offset;
  }

  public void deleteData(String filename) {
    LOGGER.info("Deleting {}", filename);
    File f = new File(filename);
    f.delete();
  }

  public void testIO() throws IOException {
    String prefix = "IOTester";
    byte[] data = createData();

    String file1 = prefix + "1.tmp";
    String file2 = prefix + "2.tmp";
    String file3 = prefix + "3.tmp";
    String file4 = prefix + "4.tmp";
    String file5 = prefix + "5.tmp";

    saveData(file1, data);
    saveData(file2, data);
    saveData(file3, data);
    saveData(file4, data);
    saveData(file5, data);

    testBlock(file1, 65536);
    testBlock(file2, 262144);
    testBlock(file3, 524288);
    testBlock(file4, 1048576);
    testSequential(file5);

    deleteData(file1);
    deleteData(file2);
    deleteData(file3);
    deleteData(file4);
    deleteData(file5);
  }

  public static void main(String[] args) throws IOException {
    new IOTester().testIO();
  }

}
