using System;

namespace PSVIMGTOOLS
{

    internal class PSVIMGConstants
    {
        public const int AES_BLOCK_SIZE = 0x10;
        public const int SHA256_BLOCK_SIZE = 0x20;
        public const int PSVIMG_BLOCK_SIZE = 0x8000;
        public const int PSVIMG_ENTRY_ALIGN = 0x400;

        public const string PSVIMG_HEADER_END = "EndOfHeader\n";
        public const string PSVIMG_TAILOR_END = "EndOfTailer\n";
        public const string PSVIMG_PADDING_END = "\n";

        public const int FULL_PSVIMG_SIZE = PSVIMG_BLOCK_SIZE + SHA256_BLOCK_SIZE;
    }
    
    internal class StringReader
    {
        internal static string ReadUntilTerminator(byte[] StringBytes)
        {
            string str = "";
            foreach (byte sByte in StringBytes)
            {
                if (sByte != 0x00)
                {
                    str += (char)sByte;
                }
                else
                {
                    return str;
                }
            }
            return str;
        }
    }
    
    internal class SceDateTime
    {
        public UInt16 Year;
        public UInt16 Month;
        public UInt16 Day;
        public UInt16 Hour;
        public UInt16 Minute;
        public UInt16 Second;
        public UInt32 Microsecond;

        public SceDateTime()
        {

        }
    }

    internal class SceIoStat
    {
        public enum Modes{
            /** Format bits mask */
            FormatBits = 0xF000,
            /** Symbolic link */
            SymbLink = 0x4000,
            /** Directory */
            Directory = 0x1000,
            /** Regular file */
            File = 0x2000,

            /** Set UID */
            SetUid = 0x0800,
            /** Set GID */
            SetGid = 0x0400,
            /** Sticky */
            Sticky = 0x0200,

            /** Others access rights mask */
            OthersAcesssMask = 0x01C0,
            /** Others read permission */
            OthersRead = 0x0100,
            /** Others write permission */
            OthersWrite = 0x0080,
            /** Others execute permission */
            OthersExecute = 0x0040,

            /** Group access rights mask */
            GroupAcessMask = 0x0038,
            /** Group read permission */
            GroupRead = 0x0020,
            /** Group write permission */
            GroupWrite = 0x0010,
            /** Group execute permission */
            GroupExecute = 0x0008,

            /** User access rights mask */
            UserAcessMask = 0x0007,
            /** User read permission */
            UserRead = 0x0004,
            /** User write permission */
            UserWrite = 0x0002,
            /** User execute permission */
            UserExecute = 0x0001,
        };
        public enum AttributesEnum
        {
            /** Format mask */
            FormatMask = 0x0038,               // Format mask
                                                /** Symlink */
            SymbLink = 0x0008,               // Symbolic link
                                                 /** Directory */
            Directory = 0x0010,               // Directory
                                                 /** Regular file */
            File = 0x0020,               // Regular file

            /** Hidden read permission */
            Read = 0x0004,               // read
                                                 /** Hidden write permission */
            Write = 0x0002,               // write
                                                 /** Hidden execute permission */
            Execute = 0x0001,               // execute
        };

        public Modes Mode;
        public AttributesEnum Attributes;
        /** Size of the file in bytes. */
        public UInt64 Size;
        /** Creation time. */
        public SceDateTime CreationTime;
        /** Access time. */
        public SceDateTime AccessTime;
        /** Modification time. */
        public SceDateTime ModificaionTime;
        /** Device-specific data. */
        public UInt32[] Private = new UInt32[6];
        public SceIoStat()
        {
            for(int i = 0; i < Private.Length; i++)
            {
                Private[i] = 0;
            }
        }
    };

    internal class PsvImgTailer
    {
        public UInt64 Flags;
        public Byte[] Padding = new Byte[1004];
        public Byte[] bEnd = new Byte[12];

        public String End
        {
            get
            {
                return StringReader.ReadUntilTerminator(bEnd);
            }
        }
    }
    internal class PSVIMGPadding
    {
        public static long GetPadding(long size)
        {
            long padding;
            if ((size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)) >= 1)
            {
                padding = (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - (size & (PSVIMGConstants.PSVIMG_ENTRY_ALIGN - 1)));
            }
            else
            {
                padding = 0;
            }
            return padding;
        }
    }

    internal class PsvImgHeader
    {
        public UInt64 SysTime;
        public UInt64 Flags;
        public SceIoStat Statistics;
        public Byte[] bParentPath = new Byte[256];
        public UInt32 unk_16C; // set to 1
        public Byte[] bPath = new Byte[256];
        public Byte[] Padding = new Byte[904];
        public Byte[] bEnd = new Byte[12];

        public String Path
        {
            get
            {
                return StringReader.ReadUntilTerminator(bPath);
            }
        }

        public String End
        {
            get
            {
                return StringReader.ReadUntilTerminator(bEnd);
            }
        }

        public String ParentPath
        {
            get
            {
                return StringReader.ReadUntilTerminator(bParentPath);
            }
        }
        public PsvImgHeader()
        {

        }
    }
}
