package gov.nasa.gsfc.spdf.cdfj;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import java.util.zip.*;
final class CDF2Impl extends CDFImpl implements CDF2, java.io.Serializable {
    public int GDROffset;
    public int VAR_OFFSET_NAME;
    public int OFFSET_zNumDims;
    public int VAR_OFFSET_NUM_ELEMENTS;
    public int OFFSET_NUM;
    FileChannel fc;
    public CDF2Impl(ByteBuffer buf, int release, FileChannel  ch) throws
        Throwable {
        super(buf);
        fc = ch;
        if (release < 5) {
            VAR_OFFSET_NAME = 192;
            VAR_OFFSET_NUM_ELEMENTS = 48 + 128;
            OFFSET_NUM = 52 + 128;
        } else {
            VAR_OFFSET_NAME = 64;
            VAR_OFFSET_NUM_ELEMENTS = 48;
            OFFSET_NUM = 52;
        }
        OFFSET_zNumDims = VAR_OFFSET_NAME + MAX_STRING_SIZE;
        setOffsets();
        thisCDF = this;
        IntBuffer ibuf = buf.asIntBuffer();
        ByteBuffer _buf = getRecord(0);
        ibuf.position(2);
        int recordSize = ibuf.get();
        ibuf.position(3);
        int recordType = ibuf.get();
        GDROffset = ibuf.get();
        version = ibuf.get();
        if (version != CDF_VERSION) {
            throw new Throwable("Version " + version +
            "is not accepted by this reader.");
        }
        release = ibuf.get();
        encoding = ibuf.get();
        byteOrder = DataTypes.getByteOrder(encoding);
        setByteOrder(byteOrder);
        flags = ibuf.get();
        ibuf.get();
        ibuf.get();
        increment = ibuf.get();
        // validate and extract GDR info
        int pos = GDROffset + 4;
        buf.position(pos);
        int x;
        if ((x = buf.getInt()) != GDR_RECORD) {
            throw new Throwable("Bad GDR type " + x);
        }
        rVDRHead = buf.getInt();
        zVDRHead = buf.getInt();
        ADRHead = buf.getInt();
        int CDFSize = buf.getInt();
        numberOfRVariables = buf.getInt();
        numberOfAttributes = buf.getInt();
        buf.getInt(); // skip rMaxRec
        int numberOfRDims = buf.getInt();
        numberOfZVariables = buf.getInt();
        buf.getInt(); // skip UIRhead
        rDimSizes = new int[numberOfRDims];
        if (numberOfRDims > 0) { // skip next 3 integer fields
            buf.getInt();
            buf.getInt();
            buf.getInt();
            for (int i = 0; i < rDimSizes.length; i++) {
                rDimSizes[i] = buf.getInt();
            }
        }
        buf.position(0);
        variableTable = variables();
        attributeTable = attributes();
    }
    protected CDF2Impl(ByteBuffer buf, int release) throws Throwable {
        this(buf, release, null);
    }

    void setOffsets() {
        offset_NEXT_VDR = OFFSET_NEXT_VDR;
        offset_NEXT_ADR = OFFSET_NEXT_ADR;
        offset_ATTR_NAME = ATTR_OFFSET_NAME;
        offset_SCOPE = OFFSET_SCOPE;
        offset_AgrEDRHead = AgrEDRHead_OFFSET;
        offset_AzEDRHead = AzEDRHead_OFFSET;
        offset_NEXT_AEDR = OFFSET_NEXT_AEDR;
        offset_ENTRYNUM = OFFSET_ENTRYNUM;
        offset_ATTR_DATATYPE = ATTR_OFFSET_DATATYPE;
        offset_ATTR_NUM_ELEMENTS = ATTR_OFFSET_NUM_ELEMENTS;
        offset_VALUE = OFFSET_VALUE;
        offset_VAR_NAME = VAR_OFFSET_NAME;
        offset_VAR_NUM_ELEMENTS = VAR_OFFSET_NUM_ELEMENTS;
        offset_NUM = OFFSET_NUM;
        offset_FLAGS = OFFSET_FLAGS;
        offset_sRecords = OFFSET_sRecords;
        offset_BLOCKING_FACTOR = OFFSET_BLOCKING_FACTOR;
        offset_VAR_DATATYPE = VAR_OFFSET_DATATYPE;
        offset_zNumDims = OFFSET_zNumDims;
        offset_FIRST_VXR = OFFSET_FIRST_VXR;
        offset_NEXT_VXR = OFFSET_NEXT_VXR;
        offset_NENTRIES = OFFSET_NENTRIES;
        offset_NUSED = OFFSET_NUSED;
        offset_FIRST = OFFSET_FIRST;
        offset_RECORD_TYPE =  OFFSET_RECORD_TYPE;
        offset_RECORDS = OFFSET_RECORDS;
        offset_CSIZE = OFFSET_CSIZE;
        offset_CDATA = OFFSET_CDATA;
    }

    public String getString(long offset)  {
        if (fc == null) return getString(offset, MAX_STRING_SIZE);
        ByteBuffer _buf;
        try {
            _buf = getRecord(offset, MAX_STRING_SIZE);
        } catch (Throwable th) {
            th.printStackTrace();
            return null;
        }
        return getString(_buf, MAX_STRING_SIZE);
    }

    public int lowOrderInt(ByteBuffer buf) {
        return buf.getInt();
    }

    public int lowOrderInt(ByteBuffer buf, int offset) {
        return buf.getInt(offset);
    }

    protected ByteBuffer getRecord(long offset)  {
        if (fc == null) return super.getRecord(offset);
        ByteBuffer lenBuf = ByteBuffer.allocate(4);
        synchronized (fc) {
            try {
                fc.position((long)offset + 4);
                fc.read(lenBuf);
                int size = lenBuf.getInt(0);
                return getRecord(offset, size);
            } catch (Throwable ex) {
                ex.printStackTrace();
                return null;
            }
        }
    }

    protected ByteBuffer getRecord(long offset, int size) throws Throwable {
        ByteBuffer bb = ByteBuffer.allocate(size);
        fc.position((long)offset);
        int got = fc.read(bb);
        if (got != size) {
            System.out.println("Needed " + size + " bytes. Got " + got);
            return null;
        }
        bb.position(0);
        return bb;
    }

    public long longInt(ByteBuffer buf) {
        return (long)buf.getInt();
    }
}