/**
 * @file img_func.c
 * @brief Image rendering and conversion helpers using Chafa.
 *
 * Handles loading and displaying album art or other images in the terminal
 * using the Chafa library. Supports scaling, aspect-ratio preservation,
 * and different rendering modes (truecolor, ASCII, etc.).
 */

#include "common/common.h"

#include "utils/img_utils.h"

#include "img_func.h"

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Include after chafa.h for G_OS_WIN32 */
#ifdef G_OS_WIN32
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <io.h>
#else
#include <sys/ioctl.h> /* ioctl */
#endif

#if CHAFA_VERSION_CUR_STABLE >= G_ENCODE_VERSION(1, 16)

static gchar *tmux_allow_passthrough_original;
static gboolean tmux_allow_passthrough_is_changed;

static gboolean
apply_passthrough_workarounds_tmux(void)
{
        gboolean result = FALSE;
        gchar *standard_output = NULL;
        gchar *standard_error = NULL;
        gchar **argv = NULL;
        gint wait_status = -1;
        gchar *mode = NULL;

        /* Use g_spawn_sync with explicit argv to avoid shell injection */
        argv = g_new0(gchar *, 4);
        argv[0] = g_strdup("tmux");
        argv[1] = g_strdup("show");
        argv[2] = g_strdup("allow-passthrough");
        argv[3] = NULL;

        if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
                          NULL, NULL, &standard_output, &standard_error,
                          &wait_status, NULL)) {
                g_strfreev(argv);
                goto out;
        }
        g_strfreev(argv);

        /* Parse output safely */
        if (standard_output && *standard_output) {
                gchar **lines = g_strsplit(standard_output, "\n", 2);
                if (lines[0]) {
                        gchar **parts = g_strsplit(lines[0], " ", 3);
                        if (parts[0] && parts[1]) {
                                mode = g_ascii_strdown(parts[1], -1);
                                g_strstrip(mode);
                        }
                        g_strfreev(parts);
                }
                g_strfreev(lines);
        }

        if (!mode || (strcmp(mode, "on") && strcmp(mode, "all"))) {
                argv = g_new0(gchar *, 4);
                argv[0] = g_strdup("tmux");
                argv[1] = g_strdup("set-option");
                argv[2] = g_strdup("allow-passthrough on");
                argv[3] = NULL;

                result = g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
                                      NULL, NULL, &standard_output, &standard_error,
                                      &wait_status, NULL);
                g_strfreev(argv);

                if (result) {
                        tmux_allow_passthrough_original = mode;
                        tmux_allow_passthrough_is_changed = TRUE;
                }
        } else {
                g_free(mode);
        }

out:
        g_free(standard_output);
        g_free(standard_error);
        g_free(mode);
        return result;
}

gboolean
retirePassthroughWorkarounds_tmux(void)
{
        gboolean result = FALSE;
        gchar *standard_output = NULL;
        gchar *standard_error = NULL;
        gint wait_status = -1;
        gchar **argv = NULL;

        if (!tmux_allow_passthrough_is_changed)
                return TRUE;

        if (tmux_allow_passthrough_original) {
                // Use argument array to avoid shell injection
                argv = g_new0(gchar *, 5);
                argv[0] = g_strdup("tmux");
                argv[1] = g_strdup("set-option");
                argv[2] = g_strdup("allow-passthrough");
                argv[3] = g_strdup(tmux_allow_passthrough_original);
                argv[4] = NULL;
        } else {
                // Use argument array for unsetting the option
                argv = g_new0(gchar *, 4);
                argv[0] = g_strdup("tmux");
                argv[1] = g_strdup("set-option");
                argv[2] = g_strdup("-u");
                argv[3] = g_strdup("allow-passthrough");
        }

        result = g_spawn_sync(
            NULL, argv, NULL,
            G_SPAWN_SEARCH_PATH,
            NULL, NULL,
            &standard_output, &standard_error,
            &wait_status, NULL);

        // Free the argument array
        for (int i = 0; argv[i] != NULL; i++)
                g_free(argv[i]);
        g_free(argv);

        if (result) {
                g_free(tmux_allow_passthrough_original);
                tmux_allow_passthrough_original = NULL;
                tmux_allow_passthrough_is_changed = FALSE;
        }

        g_free(standard_output);
        g_free(standard_error);
        return result;
}

static void detect_terminal(ChafaTermInfo **term_info_out, ChafaCanvasMode *mode_out, ChafaPixelMode *pixel_mode_out,
                            ChafaPassthrough *passthrough_out, ChafaSymbolMap **symbol_map_out)
{
        ChafaCanvasMode mode;
        ChafaPixelMode pixel_mode;
        ChafaPassthrough passthrough;
        ChafaTermInfo *term_info;
        gchar **envp;

        /* Examine the environment variables and guess what the terminal can do */

        envp = g_get_environ();
        term_info = chafa_term_db_detect(chafa_term_db_get_default(), envp);

        /* Pick the most high-quality rendering possible */

        mode = chafa_term_info_get_best_canvas_mode(term_info);
        pixel_mode = chafa_term_info_get_best_pixel_mode(term_info);
        passthrough = chafa_term_info_get_is_pixel_passthrough_needed(term_info, pixel_mode)
                          ? chafa_term_info_get_passthrough_type(term_info)
                          : CHAFA_PASSTHROUGH_NONE;

        const gchar *term_name = chafa_term_info_get_name(term_info);

        if (strstr(term_name, "tmux") != NULL && pixel_mode != CHAFA_PIXEL_MODE_KITTY) {
                /* Always use sixels in tmux */
                pixel_mode = CHAFA_PIXEL_MODE_SIXELS;
                mode = CHAFA_CANVAS_MODE_TRUECOLOR;
        }

        *symbol_map_out = chafa_symbol_map_new();
        chafa_symbol_map_add_by_tags(*symbol_map_out,
                                     chafa_term_info_get_safe_symbol_tags(term_info));

        /* Hand over the information to caller */

        *term_info_out = term_info;
        *mode_out = mode;
        *pixel_mode_out = pixel_mode;
        *passthrough_out = passthrough;

        /* Cleanup */

        g_strfreev(envp);
}

#else

static void detect_terminal(ChafaTermInfo **term_info_out, ChafaCanvasMode *mode_out, ChafaPixelMode *pixel_mode_out)
{
        ChafaCanvasMode mode;
        ChafaPixelMode pixel_mode;
        ChafaTermInfo *term_info;
        gchar **envp;

        /* Examine the environment variables and guess what the terminal can do */

        envp = g_get_environ();
        term_info = chafa_term_db_detect(chafa_term_db_get_default(), envp);

        /* See which control sequences were defined, and use that to pick the most
         * high-quality rendering possible */

        if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_BEGIN_KITTY_IMMEDIATE_IMAGE_V1)) {
                pixel_mode = CHAFA_PIXEL_MODE_KITTY;
                mode = CHAFA_CANVAS_MODE_TRUECOLOR;
        } else if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_BEGIN_SIXELS)) {
                pixel_mode = CHAFA_PIXEL_MODE_SIXELS;
                mode = CHAFA_CANVAS_MODE_TRUECOLOR;
        } else {
                pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;

                if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FG_DIRECT) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_BG_DIRECT))
                        mode = CHAFA_CANVAS_MODE_TRUECOLOR;
                else if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FG_256) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_BG_256))
                        mode = CHAFA_CANVAS_MODE_INDEXED_240;
                else if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_FG_16) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_SET_COLOR_BG_16))
                        mode = CHAFA_CANVAS_MODE_INDEXED_16;
                else if (chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_INVERT_COLORS) && chafa_term_info_have_seq(term_info, CHAFA_TERM_SEQ_RESET_ATTRIBUTES))
                        mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
                else
                        mode = CHAFA_CANVAS_MODE_FGBG;
        }

        /* Hand over the information to caller */

        *term_info_out = term_info;
        *mode_out = mode;
        *pixel_mode_out = pixel_mode;

        /* Cleanup */

        g_strfreev(envp);
}
#endif

void get_tty_size(TermSize *term_size_out)
{
        TermSize term_size;

        term_size.width_cells = term_size.height_cells = term_size.width_pixels = term_size.height_pixels = -1;

#ifdef G_OS_WIN32
        {
                HANDLE chd = GetStdHandle(STD_OUTPUT_HANDLE);
                CONSOLE_SCREEN_BUFFER_INFO csb_info;

                if (chd != INVALID_HANDLE_VALUE && GetConsoleScreenBufferInfo(chd, &csb_info)) {
                        term_size.width_cells = csb_info.srWindow.Right - csb_info.srWindow.Left + 1;
                        term_size.height_cells = csb_info.srWindow.Bottom - csb_info.srWindow.Top + 1;
                }
        }
#else
        {
                struct winsize w;
                gboolean have_winsz = FALSE;

                if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) >= 0 || ioctl(STDERR_FILENO, TIOCGWINSZ, &w) >= 0 || ioctl(STDIN_FILENO, TIOCGWINSZ, &w) >= 0)
                        have_winsz = TRUE;

                if (have_winsz) {
                        term_size.width_cells = w.ws_col;
                        term_size.height_cells = w.ws_row;
                        term_size.width_pixels = w.ws_xpixel;
                        term_size.height_pixels = w.ws_ypixel;
                }
        }
#endif

        if (term_size.width_cells <= 0)
                term_size.width_cells = -1;
        if (term_size.height_cells <= 2)
                term_size.height_cells = -1;

        /* If .ws_xpixel and .ws_ypixel are filled out, we can calculate
         * aspect information for the font used. Sixel-capable terminals
         * like mlterm set these fields, but most others do not. */

        if (term_size.width_pixels <= 0 || term_size.height_pixels <= 0) {
                term_size.width_pixels = -1;
                term_size.height_pixels = -1;
        }

        *term_size_out = term_size;
}

void tty_init(void)
{
#ifdef G_OS_WIN32
        {
                HANDLE chd = GetStdHandle(STD_OUTPUT_HANDLE);

                saved_console_output_cp = GetConsoleOutputCP();
                saved_console_input_cp = GetConsoleCP();

                /* Enable ANSI escape sequence parsing etc. on MS Windows command prompt */

                if (chd != INVALID_HANDLE_VALUE) {
                        if (!SetConsoleMode(chd,
                                            ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN))
                                win32_stdout_is_file = TRUE;
                }

                /* Set UTF-8 code page I/O */

                SetConsoleOutputCP(65001);
                SetConsoleCP(65001);
        }
#endif
}

static GString *
convert_image(const void *pixels, gint pix_width, gint pix_height,
              gint pix_rowstride, ChafaPixelType pixel_type,
              gint width_cells, gint height_cells,
              gint cell_width, gint cell_height)
{
        ChafaTermInfo *term_info;
        ChafaCanvasMode mode;
        ChafaPixelMode pixel_mode;
        ChafaSymbolMap *symbol_map;
        ChafaCanvasConfig *config;
        ChafaCanvas *canvas;
        GString *printable;

#if CHAFA_VERSION_CUR_STABLE >= G_ENCODE_VERSION(1, 16)

        ChafaPassthrough passthrough;
        ChafaFrame *frame;
        ChafaImage *image;
        ChafaPlacement *placement;

        detect_terminal(&term_info, &mode, &pixel_mode,
                        &passthrough, &symbol_map);

        if (passthrough == CHAFA_PASSTHROUGH_TMUX)
                apply_passthrough_workarounds_tmux();

        config = chafa_canvas_config_new();
        chafa_canvas_config_set_canvas_mode(config, mode);
        chafa_canvas_config_set_pixel_mode(config, pixel_mode);
        chafa_canvas_config_set_geometry(config, width_cells, height_cells);

        if (cell_width > 0 && cell_height > 0) {
                /* We know the pixel dimensions of each cell. Store it in the config. */

                chafa_canvas_config_set_cell_geometry(config, cell_width, cell_height);
        }

        chafa_canvas_config_set_passthrough(config, passthrough);
        chafa_canvas_config_set_symbol_map(config, symbol_map);

        canvas = chafa_canvas_new(config);
        frame = chafa_frame_new_borrow((gpointer)pixels, pixel_type,
                                       pix_width, pix_height, pix_rowstride);
        image = chafa_image_new();
        chafa_image_set_frame(image, frame);

        placement = chafa_placement_new(image, 1);
        chafa_placement_set_tuck(placement, CHAFA_TUCK_FIT);
        chafa_placement_set_halign(placement, CHAFA_ALIGN_CENTER);
        chafa_placement_set_valign(placement, CHAFA_ALIGN_CENTER);
        chafa_canvas_set_placement(canvas, placement);

        printable = chafa_canvas_print(canvas, NULL);

        /* Clean up and return */

        chafa_placement_unref(placement);
        chafa_image_unref(image);
        chafa_frame_unref(frame);
        chafa_canvas_unref(canvas);
        chafa_canvas_config_unref(config);
        chafa_symbol_map_unref(symbol_map);
        chafa_term_info_unref(term_info);
        canvas = NULL;
        config = NULL;
        symbol_map = NULL;
        term_info = NULL;

        return printable;
#elif CHAFA_VERSION_CUR_STABLE >= G_ENCODE_VERSION(1, 14)

        ChafaFrame *frame;
        ChafaImage *image;
        ChafaPlacement *placement;

        detect_terminal(&term_info, &mode, &pixel_mode);

        symbol_map = chafa_symbol_map_new();
        chafa_symbol_map_add_by_tags(symbol_map, CHAFA_SYMBOL_TAG_BLOCK);

        config = chafa_canvas_config_new();
        chafa_canvas_config_set_canvas_mode(config, mode);
        chafa_canvas_config_set_pixel_mode(config, pixel_mode);
        chafa_canvas_config_set_geometry(config, width_cells, height_cells);
        chafa_canvas_config_set_symbol_map(config, symbol_map);

        if (cell_width > 0 && cell_height > 0)
                chafa_canvas_config_set_cell_geometry(config, cell_width, cell_height);

        canvas = chafa_canvas_new(config);
        frame = chafa_frame_new_borrow((gpointer)pixels, pixel_type, pix_width,
                                         pix_height, pix_rowstride);
        image = chafa_image_new();
        chafa_image_set_frame(image, frame);

        placement = chafa_placement_new(image, 1);
        chafa_placement_set_tuck(placement, CHAFA_TUCK_FIT);
        chafa_placement_set_halign(placement, CHAFA_ALIGN_CENTER);
        chafa_placement_set_valign(placement, CHAFA_ALIGN_CENTER);
        chafa_canvas_set_placement(canvas, placement);

        printable = chafa_canvas_print(canvas, term_info);

        chafa_placement_unref(placement);
        chafa_image_unref(image);
        chafa_frame_unref(frame);
        chafa_canvas_unref(canvas);
        chafa_canvas_config_unref(config);
        chafa_symbol_map_unref(symbol_map);
        chafa_term_info_unref(term_info);
        canvas = NULL;
        config = NULL;
        symbol_map = NULL;
        term_info = NULL;

        return printable;
#else
        detect_terminal(&term_info, &mode, &pixel_mode);

        /* Specify the symbols we want */

        symbol_map = chafa_symbol_map_new();
        chafa_symbol_map_add_by_tags(symbol_map, CHAFA_SYMBOL_TAG_BLOCK);

        /* Set up a configuration with the symbols and the canvas size in characters */

        config = chafa_canvas_config_new();
        chafa_canvas_config_set_canvas_mode(config, mode);
        chafa_canvas_config_set_pixel_mode(config, pixel_mode);
        chafa_canvas_config_set_geometry(config, width_cells, height_cells);
        chafa_canvas_config_set_symbol_map(config, symbol_map);

        if (cell_width > 0 && cell_height > 0) {
                /* We know the pixel dimensions of each cell. Store it in the config. */

                chafa_canvas_config_set_cell_geometry(config, cell_width, cell_height);
        }

        /* Create canvas */

        canvas = chafa_canvas_new(config);

        /* Draw pixels to the canvas */

        chafa_canvas_draw_all_pixels(canvas,
                                     pixel_type,
                                     pixels,
                                     pix_width,
                                     pix_height,
                                     pix_rowstride);

        /* Build printable string */
        printable = chafa_canvas_print(canvas, term_info);

        /* Clean up and return */

        chafa_canvas_unref(canvas);
        chafa_canvas_config_unref(config);
        chafa_symbol_map_unref(symbol_map);
        chafa_term_info_unref(term_info);
        canvas = NULL;
        config = NULL;
        symbol_map = NULL;
        term_info = NULL;

        return printable;
#endif
}

float calc_aspect_ratio(void)
{
        TermSize term_size;
        gint cell_width = -1, cell_height = -1;

        tty_init();
        get_tty_size(&term_size);

        if (term_size.width_cells > 0 && term_size.height_cells > 0 && term_size.width_pixels > 0 && term_size.height_pixels > 0) {
                cell_width = term_size.width_pixels / term_size.width_cells;
                cell_height = term_size.height_pixels / term_size.height_cells;
        }

        // Set default for some terminals
        if (cell_width == -1 && cell_height == -1) {
                cell_width = 8;
                cell_height = 16;
        }

        return (float)cell_height / (float)cell_width;
}

float get_aspect_ratio()
{
        TermSize term_size;
        gint cell_width = -1, cell_height = -1;

        tty_init();
        get_tty_size(&term_size);

        if (term_size.width_cells > 0 && term_size.height_cells > 0 &&
            term_size.width_pixels > 0 && term_size.height_pixels > 0) {
                cell_width = term_size.width_pixels / term_size.width_cells;
                cell_height = term_size.height_pixels / term_size.height_cells;
        }

        // Set default cell size for some terminals
        if (cell_width == -1 || cell_height == -1) {
                cell_width = 8;
                cell_height = 16;
        }

        // Calculate corrected width based on aspect ratio correction
        return (float)cell_height / (float)cell_width;
}

void print_square_bitmap(int row, int col, unsigned char *pixels, int width, int height, int base_height, bool centered)
{
        if (pixels == NULL) {
                set_error_message("Invalid pixel data.\n");
                return;
        }

        // Use the provided width and height
        int pix_width = width;
        int pix_height = height;
        int n_channels = 4; // Assuming RGBA format

        // Validate the image dimensions
        if (pix_width == 0 || pix_height == 0) {
                set_error_message("Invalid image dimensions.\n");
                return;
        }

        TermSize term_size;
        GString *printable;
        gint cell_width = -1, cell_height = -1;

        tty_init();
        get_tty_size(&term_size);

        if (term_size.width_cells > 0 && term_size.height_cells > 0 &&
            term_size.width_pixels > 0 && term_size.height_pixels > 0) {
                cell_width = term_size.width_pixels / term_size.width_cells;
                cell_height = term_size.height_pixels / term_size.height_cells;
        }

        // Set default cell size for some terminals
        if (cell_width <= -1 || cell_height <= -1) {
                cell_width = 8;
                cell_height = 16;
        }

        if (cell_width == 0 || cell_height == 0) {
                set_error_message("Invalid image cell width dimensions.\n");
                return;
        }

        // Calculate corrected width based on aspect ratio correction
        float aspect_ratio_correction = (float)cell_height / (float)cell_width;
        int corrected_width = (int)(base_height * aspect_ratio_correction);

        if (term_size.width_cells > 0 && corrected_width > term_size.width_cells) {
                set_error_message("Invalid terminal dimensions.\n");
                return;
        }

        if (term_size.height_cells > 0 && base_height > term_size.height_cells) {
                set_error_message("Invalid terminal dimensions.\n");
                return;
        }

        // Convert image to a printable string using Chafa
        printable = convert_image(
            pixels,
            pix_width,
            pix_height,
            pix_width * n_channels,         // Row stride
            CHAFA_PIXEL_RGBA8_UNASSOCIATED, // Correct pixel format
            corrected_width,
            base_height,
            cell_width,
            cell_height);

        // Ensure the string is null-terminated
        g_string_append_c(printable, '\0');

        // Split the printable string into lines
        const gchar *delimiters = "\n";
        gchar **lines = g_strsplit(printable->str, delimiters, -1);

        if (centered)
                col = ((term_size.width_cells - corrected_width) / 2) + 1;

        // Print each line with indentation
        for (int i = 0; lines[i] != NULL; i++) {
                printf("\033[%d;%dH", row + i, col);
                printf("%s", lines[i]);
                fflush(stdout);
        }

        // Free allocated memory
        g_strfreev(lines);
        g_string_free(printable, TRUE);
}

int convert_to_ascii(int row, int col, const char *filepath, unsigned int height, bool centered)
{
        /*
        Modified, originally by Danny Burrows:
        https://github.com/danny-burrows/img_to_txt

        MIT License

        Copyright (c) 2021 Danny Burrows

        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:

        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.

        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        */

        TermSize term_size;
        gint cell_width = -1, cell_height = -1;

        tty_init();
        get_tty_size(&term_size);

        if (term_size.width_cells > 0 && term_size.height_cells > 0 &&
            term_size.width_pixels > 0 && term_size.height_pixels > 0) {
                cell_width = term_size.width_pixels / term_size.width_cells;
                cell_height = term_size.height_pixels / term_size.height_cells;
        }

        // Set default cell size for some terminals
        if (cell_width == -1 || cell_height == -1) {
                cell_width = 8;
                cell_height = 16;
        }

        float aspect_ratio_correction = (float)cell_height / (float)cell_width;
        unsigned int corrected_width = (int)(height * aspect_ratio_correction);

        int rwidth, rheight, rchannels;
        unsigned char *read_data = image_load_rgb(filepath, &rwidth, &rheight, &rchannels);

        if (read_data == NULL) {
                return -1;
        }

        float scale_w = (float)corrected_width / (float)rwidth;
        float scale_h = (float)height / (float)rheight;
        
        int fit_w = (int)(rwidth * scale_w + 0.5f);
        int fit_h = (int)(rheight * scale_h + 0.5f);
        if (fit_w < 1)
                fit_w = 1;
        if (fit_h < 1)
                fit_h = 1;
        if (fit_w > (int)corrected_width)
                fit_w = (int)corrected_width;
        if (fit_h > (int)height)
                fit_h = (int)height;

        unsigned char *composed =
            calloc((size_t)3 * corrected_width * height, 1);
        if (composed == NULL) {
                image_free(read_data);
                return -1;
        }

        unsigned char *fit_pixels = read_data;
        gboolean did_resize = FALSE;
        if (fit_w != rwidth || fit_h != rheight) {
                fit_pixels = malloc((size_t)3 * fit_w * fit_h);
                if (fit_pixels == NULL) {
                        image_free(read_data);
                        free(composed);
                        return -1;
                }
                image_resize_uint8_srgb(read_data, rwidth, rheight, fit_pixels,
                                        fit_w, fit_h, 3);
                image_free(read_data);
                did_resize = TRUE;
        }

        int off_x = ((int)corrected_width - fit_w) / 2;
        int off_y = ((int)height - fit_h) / 2;
        for (int y = 0; y < fit_h; y++) {
                for (int x = 0; x < fit_w; x++) {
                        size_t dst =
                            (size_t)((off_y + y) * (int)corrected_width +
                                     (off_x + x)) *
                            3;
                        size_t src = ((size_t)y * (size_t)fit_w + (size_t)x) * 3;
                        composed[dst + 0] = fit_pixels[src + 0];
                        composed[dst + 1] = fit_pixels[src + 1];
                        composed[dst + 2] = fit_pixels[src + 2];
                }
        }
        if (did_resize)
                free(fit_pixels);
        else
                image_free(fit_pixels);

        PixelData *data = (PixelData *)composed;

        // Calculate indentation to center the image
        if (centered)
                col = ((term_size.width_cells - corrected_width) / 2) + 1;

        printf("\033[%d;%dH", row, col);

        for (unsigned int d = 0; d < corrected_width * height; d++) {
                if (d % corrected_width == 0 && d != 0) {
                        row++;
                        printf("\033[%d;%dH", row, col);
                }

                PixelData *c = data + d;

                printf("\033[1;38;2;%03u;%03u;%03um%c", c->r, c->g, c->b, calc_ascii_char(c));
        }

        image_free(data);
        return 0;
}

int print_in_ascii(int row, int col, const char *path_to_img_file, int height, bool centered)
{
        int ret = convert_to_ascii(row, col, path_to_img_file, (unsigned)height, centered);
        if (ret == -1)
                printf("\033[0m");
        return 0;
}
