/*-
 * Copyright (C) 2006 Erik Larsson
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.catacombae.hfs.types.hfsplus;

import org.catacombae.csjc.structelements.Dictionary;
import org.catacombae.hfs.types.finder.ExtendedFolderInfo;
import org.catacombae.hfs.types.finder.FolderInfo;
import org.catacombae.util.Util;
import java.io.PrintStream;
import java.util.Date;
import org.catacombae.csjc.StructElements;
import org.catacombae.csjc.structelements.IntegerFieldRepresentation;

/**
 * This class was generated by CStructToJavaClass.
 *
 * @author <a href="https://catacombae.org" target="_top">Erik Larsson</a>
 */
public class HFSPlusCatalogFolder extends HFSPlusCatalogLeafRecordData implements HFSPlusCatalogAttributes, StructElements {
    /*
     * struct HFSPlusCatalogFolder
     * size: 88 bytes
     * description:
     *
     * BP  Size  Type                Identifier        Description
     * -----------------------------------------------------------
     * 0   2     SInt16              recordType
     * 2   2     UInt16              flags
     * 4   4     UInt32              valence
     * 8   4     HFSCatalogNodeID    folderID
     * 12  4     UInt32              createDate
     * 16  4     UInt32              contentModDate
     * 20  4     UInt32              attributeModDate
     * 24  4     UInt32              accessDate
     * 28  4     UInt32              backupDate
     * 32  16    HFSPlusBSDInfo      permissions
     * 48  16    FolderInfo          userInfo
     * 64  16    ExtendedFolderInfo  finderInfo
     * 80  4     UInt32              textEncoding
     * 84  4     UInt32              reserved
     */

    private final byte[] recordType = new byte[2];
    private final byte[] flags = new byte[2];
    private final byte[] valence = new byte[4];
    private final HFSCatalogNodeID folderID;
    private final byte[] createDate = new byte[4];
    private final byte[] contentModDate = new byte[4];
    private final byte[] attributeModDate = new byte[4];
    private final byte[] accessDate = new byte[4];
    private final byte[] backupDate = new byte[4];
    private final HFSPlusBSDInfo permissions;
    private final FolderInfo userInfo;
    private final ExtendedFolderInfo finderInfo;
    private final byte[] textEncoding = new byte[4];
    private final byte[] reserved = new byte[4];

    public HFSPlusCatalogFolder(byte[] data, int offset) {
	System.arraycopy(data, offset+0, recordType, 0, 2);
	System.arraycopy(data, offset+2, flags, 0, 2);
	System.arraycopy(data, offset+4, valence, 0, 4);
	folderID = new HFSCatalogNodeID(data, offset+8);
	System.arraycopy(data, offset+12, createDate, 0, 4);
	System.arraycopy(data, offset+16, contentModDate, 0, 4);
	System.arraycopy(data, offset+20, attributeModDate, 0, 4);
	System.arraycopy(data, offset+24, accessDate, 0, 4);
	System.arraycopy(data, offset+28, backupDate, 0, 4);
	permissions = new HFSPlusBSDInfo(data, offset+32);
	userInfo = new FolderInfo(data, offset+48);
	finderInfo = new ExtendedFolderInfo(data, offset+64);
	System.arraycopy(data, offset+80, textEncoding, 0, 4);
	System.arraycopy(data, offset+84, reserved, 0, 4);
    }

    public static int length() { return 88; }

    public short getRecordType() { return Util.readShortBE(recordType); }
    public short getFlags() { return Util.readShortBE(flags); }
    public int getValence() { return Util.readIntBE(valence); }
    public HFSCatalogNodeID getFolderID() { return folderID; }
    public int getCreateDate() { return Util.readIntBE(createDate); }
    public int getContentModDate() { return Util.readIntBE(contentModDate); }
    public int getAttributeModDate() { return Util.readIntBE(attributeModDate); }
    public int getAccessDate() { return Util.readIntBE(accessDate); }
    public int getBackupDate() { return Util.readIntBE(backupDate); }
    public HFSPlusBSDInfo getPermissions() { return permissions; }
    public FolderInfo getUserInfo() { return userInfo; }
    public ExtendedFolderInfo getFinderInfo() { return finderInfo; }
    public int getTextEncoding() { return Util.readIntBE(textEncoding); }
    public int getReserved() { return Util.readIntBE(reserved); }

    /** File is locked and cannot be written to. */
    public boolean getFileLockedFlag() {
        return (getFlags() & kHFSFileLockedMask) != 0;
    }

    /** A file thread record exists for this file. */
    public boolean getThreadExistsFlag() {
        return (getFlags() & kHFSThreadExistsMask) != 0;
    }

    /** Object has extended attributes. */
    public boolean getHasAttributesFlag() {
        return (getFlags() & kHFSHasAttributesMask) != 0;
    }

    /** Object has security data (ACLs). */
    public boolean getHasSecurityMaskFlag() {
        return (getFlags() & kHFSHasSecurityMask) != 0;
    }

    /**
     * Only for HFSX, folder maintains a separate sub-folder count (sum of
     * folder records and directory hard links).
     */
    public boolean getHasFolderCountFlag() {
        return (getFlags() & kHFSHasFolderCountMask) != 0;
    }

    /** Has hardlink chain (inode or link). */
    public boolean getHasLinkChainFlag() {
        return (getFlags() & kHFSHasLinkChainMask) != 0;
    }

    /** Folder has a child that's a dir link. */
    public boolean getHasChildLinkFlag() {
        return (getFlags() & kHFSHasChildLinkMask) != 0;
    }

    /** File/Folder has the date-added stored in the finder info. */
    public boolean getHasDateAddedFlag() {
        return (getFlags() & kHFSHasDateAddedMask) != 0;
    }

    public Date getCreateDateAsDate() { return HFSPlusDate.gmtTimestampToDate(getCreateDate()); }
    public Date getContentModDateAsDate() { return HFSPlusDate.gmtTimestampToDate(getContentModDate()); }
    public Date getAttributeModDateAsDate() { return HFSPlusDate.gmtTimestampToDate(getAttributeModDate()); }
    public Date getAccessDateAsDate() { return HFSPlusDate.gmtTimestampToDate(getAccessDate()); }
    public Date getBackupDateAsDate() { return HFSPlusDate.gmtTimestampToDate(getBackupDate()); }

    public void printFields(PrintStream ps, String prefix) {
	ps.println(prefix + " recordType: " + getRecordType());
	ps.println(prefix + " flags: " + getFlags());
	ps.println(prefix + " valence: " + getValence());
	ps.println(prefix + " folderID: ");
	getFolderID().print(ps, prefix+"  ");
	ps.println(prefix + " createDate: " + getCreateDateAsDate());
	ps.println(prefix + " contentModDate: " + getContentModDateAsDate());
	ps.println(prefix + " attributeModDate: " + getAttributeModDateAsDate());
	ps.println(prefix + " accessDate: " + getAccessDateAsDate());
	ps.println(prefix + " backupDate: " + getBackupDateAsDate());
	ps.println(prefix + " permissions: ");
	getPermissions().print(ps, prefix+"  ");
	ps.println(prefix + " userInfo: ");
	getUserInfo().print(ps, prefix+"  ");
	ps.println(prefix + " finderInfo: ");
	getFinderInfo().print(ps, prefix+"  ");
	ps.println(prefix + " textEncoding: " + getTextEncoding());
	ps.println(prefix + " reserved: " + getReserved());
    }

    public void print(PrintStream ps, String prefix) {
	ps.println(prefix + "HFSPlusCatalogFolder:");
	printFields(ps, prefix);
    }

    public byte[] getBytes() {
	byte[] result = new byte[length()];
	byte[] tempData;
	int offset = 0;

	System.arraycopy(recordType, 0, result, offset, recordType.length); offset += recordType.length;
	System.arraycopy(flags, 0, result, offset, flags.length); offset += flags.length;
	System.arraycopy(valence, 0, result, offset, valence.length); offset += valence.length;
        tempData = folderID.getBytes();
        System.arraycopy(tempData, 0, result, offset, tempData.length); offset += tempData.length;
	System.arraycopy(createDate, 0, result, offset, createDate.length); offset += createDate.length;
	System.arraycopy(contentModDate, 0, result, offset, contentModDate.length); offset += contentModDate.length;
	System.arraycopy(attributeModDate, 0, result, offset, attributeModDate.length); offset += attributeModDate.length;
	System.arraycopy(accessDate, 0, result, offset, accessDate.length); offset += accessDate.length;
	System.arraycopy(backupDate, 0, result, offset, backupDate.length); offset += backupDate.length;
        tempData = permissions.getBytes();
        System.arraycopy(tempData, 0, result, offset, tempData.length); offset += tempData.length;
        tempData = userInfo.getBytes();
        System.arraycopy(tempData, 0, result, offset, tempData.length); offset += tempData.length;
        tempData = finderInfo.getBytes();
        System.arraycopy(tempData, 0, result, offset, tempData.length); offset += tempData.length;
        System.arraycopy(textEncoding, 0, result, offset, textEncoding.length); offset += textEncoding.length;
	System.arraycopy(reserved, 0, result, offset, reserved.length); offset += reserved.length;

        return result;
    }

    public Dictionary getFlagsStructElements() {
        DictionaryBuilder db = new DictionaryBuilder("<anonymous>");

        db.addFlag("kHFSFileLocked", flags, kHFSFileLockedBit, "File locked");
        db.addFlag("kHFSThreadExists", flags, kHFSThreadExistsBit,
                "Thread exists");
        db.addFlag("kHFSHasAttributes", flags, kHFSHasAttributesBit,
                "Has attributes");
        db.addFlag("kHFSHasSecurity", flags, kHFSHasSecurityBit,
                "Has security");
        db.addFlag("kHFSHasFolderCount", flags, kHFSHasFolderCountBit,
                "Has folder count");
        db.addFlag("kHFSHasLinkChain", flags, kHFSHasLinkChainBit,
                "Has link chain");
        db.addFlag("kHFSHasChildLink", flags, kHFSHasChildLinkBit,
                "Has child link");
        db.addFlag("kHFSHasDateAdded", flags, kHFSHasDateAddedBit,
                "Has date added");

        return db.getResult();
    }

    /* @Override */
    public Dictionary getStructElements() {
        DictionaryBuilder db = new DictionaryBuilder(HFSPlusCatalogFolder.class.getSimpleName());

        /*
         * 0   2     SInt16              recordType
         * 2   2     UInt16              flags
         * 4   4     UInt32              valence
         * 8   4     HFSCatalogNodeID    folderID
         * 12  4     UInt32              createDate
         * 16  4     UInt32              contentModDate
         * 20  4     UInt32              attributeModDate
         * 24  4     UInt32              accessDate
         * 28  4     UInt32              backupDate
         * 32  16    HFSPlusBSDInfo      permissions
         * 48  16    FolderInfo          userInfo
         * 64  16    ExtendedFolderInfo  finderInfo
         * 80  4     UInt32              textEncoding
         * 84  4     UInt32              reserved
         */
        db.addUIntBE("recordType", recordType, "Record type",
                IntegerFieldRepresentation.HEXADECIMAL);
        db.add("flags", getFlagsStructElements(), "Flags");
        db.addUIntBE("valence", valence, "Valence");
        db.add("folderID", folderID.getOpaqueStructElement(), "Folder ID");
        db.add("createDate", new HFSPlusDateField(createDate, false),
                "Created");
        db.add("contentModDate", new HFSPlusDateField(contentModDate, false),
                "Content modified");
        db.add("attributeModDate", new HFSPlusDateField(attributeModDate, false),
                "Attributes modified");
        db.add("accessDate", new HFSPlusDateField(accessDate, false),
                "Accessed");
        db.add("backupDate", new HFSPlusDateField(backupDate, false),
                "Backuped");
        db.add("permissions", permissions.getStructElements(),
                "POSIX permissions");
        db.add("userInfo", userInfo.getStructElements(), "User info");
        db.add("finderInfo", finderInfo.getStructElements(), "Finder info");
        db.addUIntBE("textEncoding", textEncoding, "Text encoding");
        db.addUIntBE("reserved", reserved, "Reserved");

        return db.getResult();
    }
}
