/*             ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
 *
 *                 Copyright (c) 2014-2015 Datalight, Inc.
 *                     All Rights Reserved Worldwide.
 *
 *  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; use version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but "AS-IS," 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, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

/*  Businesses and individuals that for commercial or other reasons cannot
 *  comply with the terms of the GPLv2 license may obtain a commercial license
 *  before incorporating Reliance Edge into proprietary software for
 *  distribution in any form.  Visit http://www.datalight.com/reliance-edge for
 *  more information.
 */

/** @file
 */
#ifndef REDCORE_H
#define REDCORE_H


#include <redstat.h>
#include <redvolume.h>
#include "rednodes.h"
#include "redcoremacs.h"
#include "redcorevol.h"


#define META_SIG_MASTER      ( 0x5453414DU ) /* 'MAST' */
#define META_SIG_METAROOT    ( 0x4154454DU ) /* 'META' */
#define META_SIG_IMAP        ( 0x50414D49U ) /* 'IMAP' */
#define META_SIG_INODE       ( 0x444F4E49U ) /* 'INOD' */
#define META_SIG_DINDIR      ( 0x494C4244U ) /* 'DBLI' */
#define META_SIG_INDIR       ( 0x49444E49U ) /* 'INDI' */


REDSTATUS RedIoRead( uint8_t bVolNum,
                     uint32_t ulBlockStart,
                     uint32_t ulBlockCount,
                     void * pBuffer );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedIoWrite( uint8_t bVolNum,
                          uint32_t ulBlockStart,
                          uint32_t ulBlockCount,
                          const void * pBuffer );
    REDSTATUS RedIoFlush( uint8_t bVolNum );
#endif


/** Indicates a block buffer is dirty (its contents are different than the
 *  contents of the corresponding block on disk); or, when passed into
 *  RedBufferGet(), indicates that the buffer should be marked dirty.
 */
#define BFLAG_DIRTY          ( ( uint16_t ) 0x0001U )

/** Tells RedBufferGet() that the buffer is for a newly allocated block, and its
 *  contents should be zeroed instead of being read from disk.  Always used in
 *  combination with BFLAG_DIRTY.
 */
#define BFLAG_NEW            ( ( uint16_t ) 0x0002U )

/** Indicates that a block buffer is a master block (MASTERBLOCK) metadata node.
 */
#define BFLAG_META_MASTER    ( ( uint16_t ) ( 0x0004U | BFLAG_META ) )

/** Indicates that a block buffer is an imap (IMAPNODE) metadata node.
 */
#define BFLAG_META_IMAP      ( ( uint16_t ) ( 0x0008U | BFLAG_META ) )

/** Indicates that a block buffer is an inode (INODE) metadata node.
 */
#define BFLAG_META_INODE     ( ( uint16_t ) ( 0x0010U | BFLAG_META ) )

/** Indicates that a block buffer is an indirect (INDIR) metadata node.
 */
#define BFLAG_META_INDIR     ( ( uint16_t ) ( 0x0020U | BFLAG_META ) )

/** Indicates that a block buffer is a double indirect (DINDIR) metadata node.
 */
#define BFLAG_META_DINDIR    ( ( uint16_t ) ( 0x0040U | BFLAG_META ) )

/** Indicates that a block buffer is a metadata node.  Callers of RedBufferGet()
 *  should not use this flag; instead, use one of the BFLAG_META_* flags.
 */
#define BFLAG_META           ( ( uint16_t ) 0x8000U )


void RedBufferInit( void );
REDSTATUS RedBufferGet( uint32_t ulBlock,
                        uint16_t uFlags,
                        void ** ppBuffer );
void RedBufferPut( const void * pBuffer );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedBufferFlush( uint32_t ulBlockStart,
                              uint32_t ulBlockCount );
    void RedBufferDirty( const void * pBuffer );
    void RedBufferBranch( const void * pBuffer,
                          uint32_t ulBlockNew );
    #if ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED
        void RedBufferDiscard( const void * pBuffer );
    #endif
#endif
REDSTATUS RedBufferDiscardRange( uint32_t ulBlockStart,
                                 uint32_t ulBlockCount );


/** @brief Allocation state of a block.
 */
typedef enum
{
    ALLOCSTATE_FREE, /**< Free and may be allocated; writeable. */
    ALLOCSTATE_USED, /**< In-use and transacted; not writeable. */
    ALLOCSTATE_NEW,  /**< In-use but not transacted; writeable. */
    ALLOCSTATE_AFREE /**< Will become free after a transaction; not writeable. */
} ALLOCSTATE;

REDSTATUS RedImapBlockGet( uint8_t bMR,
                           uint32_t ulBlock,
                           bool * pfAllocated );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedImapBlockSet( uint32_t ulBlock,
                               bool fAllocated );
    REDSTATUS RedImapAllocBlock( uint32_t * pulBlock );
#endif
REDSTATUS RedImapBlockState( uint32_t ulBlock,
                             ALLOCSTATE * pState );

#if REDCONF_IMAP_INLINE == 1
    REDSTATUS RedImapIBlockGet( uint8_t bMR,
                                uint32_t ulBlock,
                                bool * pfAllocated );
    REDSTATUS RedImapIBlockSet( uint32_t ulBlock,
                                bool fAllocated );
#endif

#if REDCONF_IMAP_EXTERNAL == 1
    REDSTATUS RedImapEBlockGet( uint8_t bMR,
                                uint32_t ulBlock,
                                bool * pfAllocated );
    REDSTATUS RedImapEBlockSet( uint32_t ulBlock,
                                bool fAllocated );
    uint32_t RedImapNodeBlock( uint8_t bMR,
                               uint32_t ulImapNode );
#endif


/** @brief Cached inode structure.
 */
typedef struct
{
    uint32_t ulInode;    /**< The inode number of the cached inode. */
    #if REDCONF_API_POSIX == 1
        bool fDirectory; /**< True if the inode is a directory. */
    #endif
    #if REDCONF_READ_ONLY == 0
        bool fBranched;   /**< True if the inode is branched (writeable). */
        bool fDirty;      /**< True if the inode buffer is dirty. */
    #endif
    bool fCoordInited;    /**< True after the first seek. */

    INODE * pInodeBuf;    /**< Pointer to the inode buffer. */
    #if DINDIR_POINTERS > 0U
        DINDIR * pDindir; /**< Pointer to the double indirect node buffer. */
    #endif
    #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
        INDIR * pIndir; /**< Pointer to the indirect node buffer. */
    #endif
    uint8_t * pbData;   /**< Pointer to the data block buffer. */

    /*  All the members below this point are part of the seek coordinates; see
     *  RedInodeDataSeek().
     */
    uint32_t ulLogicalBlock;    /**< Logical block offset into the inode. */
    #if DINDIR_POINTERS > 0U
        uint32_t ulDindirBlock; /**< Physical block number of the double indirect node. */
    #endif
    #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
        uint32_t ulIndirBlock; /**< Physical block number of the indirect node. */
    #endif
    uint32_t ulDataBlock;      /**< Physical block number of the file data block. */

    uint16_t uInodeEntry;      /**< Which inode entry to traverse to reach ulLogicalBlock. */
    #if DINDIR_POINTERS > 0U
        uint16_t uDindirEntry; /**< Which double indirect entry to traverse to reach ulLogicalBlock. */
    #endif
    #if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
        uint16_t uIndirEntry; /**< Which indirect entry to traverse to reach ulLogicalBlock. */
    #endif
} CINODE;

#define CINODE_IS_MOUNTED( pInode )    ( ( ( pInode ) != NULL ) && INODE_IS_VALID( ( pInode )->ulInode ) && ( ( pInode )->pInodeBuf != NULL ) )
#define CINODE_IS_DIRTY( pInode )      ( CINODE_IS_MOUNTED( pInode ) && ( pInode )->fDirty )


#define IPUT_UPDATE_ATIME    ( 0x01U )
#define IPUT_UPDATE_MTIME    ( 0x02U )
#define IPUT_UPDATE_CTIME    ( 0x04U )
#define IPUT_UPDATE_MASK     ( IPUT_UPDATE_ATIME | IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME )


REDSTATUS RedInodeMount( CINODE * pInode,
                         FTYPE type,
                         bool fBranch );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedInodeBranch( CINODE * pInode );
#endif
#if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED )
    REDSTATUS RedInodeCreate( CINODE * pInode,
                              uint32_t ulPInode,
                              uint16_t uMode );
#endif
#if DELETE_SUPPORTED
    REDSTATUS RedInodeDelete( CINODE * pInode );
    REDSTATUS RedInodeLinkDec( CINODE * pInode );
#endif
#if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
    REDSTATUS RedInodeFree( CINODE * pInode );
#endif
void RedInodePut( CINODE * pInode,
                  uint8_t bTimeFields );
void RedInodePutCoord( CINODE * pInode );
#if DINDIR_POINTERS > 0U
    void RedInodePutDindir( CINODE * pInode );
#endif
#if REDCONF_DIRECT_POINTERS < INODE_ENTRIES
    void RedInodePutIndir( CINODE * pInode );
#endif
void RedInodePutData( CINODE * pInode );
#if ( ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || FORMAT_SUPPORTED ) ) || ( REDCONF_CHECKER == 1 )
    REDSTATUS RedInodeIsFree( uint32_t ulInode,
                              bool * pfFree );
#endif
REDSTATUS RedInodeBitGet( uint8_t bMR,
                          uint32_t ulInode,
                          uint8_t bWhich,
                          bool * pfAllocated );

REDSTATUS RedInodeDataRead( CINODE * pInode,
                            uint64_t ullStart,
                            uint32_t * pulLen,
                            void * pBuffer );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedInodeDataWrite( CINODE * pInode,
                                 uint64_t ullStart,
                                 uint32_t * pulLen,
                                 const void * pBuffer );
    #if DELETE_SUPPORTED || TRUNCATE_SUPPORTED
        REDSTATUS RedInodeDataTruncate( CINODE * pInode,
                                        uint64_t ullSize );
    #endif
#endif
REDSTATUS RedInodeDataSeekAndRead( CINODE * pInode,
                                   uint32_t ulBlock );
REDSTATUS RedInodeDataSeek( CINODE * pInode,
                            uint32_t ulBlock );

#if REDCONF_API_POSIX == 1
    #if REDCONF_READ_ONLY == 0
        REDSTATUS RedDirEntryCreate( CINODE * pPInode,
                                     const char * pszName,
                                     uint32_t ulInode );
    #endif
    #if DELETE_SUPPORTED
        REDSTATUS RedDirEntryDelete( CINODE * pPInode,
                                     uint32_t ulDeleteIdx );
    #endif
    REDSTATUS RedDirEntryLookup( CINODE * pPInode,
                                 const char * pszName,
                                 uint32_t * pulEntryIdx,
                                 uint32_t * pulInode );
    #if ( REDCONF_API_POSIX_READDIR == 1 ) || ( REDCONF_CHECKER == 1 )
        REDSTATUS RedDirEntryRead( CINODE * pPInode,
                                   uint32_t * pulIdx,
                                   char * pszName,
                                   uint32_t * pulInode );
    #endif
    #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RENAME == 1 )
        REDSTATUS RedDirEntryRename( CINODE * pSrcPInode,
                                     const char * pszSrcName,
                                     CINODE * pSrcInode,
                                     CINODE * pDstPInode,
                                     const char * pszDstName,
                                     CINODE * pDstInode );
    #endif
#endif /* if REDCONF_API_POSIX == 1 */

REDSTATUS RedVolMount( void );
REDSTATUS RedVolMountMaster( void );
REDSTATUS RedVolMountMetaroot( void );
#if REDCONF_READ_ONLY == 0
    REDSTATUS RedVolTransact( void );
#endif
void RedVolCriticalError( const char * pszFileName,
                          uint32_t ulLineNum );
REDSTATUS RedVolSeqNumIncrement( void );

#if FORMAT_SUPPORTED
    REDSTATUS RedVolFormat( void );
#endif


#endif /* ifndef REDCORE_H */
