/*
   clmovmap.c - operations on the fixed cluster map.

   Copyright (C) 2003, Imre Leber.

   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 2 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 recieved a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   If you have any questions, comments, suggestions, or fixes please
   email me at:  imre.leber@worldonline.be.

*/

#include <assert.h>
#include <stdlib.h>

#include "fte.h"
#include "..\..\modlgate\expected.h"

static unsigned long WindowStart;

static char* FixedClusterMap = NULL;

/*static*/ BOOL FixedFileMarker(RDWRHandle handle,
                            struct DirectoryPosition* pos,
                            void** structure);
/*static*/ BOOL FixedClusterMarker(RDWRHandle handle,
                               CLUSTER label,
                               SECTOR  sector,
                               void**  structure);

BOOL CreateFixedClusterMap(RDWRHandle handle)
{
    unsigned long labelsinfat;

    labelsinfat = GetLabelsInFat(handle);
    if (!labelsinfat) return FALSE;

    WindowStart = 0;

    /* Make sure that you do not allocate more then 8Kb (65536/8) */
    if (labelsinfat > 65536L) labelsinfat = 65536L;

    FixedClusterMap = CreateBitField(labelsinfat);   
    if (!FixedClusterMap) return FALSE;

    if (!WalkDirectoryTree(handle, FixedFileMarker, (void**) NULL))
       return FALSE;  
       
    LogMessage("Fixed cluster map created.");
    return TRUE;
}

/*static*/ BOOL FixedFileMarker(RDWRHandle handle,
                            struct DirectoryPosition* pos,
                            void** structure)
{
    CLUSTER firstcluster;
    struct DirectoryEntry entry;

    if (structure);
    
    if (!GetDirectory(handle, pos, &entry))
       return FAIL;
       
    if ((entry.attribute & FA_LABEL) ||
        (IsLFNEntry(&entry))         ||
        (IsDeletedLabel(entry)))
       return TRUE;
       
    if ((entry.attribute & FA_HIDDEN) ||
        (entry.attribute & FA_SYSTEM))
    {
       firstcluster = GetFirstCluster(&entry);
       if (firstcluster)
       {
          if (!FileTraverseFat(handle, firstcluster, FixedClusterMarker, 
                               (void**) NULL))
          {
              return FAIL;
          }
       }
    }

    return TRUE;
}

/*static*/ BOOL FixedClusterMarker(RDWRHandle handle,
                               CLUSTER label,
                               SECTOR  sector,
                               void**  structure)
{
    CLUSTER cluster;
    
    if (label);
    if (structure);
    
    cluster = DataSectorToCluster(handle, sector);
    if (!cluster)
       return FAIL;

    if ((cluster >= WindowStart) && (cluster < (WindowStart + 65536L)))
    {
       SetBitfieldBit(FixedClusterMap, cluster-WindowStart);
    }

    return TRUE;
}

void DestroyFixedClusterMap(void)
{
    if (FixedClusterMap)
    {
       DestroyBitfield(FixedClusterMap);
       FixedClusterMap = NULL;
    }
}

BOOL IsClusterMovable(RDWRHandle handle, CLUSTER cluster, BOOL* isMovable)
{
    if (handle);
    
    assert(FixedClusterMap);
           
    if ((cluster < WindowStart) || (cluster > (WindowStart + 65536L)))
    {
       WindowStart = cluster - (cluster % 65536L);
       if (!WalkDirectoryTree(handle, FixedFileMarker, (void**) NULL))
	  return FALSE;
    }

    *isMovable = !GetBitfieldBit(FixedClusterMap, cluster-WindowStart);
    
    return TRUE;
}
