/*!
    \file  Fat12.cpp
    \brief Fat12

    Copyright (c) 2005 HigePon
    WITHOUT ANY WARRANTY

    \author  HigePon
    \version $Revision$
    \date   create:2005/03/26 update:$Date$
*/

#include "types.h"
#include "Fat12.h"
#include "BitMap.h"
#include "FileSystemError.h"
#include "string.h"
#include "stdio.h"

namespace FatFS
{

/*----------------------------------------------------------------------
    Fat12
----------------------------------------------------------------------*/
Fat12::Fat12(IStorageDevice* fd)
{
    this->fd = fd;
}

Fat12::~Fat12()
{
    delete[] this->sectorBuffer;
    delete[] this->fat;
    delete this->flushTargetMap;
}

bool Fat12::Initialize()
{
    /* allocate sector buffer */
    this->sectorBuffer = new byte[SECTOR_SIZE];

    if (this->sectorBuffer == NULL)
    {
        this->lastError = FileSystemError::MEMORY_ALLOCATE_ERROR;
        return false;
    }

    /* read bpb */
    if (!ReadBpb()) return false;

    /* read Fat */
    if (!ReadAndSetFat()) return false;

   this->flushTargetMap = new BitMap(this->bpb.sectorsPerFAT);

    return true;
}

dword Fat12::GetNextCluster(dword cluster)
{
    dword index = cluster + cluster / 2;
    dword next;

    if (cluster & 0x01)
    {
        next = (fat[index + 1] << 4) + (fat[index] >> 4);
    }
    else
    {
        next = ((fat[index + 1] & 0x0f) << 8) + fat[index];
    }
    return next;
}

void Fat12::SetNextCluster(dword cluster, dword nextCluster)
{
    dword index = cluster + cluster / 2;

    if (cluster & 0x01)
    {
        fat[index + 1] = nextCluster >> 4;
        fat[index] = (fat[index] & 0x0f) | (nextCluster & 0x0f) << 4;
    }
    else
    {
        fat[index + 1] = (fat[index + 1] & 0xf0) | (nextCluster >> 8);
        fat[index] = nextCluster;
    }

    /* for flush */
//    this->flushTargetMap->mark(cluster / 1.5); // Mmm...

    return;
}

bool Fat12::GetClusterFromChain(dword startCluster, dword count, dword* found)
{
    dword foundCluster = startCluster;
    dword prevCluster  = startCluster;

    for (dword i = 0; i < count; i++)
    {
        foundCluster = GetNextCluster(prevCluster);

        if (foundCluster == prevCluster) return false;
        prevCluster = foundCluster;
    }

    *found = foundCluster;
    return true;
}

bool Fat12::GetPreviousCluster(dword start, dword cluster, dword* found)
{
    for (dword i = 0; i < 0xfff; i++)
    {
        dword next = GetNextCluster(start);

        if (next == cluster)
        {
            *found = start;
            return true;
        }

        start = next;
    }

    return false;
}

bool Fat12::ReadCluster(dword cluster, void* buffer)
{
    int result = this->fd->read(GetLbaFromCluster(cluster), buffer, SECTOR_SIZE);
    return result == 0;
}

bool Fat12::WriteCluster(dword cluster, void* buffer)
{
    int result = this->fd->write(GetLbaFromCluster(cluster), buffer, SECTOR_SIZE);
    return result == 0;
}

bool Fat12::Flush()
{
    for (int i = 0; i < this->flushTargetMap->getBitsNumber(); i++)
    {
        if (!this->flushTargetMap->marked(i)) continue;

        if (this->fd->write(i + FAT_START_SECTOR, &this->fat[SECTOR_SIZE * i], SECTOR_SIZE))
        {
            this->lastError = FileSystemError::DEVICE_READ_ERROR;
            return false;
        }

        this->flushTargetMap->clear(i);
    }

    return true;
}

bool Fat12::ReadBpb()
{
    if (this->fd->read(0, this->sectorBuffer, SECTOR_SIZE))
    {
        this->lastError = FileSystemError::DEVICE_READ_ERROR;
        return false;
    }

    memcpy(&this->bpb, this->sectorBuffer, sizeof(Bpb));

    if (this->bpb.sectorsPerCluster != 1 || this->bpb.bytesPerSector != 512)
    {
        printf("fat unknown format\n");
        printf("bpb.sectorsPerCluster=%d\n", bpb.sectorsPerCluster);
        printf("bpb.bytesPerSector=%d\n", bpb.bytesPerSector);
        return false;
    }

    this->rootDirectorySectors = bpb.maxRootEntries * sizeof(DirectoryEntry) /SECTOR_SIZE;
    this->notClusterArea = 1 + bpb.sectorsPerFAT * bpb.fatCount + this->rootDirectorySectors;

    return true;
}

dword Fat12::GetLbaFromCluster(dword cluster)
{
    return this->notClusterArea + bpb.sectorsPerCluster * (cluster - 2);
}

bool Fat12::ReadAtOnce(dword lba, void* buffer, dword size)
{
    dword sectors = size / SECTOR_SIZE;
    for (dword i = 0; i < sectors; i++)
    {
        int result = this->fd->read(i + lba, (void*)((byte*)buffer + SECTOR_SIZE * i), SECTOR_SIZE);
        if (result)
        {
            return false;
        }
    }
    return true;
}


bool Fat12::ReadAndSetFat()
{
    dword fatBytes = bpb.sectorsPerFAT * SECTOR_SIZE;

    /* allocate Fat buffer */
    this->fat = new byte[fatBytes];

    if (this->fat == NULL)
    {
        this->lastError = FileSystemError::MEMORY_ALLOCATE_ERROR;
        return false;
    }

    /* read Fat */
    if (!ReadAtOnce(FAT_START_SECTOR, this->fat, fatBytes))
    {
        this->lastError = FileSystemError::DEVICE_READ_ERROR;
        return false;
    }



    return true;
}


}; /* namespace FatFS */
