#ifndef _NSRNODE_ //-*-c++-*-
#define _NSRNODE_

//----------------------------------------------------------------------
//  nsrNode.h
//   Nodes - an encapsulation of ICB FileEntries and AEDs.
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//
//  CLASSES:
//   Node
//  
//----------------------------------------------------------------------
// $Source: /projects/HMS/nsr/udfCore/ss/nsrNode.h,v $
// $Revision: 1.51 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/06/21 23:37:58 $

//----------------------------------------------------------------------
//  CLASS:  Node                                --CSB
//    Our version of a File Entry
//
//  DATA MEMBERS:
//    FileEntry       fileEntry_;
//    LogExtentList   dataEL_;
//    LogExtentList   reserveLELSR_;
//    ByteArray       embeddedData_;
//    ByteArray       embeddedEASpace_;
//
//  PUBLIC METHODS:
//
// Node(), ~Node()
//    The defaults are provided, but we may want to have a separate
//    constructor for directory nodes. Note that the destructor is
//    virtual, to allow for inheritance.
// clear()
// operator!=()
// dataEL()
//    This allows access to the LogExtentList associated with the data
//    space of the Node. It corresponds to the body of the file.
//    NOTE: dataEL_ is the sole holder of informationLength and
//    recordedBlocks, in that if these are modified, the infoLength()
//    and recordedBlocks() methods will return different values
//    accordingly. Also, the values placed in the FileEntry are
//    generated from the dataEL.
// reserveLELSR()
//    This allows access to the preallocated space associated with the
//    file which is not in its data space. It is in the form of an
//    LELSpaceResource. This corresponds to the tail of the Allocation
//    Descriptors for the Node.
// embeddedData()
//    This allows access to any data to be put inside the file entry
//    in lieu of the allocation descriptors. If this is non-empty and
//    the dataEL or reserveEL are non-empty, an error will occur in
//    toDisk().
// embeddedEASpace()
//    This allows access to the space inside of the FileEntry which
//    holds Extended Attributes.
// EANodeAddr()
//    This allows access to the extent holding the location of the
//    Node describing the space for non-embedded EAs associated with
//    this Node.
// toDisk(), fromDisk()
//    These do the translation to and from NSR. Note the following:
//    - Extent lists are properly handled, including the use of AEDs.
//    - If AEDs take up more or less space than when the node was
//      fromDisk()ed, more is allocated or some is freed via the
//      spaceResource passed into toDisk().
// diskSpace()
//    This returns an extent list describing the space used by all any
//    AEDs associated with the Node. It is provided so that the entire
//    Node may be freed if desired. NOTE --- This space does NOT
//    include the space associated with the dataEL and reserveEL
//    extent lists.  These must be freed separately.  This is the
//    space taken up by all the descriptors that this node uses except
//    for the space used by the Node itself.
// uniqueID()
//    This allows access to the uniqueID field of the fileEntry. The
//    maintenance of this information is entirely up to the user of
//    the Node.
// infoLen(), recordedBlocks()
//    These are provided to allow (read-only) access to the size of
//    the file described by the node. Note that the extent list must
//    be changed to affect these values.
// descriptorBytesAvailable()
//    returns the number of additional bytes which may be used in the
//    FileEntry, given the current embedded data and embeddedEASpace.
// attributes(), attributes()
//    These are provided to map an Attributes structure in and out
//    of the FileEntry fields.
// markDirectory(), isDirectory()
//    These allow determination and specification of whether the node
//    describes a file or a directory.
// print(), printVerbose()
//    Print out a node.
//
//  NOTES:  (E.g., special cases, heads-up, and algo discussion) 
//
//----------------------------------------------------------------------

class Node {
    // The Node class must store an internal state for the type
    //   of AD (or embedded data) that it thinks it is writing.
    ADType adType_;

    // NOTE: This class does not use a FileEntry for tracking internal
    //   state, partially because we are reliant on external paramters
    //   to determine our state (blockSize, ADType).  Thus, we manage
    //   our state of EAs, ADs, and embedded data, and use FileEntry to
    //   map ourselves (write ourselves) to the surface.
    UINT64          uniqueID_;

    // We track the attributes for this node with an Attributes class.
    //   These attributes are populated from our descriptor (FileEntry)
    //   on to/from disk operations.
    Attributes attributes_;

    LogExtent eaNodeAddr_;  // Location of our EA Node.

    // Extent list to track sectors allocated for AEDs when we read this
    //   node in fromDisk().  These are available for a later write, if
    //   we need to overflow ADs into AEDs.
    LogExtentList    metaDataEL_;   
    LogExtentList    dataEL_;
    LELSpaceResource reserveLELSR_;
    ByteArray        embeddedData_;
    ByteArray        embeddedEASpace_;

private:
    // Populate all of ourselves.
    Error populate_(LogExtent& logExtent, FileEntry& fileEntry);
    // Populate our extent lists (dataEL_, reserveLELSR_, metaDataEL_)
    //    Error populateELs_(LogExtent& logExtent, FileEntry& fileEntry);
    // Populate the one extent list passed in, appending to metaDataEL_.
    //    Error populateEL_(LogExtent& logExtent, LogExtentList& list, const UINT64& infoLen, 
    //      ADTable& beginADTable, UINT32& currentIndex, AllocExtentDesc& lefotverAED);
    // Although we pass the list into this function, it cannot be static becuase
    //   we are still populating the metaDataEL_ with it.
    //    Error recursivelyAppendADsToEL_(LogExtent& logExtent, 
    //      LogExtentList& list, 
    //      const UINT64& infoLen, const ADTable& beginADTable, UINT32& currentIndex, 
    //      AllocExtentDesc& lefotverAED);
    //    UINT32 writeOverflowAEDs_(UINT32 adsInFileEntry, UINT32 fileEntryBlock, 
    //      SpaceResource& space, Error& err);
    // Populate the entire ADTable (from ADTable index 0 to however many
    //   it is already sized to hold) beginning at the "joinedIndex" specified.
    //    Error populateADTable_(ADTable& adTable, UINT32 joinedIndex);
    // Set the AD reference passed in to a reference to an overflow
    //   AED.
    //    void setContinuationAD_(SpaceResource& space, ADTable& adTable, 
    //                    UINT32 adTableIndex,
    //                        UINT32 metaDataELIndex, Error& err);

    // Free all the meta-data blocks at this index and after.
    void freeUnusedMetaDataExtents_(SpaceResource& space, UINT32 numAEDsWritten, Error& err);
    
    // The following functions are neccessary so we can iterate through our
    //   dataEL_ and reserveLELSR_ as if they were one big EL.  They cannot
    //   be in the same ExtentList, however, because the last extent in the
    //   dataEL_ may not end on a block boundary, and that cannot be internal
    //   to an ExtentList.
    // These iterators are used to write ADs toDisk() (with overflow AEDs).
    // Return the number of ADs in both the dataEL_ and reserveLELSR_.
    UINT32 numDataAndReserveExtents_(void) const
    { return dataEL_.numExtents() + reserveLELSR_.numLogExtents(); }

    // Return the next available sector on the surface that we can use
    //   for metadata.  NOTE:  If we don't have the sector left over from
    //   the read, then we must allocate the sector from the space resource,
    //   assign it to our list, and return that.
    // NOTE:  If the space resource won't give us another extent, then
    //   return NULL.
    //    LogExtent* getMetaDataBlock_(SpaceResource& space, UINT32 metaDataELIndex,
    //      Error& err);

    // !!! Nobody calls this anymore.
    //LogExtent* getDataOrReserveLogExtent_(UINT32 joinedIndex, Error& err);


public:
    Node(LogicalVolume& logicalVolume)
        :   metaDataEL_(logicalVolume),
            dataEL_(logicalVolume),
            reserveLELSR_(logicalVolume)
    { adType_ = ad_Embedded; }
    virtual ~Node() {}

    // Copy constructor.
    Node(const Node& node) :
            metaDataEL_(node.metaDataEL_.logicalVolume()),
            dataEL_(node.dataEL_.logicalVolume()),
            reserveLELSR_(node.reserveLELSR_.logVolume())
    {
        operator=(node);
    }
    // Make a deep (clone) assignment.
    Node& operator=(const Node& node) {
        uniqueID_        = node.uniqueID_;
        attributes_      = node.attributes_;
        metaDataEL_      = node.metaDataEL_;
        dataEL_          = node.dataEL_;
        reserveLELSR_    = node.reserveLELSR_;
        embeddedData_    = node.embeddedData_;
        embeddedEASpace_ = node.embeddedEASpace_;

        return *this;
    }

    void clear();

    void adType(ADType newType) { adType_ = newType; }
    ADType adType(void) { return adType_; }

    LogExtentList& dataEL() { return dataEL_; }
    const LogExtentList& dataEL() const { return dataEL_; }

    LELSpaceResource& reserveLELSR(void) { return reserveLELSR_; }
    const LELSpaceResource& reserveLELSR(void) const { return reserveLELSR_; }
    
    ByteArray& embeddedData(void) { return embeddedData_; }
    const ByteArray& embeddedData(void) const { return embeddedData_; }

    // Set our embeddedEASpace_ to the byte array passed in.
    // NOTE:  The ByteArray passed in MUST be a four-byte multiple
    //   for the following reasons:
    //   - The ISO13346 standard mandates the EA space MUST be a four 
    //     byte multiple, (whereas individual EAs only SHOULD be a 
    //     four byte multiple).
    //   - We could pad this array to a four-byte boundary, but
    //     then the caller would not know how many wasted bytes
    //     were used in the padding.  Future calls to bytesAvailable()
    //     would also not know how many wasted padding bytes we added.
    //   - Symetry would fail:  The caller would pass us embeddedEASpace
    //     that we would write and read back from disk.  The returned
    //     space would be different (padded to a four byte boundary)
    //     from the one we were told to store.  This could fail the
    //     caller's assumptions or verification logic.
    // The lower-level FileEntry forces this four-byte multiple
    //   restriction, and we are propagating that restriction up to the
    //   caller.
    // Return 
    //   error_Invalid_EA_length          If the new byte array is not a 
    //                                    four-byte multiple.
    //   error_BadParameter_Byte_Array    If the input byte array is in
    //                                    an error state.
    ByteArray& embeddedEASpace(void) { return embeddedEASpace_; }
    const ByteArray& embeddedEASpace(void) const { return embeddedEASpace_; }

    const Attributes& attributes() const { return attributes_; }
    Attributes& attributes() { return attributes_; }

    LogExtent& eaNodeAddr(void) { return eaNodeAddr_; }      
    const LogExtent& eaNodeAddr(void) const { return eaNodeAddr_; }      

    // This function is typically for debugging only.  It returns
    //   a string representation of this Node's state.
    String getState(void) const {
        // Return a string with the current state of this node object.

        // Make a string to return.
        String state;

        CHAR buf[80];
        sprintf(buf, "Node State:  uniqueID: %lu, ", uniqueID_);
        //printf("buf is %s\n", buf);
        state += buf;

        sprintf(buf, "adType_: %lu, ", (UINT32)adType_);
        state += buf;

        sprintf(buf, "eaNodeAddr_: %lu, ", eaNodeAddr_.startBlock());
        state += buf;

        // !!! Do we want attributes too?

        Error err;

        // Add in the embeddedEASpace_.
        sprintf(buf, "embeddedEASpace_:(%ld) ", embeddedEASpace_.length());
        state += buf;

        // Add in the number and sizes of extent lists.
        state += "metaDataEL: ";
        UINT32 numExtents = metaDataEL_.numLogExtents();

        sprintf(buf, " (%ld), infoLen (%ld)", numExtents, metaDataEL_.infoLen());
        state += buf;

        UINT32 i;
        for(i=0; i< numExtents; i++)
        { // For each extent in this list.
            // Add on the start block and infolen.
            sprintf(buf, " %ld:%ld ", 
                    metaDataEL_.at(i, err)->startBlock(),
                    metaDataEL_.at(i, err)->infoLen());
            state += buf;
        }
        // Add in the number and sizes of extent lists.
        sprintf(buf, "\ndataEL_: (%ld), infoLen (%ld) ",
                dataEL_.numLogExtents(),
                dataEL_.infoLen());
        state += buf;
        for(i=0; i< dataEL_.numLogExtents(); i++) {
            // For each extent in this list.
            // Add on the start block and infolen.
            sprintf(buf, " %ld:%ld ", 
                    dataEL_.at(i, err)->startBlock(),
                    dataEL_.at(i, err)->infoLen());
            state += buf;
        }
        // Add in the number and sizes of extent lists.
        sprintf(buf, "\nreserveLELSR_: (%ld), infoLen (%ld) ", reserveLELSR_.numLogExtents(),
                reserveLELSR_.availBytes());
        state += buf;
        for(i=0; i< reserveLELSR_.numLogExtents(); i++) {
            // For each extent in this list.
            // Add on the start block and infolen.
            sprintf(buf, " %ld:%ld ", 
                    reserveLELSR_.at_(i, err)->startBlock(),
                    reserveLELSR_.at_(i, err)->infoLen());
            state += buf;
        }
        // Add in the byte array.
        state += "\nembeddedData_: ";
        sprintf(buf, " %ld ", embeddedData_.length());
        state += buf;
        if (embeddedData_.length()) {
            sprintf(buf," [");
            state += buf;
            UINT32 displen = (embeddedData_.length()>10?10:embeddedData_.length());
            for (i=0; i< displen; i++){
                sprintf(buf, "%c",((CONST_BYTE_PTR)embeddedData_)[i]);
                state += buf;
            }
            sprintf(buf,"]");
            state += buf;
        }

        // !!! INSERT EAList here!
        return state;
    }


    void toDisk(   LogExtent & ext,
                   SpaceResource & space, 
                   Error & err);

    void fromDisk( LogExtent & ext,
                   Error & err );

    const UINT64 uniqueID() const {return uniqueID_;}
    void uniqueID( UINT64 uid )   {uniqueID_ = uid;}   
    const UINT64 infoLen() const  {return dataEL_.infoLen();}  

    // Note: The following SHOULD be const but can't be, because we are calling
    //   members of FileEntry, which does not accept our data members as
    //   "const" objects. 
    const INT32 descriptorBytesAvailable(const LogExtent& ext) {
        return descriptorBytesAvailable(ext.blockSize());
    }
    const INT32 descriptorBytesAvailable(UINT32 blockSize) ;
    // Return "true" if this node can successfully be written to the surface
    //   (the embedded data and ADs fit into the descriptor).

    // NOTE: This "error()" function checks ONLY that our interanl
    //   data members are NOT in an error state.  It DOES NOT check
    //   any logical data dependencies imposed by the ISO (UDF)
    //   standard (those are in ISOError()).
    // Confirm none of our members are in an error state.
    Error error(void) const {
        // Make sure none of our data members are in error state.
        Error err;
        if(!(err = eaNodeAddr_.error()))
            // If we don't have an error.
            if(!(err = metaDataEL_.error()))
            // If we don't have an error.
                if(!(err = dataEL_.error()))
                // If we don't have an error.
                    if(!(err = reserveLELSR_.error()))
                    // If we don't have an error.
                        if(!(err = embeddedData_.error()))
                        // If we don't have an error.
                            err = embeddedEASpace_.error();
        return err;
    }

    // NOTE: This "ISOError()" function calls "error()" first to
    //   confirm we do not have any internal data members in an
    //   internal error state.  Then, it additionally makes verification
    //   checks that all our logical data conforms to an internal
    //   Node state that is allowed by the ISO (UDF) standard.
    Error ISOError(const LogExtent& ext) {
        // Return false if the Node can't successfully be written to the 
        //   descriptor.
        Error returnVal = error_None;

        // First, we want to call the other error() function and
        //   confirm we don't have any members in an error state.
        returnVal = error();
        if(returnVal)

        // Confirm we have a multiple of four on our EASpace.
        if(embeddedEASpace_.length() %4) {
            // If the parameter is not on a four-byte boundary.
            returnVal = error_EA_Invalid_EA_length;
        }

        if(descriptorBytesAvailable(ext) < 0) {
            // If we don't have enough room in our one block FileEntry
            //   descriptor for minimal data, then we have a problem.
            returnVal = error_NODE_Descriptor_Overflow;
        }
        if((adType_ != ad_Embedded) && embeddedData_.length()) {
            // If we have embedded data but are not scheduled to
            //   write this data.
            returnVal = error_NODE_Embedded_Data_Set;
        }
        if((adType_ == ad_Embedded) && numDataAndReserveExtents_()) {
            // If we have extents (ADs) but are scheduled to write
            //   embedded data.
            returnVal = error_NODE_ADs_Set;
        }

        if(attributes_.fileType() != file_Directory) {
            // If we are not a directory, then we cannot have a unique id
            //   of zero.  NOTE:  Only the ROOT directory can
            //   have a unique id of zero.
            if(! uniqueID())
              returnVal = error_NODE_No_Unique_ID; // Must be greater than zero.
        }

        // At this point, the node is (grossly) correct.  We want to confirm
        //   some state issues for the EANode case ONLY!
        if(attributes_.fileType() == file_ExtendedAttributes) {   
            // If this is an EA node.

            if(eaNodeAddr_.startBlock() || eaNodeAddr_.infoLen())  // We can't have this.
                returnVal = error_EANODE_EANodeAddr_Set;

            if(embeddedEASpace_.length())    // We can't have this.
                returnVal = error_EANODE_Embedded_EASpace_Set;
         
            if(attributes_.linkCount())
                returnVal = error_EANODE_Linkcount_Set; // We can't have this.
        }

        return returnVal;
    }

    // Return a LogExtentList that refers to all disk space we ahve
    //   set aside for AED metadata so the caller can free this space
    //   when destroying the entire node (deleting the node).
    LogExtentList& diskSpace() { return metaDataEL_; }

    Boolean operator!=( const Node & other );
    Boolean operator==( const Node & other ) {
        return ( *this != other) ? False:True;
    }

    void print(UINT32 indent, const String & header = "") ;
    void printVerbose(UINT32 indent, const String & header = "") ;

};  // class Node

#endif
