/*
    Author: Josepth, Jun
    Date: 03/13/2020
    Description: Starting block after superblock.
    Includes 12 blocks, the starting 11s are direct and
    the last one is indirect lbok.
    16 inodes stored in one block.
    each inode size is 32 byte
*/

public class Inode {
    private final static int iNodeSize = 32;
    public final static int directSize = 11;

    // 16 inodes can be stored in 1 block
    // private final static int perBlockSize = 16;
    // 16 inodes in one block 512 (data retrieved from disk.java)
    private final static int diskBlockSize = 512;

    public int length;
    public short count;
    public short flag;  // 0 = unused, 1 = used ...
    public short direct[] = new short[directSize];
    public short indirect;

    public final static short UNUSED = 0;
    public final static short USED = 1;
    public final static short READ = 2;
    public final static short WRITE = 3;
    public final static short DELETE = 4;

    // make all direct and indirect block to -1
    Inode() {
        length = 0;
        count = 0;
        flag = 1;
        for (int i = 0; i < directSize; i++) {
            direct[i] = -1;
        }
        indirect = -1;
    }
    // retrieve inode from the disk based on iNumber(index)
    Inode(short iNumber) {
        // basic case
        // if (iNumber < 0) {
        //     return;
        // }
        // get block number
        int blockNumber = iNumber / 16 + 1;
        int offset = (iNumber % 16* 32;
        byte[] data = new byte[diskBlockSize];
        // read from disk
        SysLib.rawread(blockNumber, data);

        
        length = SysLib.bytes2int(data, offset);
        // int byte size
        offset += 4;
        count = SysLib.bytes2short(data, offset);
        // short byte size
        offset += 2;
        flag = SysLib.bytes2short(data, offset);
        offset += 2;

        // assign direct[i]
        for (int i = 0; i < directSize; i++) {
            direct[i] = SysLib.bytes2short(data, offset);
            offset += 2;
        }
        // assign indirect 12th
        indirect = SysLib.bytes2short(data, offset);
        offset += 2;
    }
    // save to disk based on index param must converted to bytes
    // require inode number
    int toDisk(short iNumber) {
        // basic case
        // if (iNumber < 0) {
        //     return -1;
        // }
        byte[] data = new byte[iNodeSize];
        int offset = 0;

        SysLib.int2bytes(length, data, offset);
        // integer size
        offset += 4
        SysLib.short2bytes(count, data, offset);
        // short byte
        offset += 2;
        SysLib.short2bytes(flag, data, offset);
        offset += 2;
        
        // moved offset += 2 from loop
        // removed (short)
        for (int i = 0; i < directSize; i++) {
            SysLib.short2bytes(direct[i], data, offset);
            offset+=2;
        }
        // 12th indirect
        SysLib.short2bytes(indirect, data, offset);



        offset += 2;
        int blockNumber = iNumber / 16 + 1;
        byte[] newByteData = new byte[diskBlockSize];

        SysLib.rawread(blockNumber, newByteData);
        offset = (iNumber % 16* 32;

        // copy source, source-loc, copyto, copy-loc len
        System.arraycopy(data, 0, newByteData, offset, iNodeSize);
        SysLib.rawwrite(blockNumber, newByteData);

        return 0;
    }

    int getTargetBlock(int taregetBlock) {
        int block = taregetBlock / diskBlockSize;

        if (block < directSize)
            return direct[block];

        if (indirect < 0)
            return -1;

        byte[] blockBytes = new byte[diskBlockSize];
        SysLib.rawread(indirect, blockBytes);
        int blockOffset = (block - directSize) * 2;

        return SysLib.bytes2short(blockBytes, blockOffset);
    }

    int getIndexBlock(int entryshort offset) {
        int blockNum = entry / diskBlockSize;

        if (blockNum < directSize) {
            if (direct[blockNum] >= 0)
                return -1;

            if (blockNum > 0 && direct[blockNum - 1== -1)
                return -2;

            direct[blockNum] = offset;
            return 0;
        }

        if (indirect < 0)
            return -3;

        else {
            byte[] byteArr = new byte[diskBlockSize];
            SysLib.rawread(indirect, byteArr);

            int blockSize = (blockNum - directSize) * 2;
            if (SysLib.bytes2short(byteArr, blockSize) > 0)
                return -1;
            else {
                SysLib.short2bytes(offset, byteArr, blockSize);
                SysLib.rawwrite(indirect, byteArr);
            }
        }
        return 0;
    }

    boolean setIndexBlock(short blockNum) {
        for (int i = 0; i < directSize; i++) {
            if (direct[i] == -1)
                return false;
        }

        if (indirect != -1)
            return false;

        indirect = blockNum;
        byte[] byteArr = new byte[diskBlockSize];

        for (int i = 0; i < (diskBlockSize / 2); i++)
            SysLib.short2bytes((short)-1, byteArr, i * 2);

        SysLib.rawwrite(blockNum, byteArr);
        return true;
    }

    byte[] freeIndexBlock(){
        if (indirect >= 0) {
            byte[] byteArr = new byte[diskBlockSize];
            SysLib.rawread(indirect, byteArr);
            indirect = -1;
            return byteArr;
        }
        else
            return null;
    }
}