#include "file_helper.h"

const char flipper_file_eoln = '\n';
const char flipper_file_eolr = '\r';

bool file_helper_seek(File* file, int32_t offset) {
    uint64_t position = storage_file_tell(file);
    return storage_file_seek(file, position + offset, true);
}

bool file_helper_write_hex(File* file, const uint8_t* data, const uint16_t data_size) {
    const uint8_t byte_text_size = 3;
    char byte_text[byte_text_size];

    bool result = true;
    uint16_t bytes_written;
    for(uint8_t i = 0; i < data_size; i++) {
        snprintf(byte_text, byte_text_size, "%02X", data[i]);

        if(i != 0) {
            // space
            const char space = ' ';
            bytes_written = storage_file_write(file, &space, sizeof(char));
            if(bytes_written != sizeof(char)) {
                result = false;
                break;
            }
        }

        bytes_written = storage_file_write(file, &byte_text, strlen(byte_text));
        if(bytes_written != strlen(byte_text)) {
            result = false;
            break;
        }
    }

    return result;
}

bool file_helper_read_line(File* file, string_t str_result) {
    string_clean(str_result);
    const uint8_t buffer_size = 32;
    uint8_t buffer[buffer_size];

    do {
        uint16_t bytes_were_read = storage_file_read(file, buffer, buffer_size);
        // TODO process EOF
        if(bytes_were_read == 0) break;

        bool result = false;
        bool error = false;
        for(uint16_t i = 0; i < bytes_were_read; i++) {
            if(buffer[i] == flipper_file_eoln) {
                if(!file_helper_seek(file, i - bytes_were_read)) {
                    error = true;
                    break;
                }

                result = true;
                break;
            } else if(buffer[i] == flipper_file_eolr) {
                // Ignore
            } else {
                string_push_back(str_result, buffer[i]);
            }
        }

        if(result || error) {
            break;
        }
    } while(true);

    return string_size(str_result) != 0;
}

bool file_helper_seek_to_next_line(File* file) {
    const uint8_t buffer_size = 32;
    uint8_t buffer[buffer_size];
    bool result = false;
    bool error = false;

    do {
        uint16_t bytes_were_read = storage_file_read(file, buffer, buffer_size);
        if(bytes_were_read == 0) {
            if(storage_file_eof(file)) {
                result = true;
                break;
            }
        }

        for(uint16_t i = 0; i < bytes_were_read; i++) {
            if(buffer[i] == flipper_file_eoln) {
                if(!file_helper_seek(file, i - bytes_were_read)) {
                    error = true;
                    break;
                }

                result = true;
                break;
            }
        }

        if(result || error) {
            break;
        }
    } while(true);

    return result;
}

bool file_helper_read_value(File* file, string_t value, bool* last) {
    string_clean(value);
    const uint8_t buffer_size = 32;
    uint8_t buffer[buffer_size];
    bool result = false;
    bool error = false;

    while(true) {
        uint16_t bytes_were_read = storage_file_read(file, buffer, buffer_size);

        if(bytes_were_read == 0) {
            // check EOF
            if(storage_file_eof(file) && string_size(value) > 0) {
                result = true;
                *last = true;
                break;
            }
        }

        for(uint16_t i = 0; i < bytes_were_read; i++) {
            if(buffer[i] == flipper_file_eoln) {
                if(string_size(value) > 0) {
                    if(!file_helper_seek(file, i - bytes_were_read)) {
                        error = true;
                        break;
                    }

                    result = true;
                    *last = true;
                    break;
                } else {
                    error = true;
                }
            } else if(buffer[i] == ' ') {
                if(string_size(value) > 0) {
                    if(!file_helper_seek(file, i - bytes_were_read)) {
                        error = true;
                        break;
                    }

                    result = true;
                    *last = false;
                    break;
                }

            } else if(buffer[i] == flipper_file_eolr) {
                // Ignore
            } else {
                string_push_back(value, buffer[i]);
            }
        }

        if(error || result) break;
    }

    return result;
}

bool file_helper_write(File* file, const void* data, uint16_t data_size) {
    uint16_t bytes_written = storage_file_write(file, data, data_size);
    return bytes_written == data_size;
}

bool file_helper_write_eol(File* file) {
    return file_helper_write(file, &flipper_file_eoln, sizeof(char));
}

bool file_helper_copy(File* file_from, File* file_to, uint64_t start_offset, uint64_t stop_offset) {
    bool result = false;

    const uint8_t buffer_size = 32;
    uint8_t buffer[buffer_size];
    uint64_t current_offset = start_offset;

    if(storage_file_seek(file_from, start_offset, true)) {
        do {
            int32_t bytes_count = MIN(buffer_size, stop_offset - current_offset);
            if(bytes_count <= 0) {
                result = true;
                break;
            }

            uint16_t bytes_were_read = storage_file_read(file_from, buffer, bytes_count);
            if(bytes_were_read != bytes_count) break;

            uint16_t bytes_were_written = storage_file_write(file_to, buffer, bytes_count);
            if(bytes_were_written != bytes_count) break;

            current_offset += bytes_count;
        } while(true);
    }

    return result;
}