//------------------------------------------------------------------------------
/// Maps a logical block number to an actual physical block. This allocates
/// the physical block (meaning it must be FREE), and releases the previous
/// block being replaced (if any).
/// Returns 0 if successful; otherwise returns a NandCommon_ERROR_xxx code.
/// \param mapped  Pointer to a MappedNandFlash instance.
/// \param logicalBlock  Logical block number to map.
/// \param physicalBlock  Physical block to map to the logical one.
//------------------------------------------------------------------------------
unsigned char MappedNandFlash_Map(
    struct MappedNandFlash *mapped,
    unsigned short logicalBlock,
    unsigned short physicalBlock)
{
    unsigned char error;
    signed short oldPhysicalBlock;

    TRACE_INFO("MappedNandFlash_Map(LB#%d -> PB#%d)\r\n",
               logicalBlock, physicalBlock);
    ASSERT(
       logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)),
       "MappedNandFlash_Map: logicalBlock out-of-range\r\n");
    ASSERT(
       physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)),
       "MappedNandFlash_Map: physicalBlock out-of-range\r\n");

    // Allocate physical block
    error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock);
    if (error) {

        return error;
    }

    // Release currently mapped block (if any)
    oldPhysicalBlock = mapped->logicalMapping[logicalBlock];
    if (oldPhysicalBlock != -1) {

        error =
            ManagedNandFlash_ReleaseBlock(MANAGED(mapped), oldPhysicalBlock);
        if (error) {

            return error;
        }
    }

    // Set mapping
    mapped->logicalMapping[logicalBlock] = physicalBlock;
    mapped->mappingModified = 1;

    return 0;
}
/**
 * \brief  Maps a logical block number to an actual physical block. This allocates
 * the physical block (meaning it must be FREE), and releases the previous
 * block being replaced (if any).
 * \param mapped  Pointer to a MappedNandFlash instance.
 * \param logicalBlock  Logical block number to map.
 * \param physicalBlock  Physical block to map to the logical one.
 * \return  0 if successful; otherwise returns a NandCommon_ERROR_xxx code.
 */
unsigned char MappedNandFlash_Map(
    struct MappedNandFlash *mapped,
    unsigned short logicalBlock,
    unsigned short physicalBlock)
{
    unsigned char error;
    signed short oldPhysicalBlock;

    TRACE_INFO("MappedNandFlash_Map(LB#%d -> PB#%d)\n\r",
               logicalBlock, physicalBlock);
    assert( logicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)) ) ; /* "MappedNandFlash_Map: logicalBlock out-of-range\n\r" */
    assert( physicalBlock < ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped)) ) ; /* "MappedNandFlash_Map: physicalBlock out-of-range\n\r" */

    /* Allocate physical block*/
    error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock);
    if ( error )
    {
        return error;
    }

    /* Release currently mapped block (if any)*/
    oldPhysicalBlock = mapped->logicalMapping[logicalBlock];
    if (oldPhysicalBlock != -1)
    {
        error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped), oldPhysicalBlock);
        if ( error )
        {
            return error;
        }
    }

    /* Set mapping*/
    mapped->logicalMapping[logicalBlock] = physicalBlock;
    mapped->mappingModified = 1;

    return 0;
}
/**
 * \brief  Saves the logical mapping on a FREE, unmapped physical block. Allocates the
 * new block, releases the previous one (if any) and save the mapping.
 *
 * \param mapped  Pointer to a MappedNandFlash instance.
 * \param physicalBlock  Physical block number.
 * \return  0 if successful; otherwise, returns NandCommon_ERROR_WRONGSTATUS
 * if the block is not LIVE, or a NandCommon_ERROR code.
 */
unsigned char MappedNandFlash_SaveLogicalMapping(
    struct MappedNandFlash *mapped,
    unsigned short physicalBlock)
{
    unsigned char error;
    unsigned char data[NandCommon_MAXPAGEDATASIZE];
    unsigned short pageDataSize =
                    NandFlashModel_GetPageDataSize(MODEL(mapped));
    /*unsigned short numBlocks =
                    ManagedNandFlash_GetDeviceSizeInBlocks(MANAGED(mapped));*/
    unsigned int i;
    unsigned int remainingSize;
    unsigned char *currentBuffer;
    unsigned short currentPage;
    unsigned int writeSize;
    signed short previousPhysicalBlock;

    TRACE_INFO("MappedNandFlash_SaveLogicalMapping(B#%d)\n\r", physicalBlock);

    /* If mapping has not been modified, do nothing*/
    if (!mapped->mappingModified) {

        return 0;
    }

    /* Allocate new block*/
    error = ManagedNandFlash_AllocateBlock(MANAGED(mapped), physicalBlock);
    if (error) {

        return error;
    }

    /* Save mapping*/
    previousPhysicalBlock = mapped->logicalMappingBlock;
    mapped->logicalMappingBlock = physicalBlock;

    /* Save actual mapping in pages #1-#XXX*/
    currentBuffer = (unsigned char *) mapped->logicalMapping;
    remainingSize = sizeof(mapped->logicalMapping);
    currentPage = 1;
    while (remainingSize > 0) {

        writeSize = min(remainingSize, pageDataSize);
        memset(data, 0xFF, pageDataSize);
        memcpy(data, currentBuffer, writeSize);
        error = ManagedNandFlash_WritePage(MANAGED(mapped),
                                           physicalBlock,
                                           currentPage,
                                           data,
                                           0);
        if (error) {

            TRACE_ERROR(
             "MappedNandFlash_SaveLogicalMapping: Failed to write mapping\n\r");
            return error;
        }

        currentBuffer += writeSize;
        remainingSize -= writeSize;
        currentPage++;
    }

    /* Mark page #0 of block with a distinguishible pattern, so the mapping can
       be retrieved at startup*/
    for (i=0; i < pageDataSize; i++) {

        data[i] = PATTERN(i);
    }
    error = ManagedNandFlash_WritePage(MANAGED(mapped),
                                       physicalBlock, 0,
                                       data, 0);
    if (error) {

        TRACE_ERROR(
            "MappedNandFlash_SaveLogicalMapping: Failed to write pattern\n\r");
        return error;
    }

    /* Mapping is not modified anymore*/
    mapped->mappingModified = 0;

    /* Release previous block (if any)*/
    if (previousPhysicalBlock != -1) {

        TRACE_DEBUG("Previous physical block was #%d\n\r",
                    previousPhysicalBlock);
        error = ManagedNandFlash_ReleaseBlock(MANAGED(mapped),
                                              previousPhysicalBlock);
        if (error) {

            return error;
        }
    }

    TRACE_INFO("Mapping saved on block #%d\n\r", physicalBlock);

    return 0;
}