//-*-c++-*--------------------------------------------------------------
//  nsrNode.cpp
// This class contains all member functions for the Node class.
//
//      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.cpp,v $
// $Revision: 1.51 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/06/21 23:37:36 $
//

#include "nsrLib.h"

//----------------------------------------------------------------------
// METHOD: Node::print()                                        --CSB
//
//  Prints out the state of the Node.
//
// ARGS:
//  indent                  IN      Number of spaces to indent
//  header                  IN      String to print as a header title
//
// RETURNS:
//  void
// 
// PRE-CONDITIONS:
//  0 <= indent < 160
// 
// POST-CONDITIONS:
//  none
// 
// ERRORS:
// 
// NOTES:
//  Must compile with DEBUG compiler flag.
//
//  User should use getState(), and this function should be removed
//  in the future.
//
//----------------------------------------------------------------------
void 
NSR::Node::print(UINT32 indent, const String & header) {

    // Indent.
    for(UINT32 i=0; i<indent; i++)
        printf(" ");

    printf("%s: %s\n", (CONST_CHAR_PTR) header, (CONST_CHAR_PTR) getState());

} // Node::print()


//----------------------------------------------------------------------
// METHOD: Node::printVerbose()                                 --CSB
//
//  Prints out the state of the Node.
//
// ARGS:
//  indent                  IN      Number of spaces to indent
//  header                  IN      String to print as a header title
//
// RETURNS:
//  void
// 
// PRE-CONDITIONS:
//  0 <= indent < 160
// 
// POST-CONDITIONS:
//  none
// 
// ERRORS:
// 
// NOTES:
//  Must compile with DEBUG compiler flag.
//
//  User should use getState(), and this function should be removed
//  in the future.
//----------------------------------------------------------------------
void 
NSR::Node::printVerbose(UINT32 indent,
                        const String & header) {

    CHAR_PTR ind = (CHAR_PTR) NSR::space(indent);               // cast to CHAR_PTR

    // Indent.
    for(UINT32 i=0; i<indent; i++)
        printf(" ");

    printf("%s: %s\n", (CONST_CHAR_PTR) header, (CONST_CHAR_PTR) getState());

} // Node::printVerbose()

//----------------------------------------------------------------------
// METHOD: Node::populate_()                    --CSB
//
//  This is a protected member function that populates the Node state from
//    the FileEntry passed in.  (This function is for internal use only, a
//    necessary support function for the fromDisk() operation).
//
//  If the FileEntry passed in contains ADs that are overflowed to AEDs, then
//    we continue reading in those overflowed AEDs until we have read in all
//    parts of this Node.  Of course, all ADs may have been stored in this
//    FileEntry (if all ADs fit in one sector on the surface) and if we hold
//    embedded data, all embedded data will already be populated in this FileEntry.
//
// ARGS:
//  logExtent   IN  The one-block extent on the surface where this FileEntry
//                  is located (but we use this to know the blockSize(), and
//                  to know where the fileEntry is located--we don't read
//                  from the surface.
//  fileEntry   IN  The FileEntry we read state from.
//
// RETURNS:
//  error_None on success, specific error on failure.
// 
// PRE-CONDITIONS:
//  Assumes the FileEntry holds our new state.
// 
// POST-CONDITIONS:
//  All of this Node's state members are modified upon completion of this
//    call.  If the FileEntry passed in is already in an error state, 
//    no members are modified.
// 
// ERRORS:
//   error_None on success
//   error_NODE_InfoLen_mismatch  (the infoLen field in the FileEntry
//     does not match the infoLen read in).
//   error_NODE_LogBlocksRecorded_mismatch  (the logicalBlocksRecorded
//     field in the FileEntry does not match the number of logical
//     blocks read in.)  NOTE:  This error is set if the infoLen
//     matches, but the number of logical blocks recorded does not
//     match the descriptor.  If we came up short on infoLen (even though
//     the number of logical blocks recorded is also incorrect), that
//     error is returned instead of this one.
// 
// NOTES:
//  This function continues to read overflow AEDs from the surface if 
//  the FileEntry does not hold all the ADs associated with this node.
//
//----------------------------------------------------------------------
NSR::Error 
NSR::Node::populate_(LogExtent& logExtent, 
                     FileEntry& fileEntry)
{
    Error err = error_None;  // By default, we will populate ourselves.

    if(fileEntry.error()) {
        // The FileEntry passed in is in error.  We don't do
        //   anything.
        err = error_Bad_Parameter_File_Entry;  
    } else {
        // The fileEntry object passed in is not in an error state.
    
        // We are going to populate ourselves.  Clear the old state.
        clear();

        // Set all our state for the non-embedded data or non-ADs.
        uniqueID_ = fileEntry.uniqueID;
        // Populate all our attributes.
        attributes_.fromFileEntry(fileEntry);

        //   Track the infoLength so we can verify we are correct
        //   after we are done populating ourselves from the
        //   FileEntry.
        UINT64 infoLength = fileEntry.infoLength;
        fileEntry.extendedAttrICB.toLogExtent(logExtent.logVolumep(), 
                                              eaNodeAddr_, err);
        if (Trace::logAlgorithms()) {
            eaNodeAddr_.print(0, "########## POPULATED eaNodeAddr_ from fileEntry:");
        }

        if(err) { 
            // If we could not set the extendedAttrICB.
            if (Trace::logAlgorithms()) {
                printf("ERROR: Could not set extendedAttrICB! %d, %s\n", (UINT32)err, err.message());
            }
        } else {
            // We set the extendedAttrICB.

            // !!! INSERT OTHER STATE HERE.

            // Now, populate our EAs.
            embeddedEASpace_.setLen(fileEntry.getEALen());
            embeddedEASpace_.fromMem(fileEntry.embeddedEAp());

            // Now, populate our embedded data or ADs. 
            adType_ = fileEntry.adTable().adType();
            if(adType_ == ad_Embedded) {
                // If we have embedded data.
    
                // RECALL:  Since we are storing embedded data, the
                //   fileEntry::ADTable stores the number of bytes for
                //   the embedded data as the number of entries in the
                //   ADTable.
                embeddedData_.setLen(fileEntry.numADTEnts());
                embeddedData_.fromMem(fileEntry.embeddedDatap());
    
                // We have now set our embedded data.
            } else {
                // We have ADs.  Read them in.
          
                // Read in the chain of ADs, starting in this FileEntry.
                // Capture this error value and return it, in case
                //   something went wrong.
                if (Trace::logAlgorithms()) {
                    printf("about to populate ADs from the fileEntry.\n");
                }
                AEDWriter aedWriter;
                aedWriter.fromDisk(logExtent, 
                                   fileEntry.adTable(),
                                   dataEL_,
                                   (UINT64)fileEntry.infoLength,
                                   reserveLELSR_,
                                   metaDataEL_,
                                   err);
                // RECALL: The aedWriter will ensure that everything in
                //   our reserveLELSR_ is sorted and coalesced.
            }

            // At this point, we are populated.  Confirm we did actually
            //   read the correct infoLen and have the correct number
            //   of recorded blocks.
            // NOTE:  We do logicalBlocksRecorded first!  If we have
            //   an incorrect infoLen, that must be the error returned
            //   even though the number of recorded blocks ALSO does
            //   not match!  Thus, the error reflects the logical
            //   blocks recorded mismatch only when we also have the
            //   correct infoLen!.
    
            // Confirm our logical blocks recorded is correct.
            if((UINT32)fileEntry.logicalBlocksRecorded != (UINT32)dataEL_.recordedBlocks()) {
                // If the FileEntry's recorded blocks is not the same
                //   as the blocks we read into our file body.
                err = error_NODE_LogBlocksRecorded_mismatch;
                if (Trace::logAlgorithms()) {
                    printf("Logical blocks recorded mismatch! (fileEntry has %lu, dataEL_ has %lu\n", (UINT32)fileEntry.logicalBlocksRecorded, (UINT32)dataEL_.recordedBlocks());
                }
            }

            // Confirm our infoLen is correct (and possibly overwrite
            //   the error for the logical blocks recorded).
            if(fileEntry.adTable().adType() == ad_Embedded) {
                // If we have embedded data.
                if(infoLength != embeddedData_.length()) {
                    // If we have an incorrect infoLength.
                    err = error_NODE_InfoLen_mismatch;
                    if (Trace::logAlgorithms()) {
                        printf("Embedded data incorrect infoLength!\n");
                    }
                }
            } else { // We have ads.
                if(infoLength != dataEL_.infoLen()) {
                    // If we have an incorrect infoLength.
                    err = error_NODE_InfoLen_mismatch;
                    if (Trace::logAlgorithms()) {
                        printf("Embedded ADs incorrect infoLength!\n");
                    }
                }
            }
        }

    }

    return err;
}

//----------------------------------------------------------------------
// METHOD: Node::fromDisk()                                     --CSB
//
// Given an extent that holds a FileEntry, populate ourselves with
//   all of the Node state in that FileEntry, including all overflow
//   AEDs, if there are any (additional blocks will be read from the
//   surface).
//
// ARGS:
//  LogExtent& logExtent   IN  The logical extent on the surface where
//                             the FileEntry is located.
//  Error& error           OUT The return status of this operation.
//
// RETURNS:
//  error_None on success.
// 
// PRE-CONDITIONS:
//  Assumes a FileEntry is at this LogExtent on the surface.  If the
//  FileEntry cannot successfully read from the surface, an error is
//  returned and our Node's state is not modified.
// 
// POST-CONDITIONS:
//  All previous Node state is cleared, and the node has an entirely
//  new state reflecting the FileEntry and overflow AEDs we read from
//  the surface.
// 
// ERRORS:
//  error_NODE_InfoLen_mismatch; 
//  error_NODE_LogBlocksRecorded_mismatch;
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::Node::fromDisk( LogExtent & logExtent, Error & err ) 
{
    // Zero our error out, in case all goes well.
    err = error_None;

    // We want to read this Node from the disk.  Start with the
    //   descriptor portion (in the FileEntry).

    if(logExtent.error()) {
        // If the LogExtent passed in is in an error state, we can't
        //   use it.
        err = error_Bad_Parameter_Extent;
    } else {
        // The LogExtent passed in is not in error.

        // Make a buffer to hold our descriptor extent.
        ByteArray buffer(logExtent.infoLen());

        // Read the descriptor extent from the surface.
        INT32   bytesRead;
        bytesRead = logExtent.read( buffer, err );

        if(! err) {
            // If we did not encounter an error when reading the bytes
            //   from the surface.

            // Read the FileEntry from the buffer.
            FileEntry fileEntry;

            if (Trace::logAlgorithms()) {
                printf("doing a FileEntry::fromISO() at block %ld\n",
                       logExtent.startBlock() );
            }
            fileEntry.fromISO( buffer, logExtent.startBlock());

            if (! (err = fileEntry.error())) {
                // If we were able to read the descriptor from the buffer.
                if (Trace::logAlgorithms()) {
                    printf("FileEntry::fromISO() succeeded!\n");
                }

                // We are ready to populate ourselves from this
                //   descriptor. 
                // We want to populate ourselves from everything this
                //   descriptor has, and if we continue into overflow
                //   AEDs, we want to read those in also.
                // Capture the error value returned, in case something
                //   bad happened during the population process.
                err = populate_(logExtent, fileEntry);
                // At this point, we are done.  We read the Node from
                //   the disk. 
            }
        }
    }
    if(!err) {
        // If we did not encounter an error when reading, check our
        //   logical state.
        err = ISOError(logExtent);
    }

    return; 
} // Node::fromDisk()


//----------------------------------------------------------------------
// METHOD: Node::toDisk()                                       --CSB
//
//  Given the address (extent) to write a FileEntry, write out this
//  Node with a FileEntry descriptor at the LogExtent provided,
//  continuing overflow AEDs (if necessary) at additional blocks.  The
//  AEDs will be written first to blocks already allocated for meta
//  data (remembered from the Node::fromDisk() operation), or
//  additional blocks will be allocated from the SpaceResource passed
//  in (if we don't already have enough meta data blocks).  If we have
//  a surplus of available meta data blocks remembered from the
//  Node::fromDisk() operation, these surplus blocks are freed back to
//  the SpaceResource.
//
//  Because the Node class does not store the ADType internally, that
//  parameter must be passed in so we know to convert the LogExtents
//  to shortAllocDesc, longAllocDesc, or extendedAllocDesc.
//
//  The user can first call the Node::ISOError() to get an Error
//  return value to confirm that this Node has correct internal state
//  that can be written to the surface (not too much data, Node can
//  reconcile embedded data from embedded ADs, etc.)
//  
// ARGS:
//
//  LogExtent& ext    IN          The extent on the surface where the
//                                FileEntry is to be written. See OSTA
//                                2.3.10 for info on which type to
//                                use.
//  SpaceResource& space  IN/OUT  The space resource we can get
//                                available blocks from or free blocks
//                                to on the write.
//
// RETURNS:
//  error_None on success.
//
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
//
//  error_NODE_InfoLen_mismatch
//  error_NODE_LogBlocksRecorded_mismatch
// 
// NOTES:
//
//  This function ASSUMES the logical extent passed in is big enough
//  to write the entire FileEntry.  At present, this means the extent
//  should be one block (for non-WORM media, the FileEntry is written
//  in one block extents.)
//----------------------------------------------------------------------
void 
NSR::Node::toDisk( NSR::LogExtent & ext, 
                   NSR::SpaceResource & space, 
                   Error & err) 
{
    if (Trace::logAlgorithms()) {
        printf("Enter Node::toDisk(), with a type of %d.\n", adType_);
    }
    // Set to no error by default.
    err = error_None;

    if(ext.error()) {
        // If the extent passed in is already in an error state.
        err = error_Bad_Parameter_Extent;    
    } else if(space.error()) {
        // If the space resource is already in an error state.
        err = error_Bad_Parameter_Space_Resource;
    } else if(err = ISOError(ext)) {
        // We have too much data or are otherwise in state violation.
        //   Abort the call.
        // Do nothing:  We have err set.
    } else {
        // Our parameters are ok.

        // Now, instantiate a FileEntry so we can later write our descriptor
        //   information to disk.
        if (Trace::logAlgorithms()) {
            printf("We have %d data and reserve ADs.\n", numDataAndReserveExtents_());
            printf("We have data infoLen of %ld, reserve infoLen %ld.\n", dataEL_.infoLen(),
                   reserveLELSR_.availBytes());
        }
        // Assume a zero-length file entry by default.
        FileEntry fileEntry(0, adType_);

        // Set all our state for the non-embedded data or non-ADs.
        fileEntry.uniqueID = uniqueID_;
        // Populate all our attributes.
        attributes_.toFileEntry(fileEntry);

        // Populate the EAAttr.
        // NOTE: We must set the partition pointer if the user never set
        //   the eaNodeAddr_.
        if(!eaNodeAddr_.partitionp()) {
            // If the user never set this extent.
            eaNodeAddr_.reinit(ext.partitionp(), 0, 0, err);
        }
        if (Trace::logAlgorithms()) {
            eaNodeAddr_.print(0, "########## POPULATING fileEntry from eaNodeAddr_:");
        }
        fileEntry.extendedAttrICB.fromLogExtent(eaNodeAddr_, err);
        if(err) {
            // If we could not set the extendedICBAddr.
#ifdef NODE_TEST
            printf("ERROR: Could not set extendedICBAddr! %ld, %s\n", (UINT32)err, err.message());
#endif
        } else {

            // !!! INSERT OTHER STATE HERE.
    
            // Now, populate the fileEntry with our EAs.
            // NOTE: We don't need to call
            //   fileEntry.EAPadLen(embeddedEASpace_.length()) to round
            //   up to a four-byte boundary, because our
            //   embeddedEASpace_ was forced to a four-byte boundary
            //   when it was set.

            err = fileEntry.setEALen(ext.blockSize(),
                                     embeddedEASpace_.length() );
            if(!err) {
                embeddedEASpace_.toMem(fileEntry.embeddedEAp());
            } else if (Trace::logAlgorithms()) {
                printf("ERROR!  Could not set FileEntry::setEALen()! (set to %ld),  (%ld), %s\n", embeddedEASpace_.length(), (UINT32)err, err.message());
            }

            // Now, populate our embedded data or ADs. 
            if(!err && (fileEntry.adTable().adType() == ad_Embedded)) {
                // If we have embedded data.
                if (Trace::logAlgorithms()) {
                    printf("We have embedded data.\n");
                    fileEntry.adTable().dump("fileEntry adTable");
                }
    
                // Set the length of the file body, which is the number of
                //   embedded bytes if we have embedded data.
                fileEntry.infoLength = embeddedData_.length();
    
                // Set the number of recorded blocks.
                // RECALL:  We have no recorded blocks if we have embedded data.
                fileEntry.logicalBlocksRecorded = 0;
        
                // RECALL:  Since we are storing embedded data, the
                //   fileEntry::ADTable stores the number of bytes for
                //   the embedded data as the number of entries in the
                //   ADTable.

                // Resize the ADTable in the fileEntry.
                err = fileEntry.numADTEnts(space.blockSize(), embeddedData_.length());
                // Set the embedded data.
                if(!err) {
                    embeddedData_.toMem(fileEntry.embeddedDatap());
                } else if (Trace::logAlgorithms()) {
                    printf("ERROR!  Could not resize FileEntry::numADTEnts()! (%ld), %s\n", (UINT32)err, err.message());
                }

                // We have now set our embedded data.
            } else if(!err) {
                // We have ADs.  Write them out.
                if (Trace::logAlgorithms()) {
                    printf("We have ADs.\n");
                }

                // Set the length of the file body.
                fileEntry.infoLength = dataEL_.infoLen();
    
                // Set the number of recorded blocks.
                fileEntry.logicalBlocksRecorded = dataEL_.recordedBlocks();
    
                // We want to set the FileEntry to hold the maximum number of
                //   this type of ADs it can hold.
                fileEntry.numADTEnts(ext.blockSize(), 
                                     fileEntry.maxADTEnts(ext.blockSize()),
                                     adType_);
                
                // Now, actually write them out.
                AEDWriter aedWriter;
                aedWriter.toDisk(ext, 
                                 fileEntry.adTable(), 
                                 dataEL_,
                                 reserveLELSR_,
                                 metaDataEL_,
                                 space,
                                 err);
                // RECALL:  The AEDWriter.toDisk() should have set the last
                //   entry in the fileEntry.adTable() to be a reference to
                //   the AED, if necessary.

                // At this point, we can (optionally) shrink the size

                //   of thefileEntry.adTable() so that the un-used
                //   entries are not allocated (but the space is not
                //   usable for anything else on the disk anyway:  We
                //   need 2, but make room for 8, the last entry must
                //   simply be a terminating entry (set in the
                //   AEDWriter.toDisk()).
            }

            // At this point, we have set up our FileEntry with the
            // correct state (either ADs or embedded data, and
            // overflow AEDs are already written).
        
            if(!err) {
                // If we did not have any errors setting the state of
                // the FileEntry. Write this fileEntry to disk.
                // NOTE:  The ext (LogExtent) passed in was allocated
                //   by the user and explicitly refers to the location
                //   on the disk where this fileEntry is to go!  We do
                //   not allocate additional space for it, and we
                //   assume that the length is blocksize!
                ByteArray buffer(ext.infoLen()); 
        
                if (Trace::logAlgorithms()) {
                    printf("Doing a FileEntry::toISO() at block %ld, and the type is %d\n", 
                           ext.startBlock(), fileEntry.adTable().adType());
                    printf("--------We have %d ADs.\n", fileEntry.numADTEnts());
      
                    fileEntry.adTable().dump("++++++on toISO, adTable state:");
                    printf("Before toISO, fileEntry.error() is (%ld) [%s].\n", (UINT32)fileEntry.error(), fileEntry.error().message());
                }

                fileEntry.toISO( buffer, ext.startBlock());
                if (Trace::logAlgorithms()) {
                    fileEntry.adTable().dump("after toISO, adTable state:");
                    printf("After toISO, fileEntry.error() is (%d) [%s].\n", (UINT32)fileEntry.error(), fileEntry.error().message());
                }
                if(! (err = fileEntry.error())) {
                    // If we could write to the block in an ISO form.
                    // Write the buffer to the surface.
                    ext.write( buffer , err);
                } else {
                    // We have an error doing a toISO().
                    if (Trace::logAlgorithms()) {
                        printf("The fileEntry is in error()!\n");
                    }
                }
            } else if (Trace::logAlgorithms()) {
                // We have an error.
                printf("Have error:  [%ld] %s\n", (UINT32)err, err.message());
            }
        }
    }
    // At this point, we know how many AED blocks are written.  If we
    //   have an error, don't free the meta-data space (don't do
    //   anything-- the caller can clean that up by freeing the
    //   metaDataEL_ from a diskSpace() call).  However, if we do not
    //   have an error, then the write successfully completed and we
    //   can get rid of any space not used during the write.
    // WAIT!! SAI determined that we would be freeing space we should
    //   not, so the freeing of ANYTHING is NOT done.

#if 0                           // should NOT free anything
    if(!err) {
        // If we could write all of ourselves out without error, free
        //   unused (un-needed) meta-data space.
        if (Trace::logAlgorithms()) {
            printf("Freeing meta-data space.\n");
        }
        freeUnusedMetaDataExtents_(space, numAEDsWritten, err);
    }
#endif // 0    
} // Node::toDisk()

//----------------------------------------------------------------------
// METHOD: Node::freeUnusedMetaDataExtents_()                   --CSB
//
// Free all LogExtents in our metaDataEL_ for the index passed in and
//   beyond.  Thus, if 0 were passed in, we free the entire list.
//
// ARGS:
//  SpaceResource& space  IN/OUT  The space resource to which we free
//                                meta data blocks we no longer need.
//  UINT32 numAEDsWritten IN      The index into the metaDataEL_ where
//                                we begin freeing blocks.  We free
//                                that block (if it exists) and all
//                                blocks after that index (if they
//                                exist).
//  Error& err            OUT     The exit status of this operation.
//
// RETURNS:
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//  The index passed in is assumed to be the number of AED blocks
//   (meta-data blocks) required for this node; thus, we can free all
//   past those required.
//
//----------------------------------------------------------------------
void
NSR::Node::freeUnusedMetaDataExtents_(SpaceResource& space, UINT32 numAEDsWritten,
                                      Error& err)
{
    err = error_None;

    if(space.error()) {
        // If our space resource is in an error state.
        err = error_Bad_Parameter_Space_Resource;
    } else {
        // We are ok.
        // Iterate and free all nodes at this index until there are none
        //   left.
        while((metaDataEL_.numExtents() > numAEDsWritten) && (!err)) {
            // While we have extents at or after this supplied index, and
            // we don't have an error.
            // Free the extent to the space resource.
            // Should we check first that we got a logExtent pointer back?
            if (Trace::logAlgorithms()) {
                printf("---FREEING UNUSED META-DATA BLOCK %ld!---\n", 
                       metaDataEL_.at(numAEDsWritten, err));
            }
            // ISSUE: Bug: Needs to check err between calls
            space.putLogExtent(*metaDataEL_.at(numAEDsWritten, err), err);
            if(!err)
                metaDataEL_.remove(numAEDsWritten, err);
        }
    }
}
  
//----------------------------------------------------------------------
// METHOD: Node::descriptorBytesAvailable()                     --CSB
//
//  Returns the number of bytes available in the FileEntry portion
//  of a Node write operation to the surface.
//
//  The following is for size calculations:  
//    o If the Node contains embedded data
//        Return the bytes available AFTER SUBTRACTING the sum of all 
//        Nodes information (which is all FileEntry information, plus 
//        all EAs, plus all embedded data).
//    o If the Node contains ADs (not embedded data)
//        Return the bytes available AFTER SUBTRACTING the sum of all 
//        FileEntry information, all EAs, and ONLY THE FIRST AD (because 
//        every AD after the first can optionally overflow past the 
//        FileEntry write [past the one logical sector] to a list of 
//        AEDs [outside the one logical sector]).
//
//        NOTE: If we do not have any extents in our LogExtentLists
//          (no extents in the dataEL_ or reserveLELSR_) then we DO NOT
//          reserve space for that one AD!  All available bytes are
//          returned, NOT CONSIDERING the one AD!  (Just as in embedded
//          data).
//
//        NOTE: Even though we may be able to store more than one AD,
//         do our calculation with the minimum:  only one AD (we can
//         pack them in on the actual write operation if there is room
//         for more than one AD).
//
// ARGS:
//
//  const LogExtent& logExtent  IN  An extent primarily used to merely
//                                  get the logical block size for the
//                                  surface.
// RETURNS:
// 
//  This function returns an INT32 with the following:
//    positive integer:  The number of bytes available for additional
//                       EAs or embedded data.
//    zero:              This Node cannot hold additional EAs or
//                       embedded data.
//    negative number:   This Node holds more EAs or embedded data than
//                       it is able to write to disk (a write error 
//                       will occur on a Node::toDisk() attempt).
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
const NSR::INT32 
NSR::Node::descriptorBytesAvailable(UINT32 blockSize)
{
    // The available number of bytes is the length of one block.
    INT32 available = blockSize;

    // Determine the length of our descriptor (calculated by FileEntry).
    FileEntry fileEntry(1, adType_);

    if(adType_ == ad_Embedded) {
        // We store embedded data, not ADs.
        // Force the AD table to zero.
        fileEntry.numADTEnts(0, ad_Embedded);
        // NOTE:  We do not force the AD table to the length of the
        //   embedded data because the ADTable will abort the
        //   assignment if we try to put too much data in there.

        // Subtract the amount of space for our embedded data.
        available -= embeddedData_.length();
    } else {
        // We have ADs.  However, we DO NOT want to reserve space for
        //   that single AD if we have nothing in our extent lists.
        if(numDataAndReserveExtents_() == 0) {
            // If we have no data or reserve extents, then we
            //   should resize to zero entries when determining the
            //   bytes available (do not leave room for that single AD).
            fileEntry.numADTEnts(0, adType_);  // No ADs.
        }
    }

    // At this point, we must add in the length of the FileEntry
    //   descriptor to our length.  If we have embedded data, this is
    //   zero ADs, and we already accumulated the embedded data size.
    //   Otherwise, we have one AD of the correct type in the
    //   descriptor.
    available -= fileEntry.descLen();

    // Now, subtract our space requirements for our EAs.
    // NOTE: We don't need to call
    //   fileEntry.EAPadLen(embeddedEASpace_.length()) to round up to
    //   a four-byte boundary, because our embeddedEASpace_ was forced
    //   to a four-byte boundary when it was set.
    available -=  embeddedEASpace_.length();

    // At this point, we know how much was budgeted (the blockSize),
    //   and we subtracted the length of a FileEntry descriptor (in
    //   FE), the length of one AD if we don't have embedded data (in
    //   FE), and manually subtracted the length of embedded data, and
    //   the length of the embedded EA space padded to a 4-byte
    //   boundary.  We are done.
    return available;
}

//----------------------------------------------------------------------
// METHOD: Node::operator!=()                                   --CSB
//
//  Compare this Node with another Node.  Return non-zero if the nodes
//  have different state.
//
// ARGS:
//  const Node& otherNode   IN  The other node we compare against.
//
// RETURNS:
//  Returns non-zero if the nodes have different internal state.
//  Zero if the nodes have the same internal state.
// 
// PRE-CONDITIONS:
// 
// POST-CONDITIONS:
// 
// ERRORS:
// 
// NOTES:
//  We ignore the fact that either node may be in an error state.  All
//    we care about is whether or not the Nodes have the same state.
//
//----------------------------------------------------------------------
NSR::Boolean
NSR::Node::operator!=(const Node & other) 
{
    Boolean returnVal = False;  // Nodes are different by default.

#if 0
    // See if the internal members have the same state.
    if(
        (adType_          != other.adType_)       ||
        (uniqueID_        != other.uniqueID_)     ||
        (attributes_      != other.attributes_)   ||
        (eaNodeAddr_      != other.eaNodeAddr_)   ||
        (metaDataEL_      != other.metaDataEL_)   ||
        (dataEL_          != other.dataEL_)       ||
        (reserveLELSR_       != other.reserveLELSR_)    ||
        (embeddedData_    != other.embeddedData_) ||
        (embeddedEASpace_ != other.embeddedEASpace_)) {
        // If any of these members are different, then we have different state.
        returnVal = True;
    }

#else   // The old way.
    UINT32 bad = 0;
    // Do we want an error member?
    //bad += error();
    //bad += other.error();
    bad += (uniqueID_ != other.uniqueID_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (uniqueID_)     bad is %ld\n", bad);
        //printf("because node is %ld and other is %ld\n", (UINT64)uniqueID_, (UINT64)other.uniqueID_);
        //printf("node state: [%s], other state: [%s]\n", (CHAR_PTR)getState(), (CHAR_PTR)other.getState());
    }
    bad += (adType_ != other.adType_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (adType_)   bad is %ld\n", bad);
    }
    bad += (uniqueID_ != other.uniqueID_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (uniqueID_)   bad is %ld\n", bad);
    }
    bad += (attributes_ != other.attributes_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (attributes_)   bad is %ld\n", bad);
    }
    bad += (eaNodeAddr_ != other.eaNodeAddr_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (eaNodeAddr_)   bad is %ld\n", bad);
        eaNodeAddr_.print(0, "OURS:");
        other.eaNodeAddr_.print(0, "THEIRS:");
    }

#if 0  //-------------------------------------------
// NOTE:  We CAN'T make this comparison, because the metaDataEL_ is
//   empty before the write, but will be full after the write!  This
//   is not a logical comparison!  Invalid!  We want this to change
//   for the same logical Node before and after the write!
    bad += (metaDataEL_ != other.metaDataEL_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (metaDataEL_)   bad is %ld\n", bad);
        metaDataEL_.print(0, "OURS:");
        other.metaDataEL_.print(0, "THEIRS:");
    }
#endif // #if 0  //---------------------------------

    bad += (dataEL_ != other.dataEL_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (dataEL_)       bad is %ld\n", bad);
    }
    bad += (reserveLELSR_ != other.reserveLELSR_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (reserveLELSR_)    bad is %ld\n", bad);
        //reserveLELSR_.print(0, "OURS:");
        //other.reserveLELSR_.print(0, "THEIRS:");
    }
    bad += (embeddedData_ != other.embeddedData_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (embeddedData_) bad is %ld\n", bad);
    }
    bad += (embeddedEASpace_ != other.embeddedEASpace_);
    if (Trace::logAlgorithms()) {
        printf("Node::operator!=(): (embeddedEASpace_)  bad is %ld\n", bad);
    }
    returnVal = (bad? True:False);
#endif // not old way
    return returnVal;
} // Node::operator!=


//----------------------------------------------------------------------
// METHOD: Node::clear()                                --CSB
//
//  Resets the entire state of the Node object by calling clear()
//  for all data members.
//   
// ARGS:
//
// RETURNS:
// 
// PRE-CONDITIONS:
//  This object could be in any possible state; everything will be reset
//  in the clear() operation.
// 
// POST-CONDITIONS:
//  All internal members of this Node object are re-set.
// 
// ERRORS:
// 
// NOTES:
//
//----------------------------------------------------------------------
void 
NSR::Node::clear() {
    uniqueID_        = 0;
    attributes_      .clear();
    metaDataEL_      .clear();
    dataEL_          .clear();
    reserveLELSR_    .clear();
    embeddedData_    .clear();
    embeddedEASpace_ .clear();  
}

