package nxm.sys.test.junit.lib;

import java.io.File;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.Arrays;

import nxm.sys.lib.*;
import nxm.sys.test.junit.NMTestCase;

/** This will test the FileResource class.
    @author  Conrad Clark
 */
public final class FileResourceTest extends NMTestCase {
  static {
  }

  /** public constructor to allow running this test case directly with JUnit. */
  public FileResourceTest (String methodName) { this(methodName, null); }

  /** This constructor is required by NeXtMidas Unit Testing framework.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
   */
  public FileResourceTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  public void testRead () {
    // this method is tested by testWrite()
  }

  public void testWrite () {
    Midas M = getMidasContext();
    try {
      byte[] foo = "foo".getBytes();
      byte[] bar = "bar".getBytes();
      byte[] baz = "baz".getBytes();
      byte[] buf = new byte[6];
      BaseFile bf = new BaseFile(M, File.createTempFile("nxmFileResourceTest", null).getAbsolutePath());
      bf.open(BaseFile.OUTPUT);
      FileResource fr = (FileResource)bf.getResource();

      // confirm that CoreIO.IOOptions.FileIONoAbort is off by default
      assertTrue("FileResource.write()", !isSet_FileIONoAbort(M));

      // test normal write()
      assertEquals("FileResource.write()", 3, fr.write(foo, 0, 3));
      assertEquals("FileResource.write()", 3, fr.write(bar, 0, 3));

      // force all subsequent write() attempts to fail
      FileResource_getRaf(fr).close();

      // test for pre-NeXtMidas 3.7.1 behavior following interrupted write()
      set_FileIONoAbort(M);
      assertTrue("FileResource.write()", isSet_FileIONoAbort(M));
      assertEquals("FileResource.write()", -1, fr.write(baz, 0, baz.length));

      // test for since-NeXtMidas 3.7.1 behavior following interrupted write()
      unset_FileIONoAbort(M);
      try {
        fr.write(baz, 0, baz.length);
        fail("FileResource.write() should have thrown an Exception");
      } catch (MidasIOException e) {
        // this is the expected behavior
      } catch (Exception e) {
        fail("FileResource.write() should have thrown a MidasIOException");
      }

      // reset
      bf.close();
      bf.open(BaseFile.INPUT);
      fr = (FileResource)bf.getResource();

      // test normal read()
      assertEquals("FileResource.read()", 3, fr.read(buf, 0, 3));
      assertArrayEquals("FileResource.read()", foo, Arrays.copyOf(buf, 3));

      // force all subsequent read() attempts to fail
      FileResource_getRaf(fr).close();

      // test for pre-NeXtMidas 3.7.1 behavior following interrupted read()
      set_FileIONoAbort(M);
      assertEquals("FileResource.read()", -1, fr.read(buf, 3, 3));

      // test for since-NeXtMidas 3.7.1 behavior following interrupted read()
      unset_FileIONoAbort(M);
      try {
        fr.read(buf, 3, 3);
        fail("FileResource.read() should have thrown an Exception");
      } catch (MidasIOException e) {
        // this is the expected behavior
      } catch (Exception e) {
        fail("FileResource.read() should have thrown a MidasIOException");
      }

      bf.close();
      bf.erase(true);
    } catch(Exception e) {
      e.printStackTrace();
      throw new MidasException("Problem in FileResourceTest.testWrite()", e);
    }
  }

  private RandomAccessFile FileResource_getRaf (FileResource fr) {
    try {
      Field f = FileResource.class.getDeclaredField("raf");
      f.setAccessible(true);
      return (RandomAccessFile)f.get(fr);
    } catch (Exception e) {
      e.printStackTrace();
      fail("unable to retrieve FileResource.raf via reflection");
      return null; // can't get here, but needed by compiler
    }
  }

  private boolean isSet_FileIONoAbort(Midas midas) {
    return -1 != midas.io.getOptions().indexOf("FileIONoAbort");
  }

  private void set_FileIONoAbort(Midas midas) {
    midas.io.setOptions("+FileIONoAbort");
  }

  private void unset_FileIONoAbort(Midas midas) {
    midas.io.setOptions("-FileIONoAbort");
  }
}
