/*    
   Unfrag.c - unfragment files only.

   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 received 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 <string.h>
#include <stdio.h>

#include "fte.h"
#include "DfrgDrvr.h"
#include "FllDfrag\flldfrg.h"
#include "..\dtstruct\ClMovMap.h"
#include "..\..\modlgate\expected.h"
#include "defrmap.h"

/* Public functions */
static BOOL SelectFile(RDWRHandle handle, CLUSTER startfrom,
                       CLUSTER* clustertoplace);                         

/* Private functions */                  
static BOOL ConservativePlace(RDWRHandle handle, CLUSTER clustertoplace,
                              CLUSTER clustertobereplaced, CLUSTER* stop);                          
                           
static BOOL MarkNotFragmentedFiles(RDWRHandle handle);

struct Pipe
{
   CLUSTER cluster;
   CLUSTER clustertoplace;

   BOOL found;
};

BOOL UnfragmentFiles(RDWRHandle handle)
{
    BOOL result;
    
    if (CreateFastSelectMap(handle) != TRUE)
       return FALSE;
       
    if (CreateNotFragmentedMap(handle) != TRUE)
    {
       DestroyFastSelectMap();
       return FALSE;
    }

    if (!MarkNotFragmentedFiles(handle))
	return FALSE;

    result = DefragmentVolume(handle, SelectFile, ConservativePlace);
    
    DestroyFastSelectMap();
    DestroyNotFragmentedMap();
    
    return result;
}

static BOOL MarkNotFragmentedFiles(RDWRHandle handle)
{
    CLUSTER i, start = 0;
    unsigned long LabelsInFat;
    char* NotFragmentedMap;
    BOOL NotFrag;
    
    NotFragmentedMap = GetNotFragmentedMap();
    
    assert(NotFragmentedMap);
    
    LabelsInFat = GetLabelsInFat(handle);
    if (!LabelsInFat) return FALSE;	
    
    for (i = 0; i < LabelsInFat; i++)
    {
	NotFrag = GetBitfieldBit(NotFragmentedMap, i);
	
	if (!start && NotFrag)
        {
           start = i;
        }
	else if (start && !NotFrag)
	{
	   DrawMoreOnDriveMap(start, OPTIMIZEDSYMBOL, i-start);
	   start = 0;
	}	
    }
    
    if (start)
    {
	DrawMoreOnDriveMap(start, OPTIMIZEDSYMBOL, LabelsInFat - start);	
    }    
    
    return TRUE;
}

static BOOL SelectFile(RDWRHandle handle, CLUSTER startfrom,
                       CLUSTER* clustertoplace)
{
    CLUSTER i;
    unsigned long LabelsInFat;
    char* FastSelectMap;
        
    FastSelectMap = GetFastSelectMap();    
        
    LabelsInFat = GetLabelsInFat(handle);
    if (!LabelsInFat) return FAIL;
    
    for (i = startfrom; i < LabelsInFat; i++)
    {
        if (GetBitfieldBit(FastSelectMap, i))
        {
           *clustertoplace = i;
           return TRUE;
        }
    }
    
    return FALSE;
}

static CLUSTER GetNextContinousBlock(RDWRHandle handle,
                                     CLUSTER clustertoplace)
{
    CLUSTER i;
    unsigned long LabelsInFat;
    char* NotFragmentedMap;
    
    NotFragmentedMap = GetNotFragmentedMap();
        
    LabelsInFat = GetLabelsInFat(handle);
    if (!LabelsInFat) return 0;
    
    for (i = clustertoplace; i < LabelsInFat; i++)
    {
        if (GetBitfieldBit(NotFragmentedMap, i))
        {
           return i;
        }
    }
    
    return 0;  /* No next continous block found */  
}

static unsigned long CalculateWorkingSpace(RDWRHandle handle, 
                                           CLUSTER clustertoplace)
{
    BOOL isMovable;
    CLUSTER i, label;
    unsigned long LabelsInFat, result = 0;
    char* NotFragmentedMap;
    
    NotFragmentedMap = GetNotFragmentedMap();
        
    LabelsInFat = GetLabelsInFat(handle);
    if (!LabelsInFat) return 0;
    
    for (i = clustertoplace; i < LabelsInFat; i++)
    {
        if (GetBitfieldBit(NotFragmentedMap, i))
        {
           return result;
        }

        /* Make sure not to count the bad and non movable clusters. */
        if (!GetNthCluster(handle, i, &label))
           return 0;
           
        if (!FAT_BAD(label))
        {
           if (!IsClusterMovable(handle, i, &isMovable))
              return 0;
           if (isMovable)
              result++;
        }
    }
    
    return result;  /* No next continous block found */  
}

static CLUSTER SkipNonMovables(RDWRHandle handle, CLUSTER start)
{
    CLUSTER i;
    unsigned long LabelsInFat;
    char* NotFragmentedMap;
    
    NotFragmentedMap = GetNotFragmentedMap();
        
    LabelsInFat = GetLabelsInFat(handle);
    if (!LabelsInFat) return 0;
    
    for (i = start; i < LabelsInFat; i++)
    {
        if (!GetBitfieldBit(NotFragmentedMap, i))
        {
           return i;
        }
    }
    
    return 0;  /* No next continous block found */  
}

static BOOL ConservativePlace(RDWRHandle handle, CLUSTER clustertoplace,
                              CLUSTER clustertobereplaced, CLUSTER* stop)
{
    CLUSTER temp;
    unsigned long filelen, workingspace;
    CLUSTER FreeSpace, ContinousBlockStart;

    temp = SkipNonMovables(handle, clustertobereplaced);  
    
    if (temp != clustertobereplaced)
    {
       *stop = temp;
       return (temp == 0) ? FAIL : TRUE;
    }
    

    /* Calculate the size of the file to place, and the space available
       to the next non fragmented block. */
    filelen = CalculateFileSize(handle, clustertoplace);  
    if (filelen == FAIL) return FAIL;
            
    workingspace = CalculateWorkingSpace(handle, clustertobereplaced);
    if (!workingspace) return FAIL;    

    /* If there is enough space to place the file completely, then move
       the complete file to this location. */
    if (filelen <= workingspace)
    {
       return FullDefragPlace(handle,
                              clustertoplace, clustertobereplaced, 
                              stop);
    }
    /* If there is not enough space, try putting the file in a free
       space on the disk that is large enough to contain the file. */
    else
    {
       switch (FindFirstFittingFreeSpace(handle, filelen, &FreeSpace))
       {
         case TRUE:
              if (FullDefragPlace(handle, 
                                   clustertoplace, FreeSpace, 
                                   stop) == FAIL)
              {
                 return FAIL; 
              }
              
              MarkFileAsContinous(FreeSpace, filelen);
              *stop = clustertobereplaced;
              break;
                 
         case FAIL:
              return FAIL;
              
         case FALSE:
              /* Otherwise move the continous block to the front,
                 and start again. */   
              ContinousBlockStart = GetNextContinousBlock(handle, 
                                                          clustertobereplaced); 
                                                          
              if (!ContinousBlockStart) return FAIL;
              
              /* Notice that the first cluster of that block is the start
                 of some file (since the file is continous). */
              if (ContinousBlockStart != clustertobereplaced)
              {
                 return FullDefragPlace(handle,
                                        ContinousBlockStart,
                                        clustertobereplaced,
                                        stop);
              }
              else
              {
                 *stop = SkipNonMovables(handle, clustertobereplaced);
                 
                 if ((*stop) == 0)
                    return FAIL;
              }
	      break;
	      
	 default:
	      assert(FALSE);  
       }
    }
    
    return TRUE;
}
         

 
