#include "hex_viewer_storage.h"

static Storage* hex_viewer_open_storage() {
    return furi_record_open(RECORD_STORAGE);
}

static void hex_viewer_close_storage() {
    furi_record_close(RECORD_STORAGE);
}

static void hex_viewer_close_config_file(FlipperFormat* file) {
    if(file == NULL) return;
    flipper_format_file_close(file);
    flipper_format_free(file);
}

void hex_viewer_save_settings(void* context) {
    HexViewer* app = context;
    if(app->save_settings == 0) {
        return;
    }

    FURI_LOG_D(TAG, "Saving Settings");
    Storage* storage = hex_viewer_open_storage();
    FlipperFormat* fff_file = flipper_format_file_alloc(storage);

    // Overwrite wont work, so delete first
    if(storage_file_exists(storage, HEX_VIEWER_SETTINGS_SAVE_PATH)) {
        storage_simply_remove(storage, HEX_VIEWER_SETTINGS_SAVE_PATH);
    }

    // Open File, create if not exists
    if(!storage_common_stat(storage, HEX_VIEWER_SETTINGS_SAVE_PATH, NULL) == FSE_OK) {
        FURI_LOG_D(
            TAG, "Config file %s is not found. Will create new.", HEX_VIEWER_SETTINGS_SAVE_PATH);
        if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) {
            FURI_LOG_D(
                TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH);
            if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) {
                FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH);
            }
        }
    }

    if(!flipper_format_file_open_new(fff_file, HEX_VIEWER_SETTINGS_SAVE_PATH)) {
        //totp_close_config_file(fff_file);
        FURI_LOG_E(TAG, "Error creating new file %s", HEX_VIEWER_SETTINGS_SAVE_PATH);
        hex_viewer_close_storage();
        return;
    }

    // Store Settings
    flipper_format_write_header_cstr(
        fff_file, HEX_VIEWER_SETTINGS_HEADER, HEX_VIEWER_SETTINGS_FILE_VERSION);
    flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
    flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
    flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_LED, &app->led, 1);
    flipper_format_write_uint32(
        fff_file, HEX_VIEWER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1);

    if(!flipper_format_rewind(fff_file)) {
        hex_viewer_close_config_file(fff_file);
        FURI_LOG_E(TAG, "Rewind error");
        hex_viewer_close_storage();
        return;
    }

    hex_viewer_close_config_file(fff_file);
    hex_viewer_close_storage();
}

void hex_viewer_read_settings(void* context) {
    HexViewer* app = context;
    Storage* storage = hex_viewer_open_storage();
    FlipperFormat* fff_file = flipper_format_file_alloc(storage);

    if(storage_common_stat(storage, HEX_VIEWER_SETTINGS_SAVE_PATH, NULL) != FSE_OK) {
        hex_viewer_close_config_file(fff_file);
        hex_viewer_close_storage();
        return;
    }
    uint32_t file_version;
    FuriString* temp_str = furi_string_alloc();

    if(!flipper_format_file_open_existing(fff_file, HEX_VIEWER_SETTINGS_SAVE_PATH)) {
        FURI_LOG_E(TAG, "Cannot open file %s", HEX_VIEWER_SETTINGS_SAVE_PATH);
        hex_viewer_close_config_file(fff_file);
        hex_viewer_close_storage();
        return;
    }

    if(!flipper_format_read_header(fff_file, temp_str, &file_version)) {
        FURI_LOG_E(TAG, "Missing Header Data");
        hex_viewer_close_config_file(fff_file);
        hex_viewer_close_storage();
        furi_string_free(temp_str);
        return;
    }
    furi_string_free(temp_str);

    if(file_version < HEX_VIEWER_SETTINGS_FILE_VERSION) {
        FURI_LOG_I(TAG, "old config version, will be removed.");
        hex_viewer_close_config_file(fff_file);
        hex_viewer_close_storage();
        return;
    }

    flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_HAPTIC, &app->haptic, 1);
    flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_SPEAKER, &app->speaker, 1);
    flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_LED, &app->led, 1);
    flipper_format_read_uint32(
        fff_file, HEX_VIEWER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1);

    flipper_format_rewind(fff_file);

    hex_viewer_close_config_file(fff_file);
    hex_viewer_close_storage();
}

bool hex_viewer_open_file(void* context, const char* file_path) {
    HexViewer* hex_viewer = context;
    furi_assert(hex_viewer);
    furi_assert(file_path);

    // TODO Separate function?
    if(hex_viewer->model->stream) {
        buffered_file_stream_close(hex_viewer->model->stream);
        stream_free(hex_viewer->model->stream);
        hex_viewer->model->file_offset = 0;
    }

    hex_viewer->model->stream = buffered_file_stream_alloc(hex_viewer->storage);
    bool isOk = true;

    do {
        if(!buffered_file_stream_open(
               hex_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
            FURI_LOG_E(TAG, "Unable to open stream: %s", file_path);
            isOk = false;
            break;
        };

        hex_viewer->model->file_size = stream_size(hex_viewer->model->stream);
    } while(false);

    return isOk;
}

bool hex_viewer_read_file(void* context) {
    HexViewer* hex_viewer = context;
    furi_assert(hex_viewer);
    furi_assert(hex_viewer->model->stream);
    furi_assert(hex_viewer->model->file_offset % HEX_VIEWER_BYTES_PER_LINE == 0);

    memset(hex_viewer->model->file_bytes, 0x0, HEX_VIEWER_BUF_SIZE);
    bool isOk = true;

    do {
        uint32_t offset = hex_viewer->model->file_offset;
        if(!stream_seek(hex_viewer->model->stream, offset, true)) {
            FURI_LOG_E(TAG, "Unable to seek stream");
            isOk = false;
            break;
        }

        hex_viewer->model->file_read_bytes = stream_read(
            hex_viewer->model->stream,
            (uint8_t*)hex_viewer->model->file_bytes,
            HEX_VIEWER_BUF_SIZE);
    } while(false);

    return isOk;
}