/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jp.sourceforge.dvibrowser.dvicore.io;

import java.io.RandomAccessFile;
import java.io.IOException;
import java.io.EOFException;

import jp.sourceforge.dvibrowser.dvicore.api.DviInput;


public class DviRandomAccessFileInput
implements DviInput
{
  private final RandomAccessFile in;

  public DviRandomAccessFileInput(RandomAccessFile in)
  {
    this.in = in;
  }

  public void close()
  throws IOException
  {
    in.close();
  }

  private long offset = 0;
  public long getOffset() { return offset; }
  public void setOffset(long offset) { this.offset = offset; }

  private long end = Long.MAX_VALUE;
  public void setEnd(long end) { this.end = end; }

  public boolean ready()
  throws IOException
  {
    return (offset <= end && (in.getFilePointer() < in.length()));
  }

  public int readU1()
  throws IOException
  {
    if (offset > end)
      throw new EOFException();
    int c = in.readUnsignedByte();
    if (c < 0) c += 256;
    offset++;
    return c;
  }

  public void readFully(byte [] buf)
  throws IOException
  {
    if (buf == null) return;
    if (buf.length <= 0) return;
    if (offset + buf.length - 1> end)
      throw new EOFException();
    in.readFully(buf);
    offset += buf.length;
  }

  public int readU(int len)
  throws IOException
  {
    int v = 0;

    if (len <= 0 || len > 4) 
      throw new IllegalArgumentException
        ("illegal value of len.");

    while (len > 0) {
      v = (v << 8) | readU1();
      len--;
    }

    return v;
  }

  public int readS(int len)
  throws IOException
  {
    int bits = len * 8;
    int a = 1;
    int v = 0;

    if (len <= 0 || len > 4)
      throw new IllegalArgumentException
        ("illegal value of len.");

    while (len > 0) {
      v = (v << 8) | readU1();
      a <<= 8;
      len--;
    }
    if (0 != (v & (1 << (bits - 1)))) {
      v -= a;
    }

    return v;
  }

  public int readU2()
  throws IOException
  {
    return ((readU1() << 8)  |
             readU1());
  }

  public int readU3()
  throws IOException
  {
    return ((readU1() << 16) |
            (readU1() << 8)  |
             readU1());
  }

  public int readU4()
  throws IOException
  {
    return ((readU1() << 24) |
            (readU1() << 16) |
            (readU1() << 8)  |
             readU1());
  }

  public int readS1()
  throws IOException
  {
    return readS(1);
  }

  public int readS2()
  throws IOException
  {
    return readS(2);
  }

  public int readS3()
  throws IOException
  {
    return readS(3);
  }

  public int readS4()
  throws IOException
  {
    return readS(4);
  }

  public void skip(int len)
  throws IOException
  {
    in.seek(in.getFilePointer() + len-1);
    // TODO: use in.skipBytes();
    offset += len - 1;
    if (!ready()) {
      throw new EOFException();
    }
    readU1();
  }
}
