package nxm.sys.lib;

import java.io.FilenameFilter;
import java.util.ArrayList;

import nxm.sys.lib.TarFile.TarEntry;

/** Resource for interacting with a TAR (Tape Archive) File.

    @author  J. Czechowski IV
    @since   NeXtMidas 2.5.0
 */
public class TarResource extends IOResource {
  private final TarFile  tarFile  = new TarFile();  // The archive
  private       String   fileName = null;           // The name of the file in the archive
  private       TarEntry tarEntry = null;           // The entry in the archive being accessed

  /** default constructor */
  public TarResource () {}

  @Override
  public String toString () {
    return "TarResource: " + getURL();
  }

  /** Sets the fully qualified URL of the resource.
      <pre>
        This is based on the format used by {@link JarResource} and {@link java.net.JarURLConnection}
        Some examples:

            tar:http://www.foo.com/bar/baz.jar!com/foo/Quux.class    ! Jar entry
            tar:http://www.foo.com/bar/baz.jar!com/foo/              ! Jar Directory

        Unlike {@link JarResource}, {@link TarResource} is a little more restrictive of
        the URL syntax allowed (it is closer to that used by {@link java.net.JarURLConnection}).
        This difference is intentional and due to the fact that:
          (1) some of the syntax allowed by {@link JarResource} causes ambiguities
          (2) TAR files that start with a "/" mean they expand into the root
              directory of the file system vs in the current directory
      </pre>
      @param urlstr The fully qualified URL of the resource.
   */
  @Override
  public void setURL (String urlstr) {
    urlstr = urlstr.replace("\\", "/"); // convert to UNIX style url, since 3.7.3 for NXM-3099
    super.setURL("tar:", ".tar", new String[] { "!", "!/" }, urlstr);

    String url      = getURL();
    int    index    = url.indexOf('!');
    String archName = url.substring(4,index); // 4 = Strip off "tar:"
    this.fileName   = url.substring(index+1);
    tarFile.init(M, archName);
    tarFile.open();
  }

  @Override
  public boolean exists () {
    if (tarEntry == null) {
      tarEntry = tarFile.getEntry(fileName);
    }
    return (tarEntry != null);
  }

  @Override
  public boolean open () {
    isOpen = false;
    if (dir <= DIR_INOUT) { // Input or In/Out
      tarEntry = tarFile.getEntry(fileName);
      if (tarEntry == null) {
        throw new MidasException("TarResource: Unable to open "+getURL()+": "
                               + "Can not find "+fileName+" in "+tarFile.getName());
      }
    }
    if (dir >= DIR_INOUT) { // Output
      // Output to a TAR file is not as simple as opening an output stream. There are a
      // number of special requirements that are not adept for read/write access to the
      // internal files. We may add this in the future... but that says maybe.
      throw new MidasException("TarResource: Output not supported at this time, URL="+getURL());
    }
    isOpen = true;
    seek(offset);
    return isOpen;
  }

  @Override
  public boolean close () {
    tarEntry = null;
    tarFile.close();
    isOpen = false;
    return isOpen;
  }

  @Override
  public long getLength () {
    return (tarEntry != null)? tarEntry.getSize() : 0L;
  }

  @Override
  public boolean seek (long off) {
    offset = off; // <-- DEPRECATE THIS!!!
    double seek = (double)(off + tarEntry.getOffset());
    tarFile.seek(seek);
    return true;
  }

  @Override
  public long seek () {
    return (long)(tarFile.seek() - tarEntry.getOffset());
  }

  @Override
  public long avail () {
    return tarEntry.getSize() - seek();
  }

  @Override
  public int read (byte[] buf, int boff, int bytes) {
    int avail = (int)Math.min(Integer.MAX_VALUE, avail());
    if (avail == 0) return -1;  // since 3.7.0: end of tar Entry (i.e. EOF) BUG 3012
    if (bytes > avail) bytes = avail;
    return tarFile.read(buf, boff, bytes);
  }

  @Override
  public int write (byte[] buf, int boff, int bytes) {
    throw new MidasException("TarResource: Output not supported at this time, URL="+getURL());
  }

  /** Return an array of the Objects inside a container. (This is used by HFiles.java
      to display the file information
      @return array of the objects
   */
  public Object[] getEntries () { return tarFile.getEntries(); }

  @Override
  public String[] getEntryList (FilenameFilter filter) {
    TarEntry[] entries = (TarEntry[]) tarFile.getEntries();
    ArrayList<String> matches = new ArrayList<>();

    for (TarEntry entry : entries) {
      String name = entry.getName();

      if (name.startsWith(fileName)) {
        name = name.substring(fileName.length());
        if ((name.length() > 0) && name.indexOf('/') < 0) {
          if ((filter == null) || filter.accept(null, name)) {
            matches.add(name);
          }
        }
      }
    }
    return matches.toArray(StringUtil.EMPTY_STRING_ARRAY);
  }

  /** @since NeXtMidas 3.3.0 */
  @Override
  public Time lastModifiedTime () {
    return (tarEntry == null)? new Time() : tarEntry.getTime();
  }

  /** @since NeXtMidas 3.3.0 */
  @Override
  public long lastModified () {
    return (long) lastModifiedTime().toJ1970();
  }
}
