/*
 * This file is part of the Aaru Data Preservation Suite.
 * Copyright (c) 2019-2025 Natalia Portillo.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <aaruformat.h>
#include <unicode/ucnv.h>
#include <unicode/ustring.h>

#include "aaruformattool.h"

int convert(const char *input_path, const char *output_path, bool use_long)
{
    aaruformat_context *input_ctx     = NULL;
    aaruformat_context *output_ctx    = NULL;
    int32_t             res           = 0;
    uint32_t            sector_size   = 0;
    uint64_t            total_sectors = 0;
    uint8_t            *sector_data   = NULL;
    uint8_t             sector_status = 0;

    printf("Converting image from %s to %s%s...\n", input_path, output_path, use_long ? " (long mode)" : "");

    // Open input image
    input_ctx = aaruf_open(input_path, false, NULL);
    if(input_ctx == NULL)
    {
        printf("Error %d when opening input AaruFormat image.\n", errno);
        return errno;
    }

    // Get image information from input
    total_sectors = input_ctx->image_info.Sectors;
    sector_size   = input_ctx->image_info.SectorSize;

    printf("Input image has %llu sectors of %u bytes each.\n", total_sectors, sector_size);

    const char *app_name_utf8     = "aaruformattool";
    size_t      app_name_utf8_len = strlen(app_name_utf8);

    // Create output image
    output_ctx = aaruf_create(output_path, input_ctx->image_info.MediaType, sector_size, total_sectors,
                              0,     // negative sectors
                              0,     // overflow sectors
                              NULL,  // options
                              app_name_utf8,
                              app_name_utf8_len,  // application name length in bytes
                              1,                  // major version
                              0,                  // minor version
                              false);

    if(output_ctx == NULL)
    {
        printf("Error %d when creating output AaruFormat image.\n", errno);
        aaruf_close(input_ctx);
        return errno;
    }

    size_t tracks_size = 0;
    res                = aaruf_get_tracks(input_ctx, sector_data, &tracks_size);

    if(res != AARUF_ERROR_BUFFER_TOO_SMALL && res != AARUF_ERROR_TRACK_NOT_FOUND)
    {
        printf("Error %d when getting tracks from input image.\n", res);
        aaruf_close(input_ctx);
        aaruf_close(output_ctx);
        return res;
    }

    if(res == AARUF_ERROR_BUFFER_TOO_SMALL)
    {
        sector_data = malloc(tracks_size);
        if(sector_data == NULL)
        {
            printf("Error allocating memory for tracks buffer.\n");
            aaruf_close(input_ctx);
            aaruf_close(output_ctx);
            return AARUF_ERROR_NOT_ENOUGH_MEMORY;
        }

        res = aaruf_get_tracks(input_ctx, sector_data, &tracks_size);
        if(res != AARUF_STATUS_OK)
        {
            printf("Error %d when getting tracks from input image.\n", res);
            free(sector_data);
            aaruf_close(input_ctx);
            aaruf_close(output_ctx);
            return res;
        }

        res = aaruf_set_tracks(output_ctx, (TrackEntry *)sector_data, (int)(tracks_size / sizeof(TrackEntry)));
        if(res != AARUF_STATUS_OK)
        {
            printf("Error %d when setting tracks on output image.\n", res);
            free(sector_data);
            aaruf_close(input_ctx);
            aaruf_close(output_ctx);
            return res;
        }

        free(sector_data);
        sector_data = NULL;
    }

    // Allocate buffer for sector data
    sector_data = malloc(sector_size);
    if(sector_data == NULL)
    {
        printf("Error allocating memory for sector buffer.\n");
        aaruf_close(input_ctx);
        aaruf_close(output_ctx);
        return AARUF_ERROR_NOT_ENOUGH_MEMORY;
    }

    // Copy sectors from input to output
    for(uint64_t sector = 0; sector < total_sectors; sector++)
    {
        uint32_t read_length = 0;

        // Show progress every 1000 sectors
        if(sector % 1000 == 0 || sector == total_sectors - 1)
        {
            printf("\rProgress: %llu/%llu sectors (%.1f%%)", sector + 1, total_sectors,
                   (double)(sector + 1) / total_sectors * 100.0);
            fflush(stdout);
        }

        // Check sector size
        if(use_long)
            res = aaruf_read_sector_long(input_ctx, sector, false, sector_data, &read_length, &sector_status);
        else
            res = aaruf_read_sector(input_ctx, sector, false, sector_data, &read_length, &sector_status);

        if(res != AARUF_ERROR_BUFFER_TOO_SMALL)
        {
            printf("\nError %d when reading sector %llu from input image.\n", res, (unsigned long long)sector);
            break;
        }

        if(sector_size < read_length)
        {
            free(sector_data);
            sector_size = read_length;
            sector_data = malloc(sector_size);
            if(sector_data == NULL)
            {
                printf("Error allocating memory for sector buffer.\n");
                aaruf_close(input_ctx);
                aaruf_close(output_ctx);
                return AARUF_ERROR_NOT_ENOUGH_MEMORY;
            }
        }

        // Read sector from input
        if(use_long)
            res = aaruf_read_sector_long(input_ctx, sector, false, sector_data, &read_length, &sector_status);
        else
            res = aaruf_read_sector(input_ctx, sector, false, sector_data, &read_length, &sector_status);

        if(res != AARUF_STATUS_OK)
        {
            printf("\nError %d when reading sector %llu from input image.\n", res, (unsigned long long)sector);
            break;
        }

        // Write sector to output
        if(use_long)
            res = aaruf_write_sector_long(output_ctx, sector, false, sector_data, sector_status, read_length);
        else
            res = aaruf_write_sector(output_ctx, sector, false, sector_data, sector_status, read_length);

        if(res != AARUF_STATUS_OK)
        {
            printf("\nError %d when writing sector %llu to output image.\n", res, (unsigned long long)sector);
            break;
        }
    }

    size_t tracks_length = 0;
    res                  = aaruf_get_tracks(input_ctx, NULL, &tracks_length);
    if(res != AARUF_ERROR_TRACK_NOT_FOUND && res == AARUF_ERROR_BUFFER_TOO_SMALL)
    {
        uint8_t *tracks = calloc(1, tracks_length);

        res = aaruf_get_tracks(input_ctx, tracks, &tracks_length);
        if(res == AARUF_STATUS_OK)
        {
            res = aaruf_set_tracks(output_ctx, (TrackEntry *)tracks, (int)(tracks_length / sizeof(TrackEntry)));
            if(res != AARUF_STATUS_OK) printf("\nError %d when setting tracks on output image.\n", res);
        }
    }

    printf("\n");

    // Clean up
    free(sector_data);
    aaruf_close(input_ctx);
    res = aaruf_close(output_ctx);

    if(res == AARUF_STATUS_OK)
        printf("Conversion completed successfully.\n");
    else
        printf("Conversion failed with error %d.\n", res);

    return res;
}
