/* file_diff_row/gutter.rs
 *
 * Copyright 2026 Christian Kruse
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

use adw::prelude::*;
use gtk::gdk;

pub fn attach_gutter(text_view: &gtk::TextView, gutter_lines: Vec<(Option<u32>, Option<u32>)>, gutter_digits: usize) {
    let max_digits = gutter_digits.max(1);
    let gutter = gtk::DrawingArea::new();

    gutter.set_content_width((2 * max_digits as i32) * 10 + 28);

    {
        let g_weak = gutter.downgrade();
        text_view.connect_map(move |tv| {
            let Some(g) = g_weak.upgrade() else { return };

            let sample = gutter_sample(max_digits);
            let layout = tv.create_pango_layout(Some(&sample));
            let (lw, _) = layout.pixel_size();

            if lw > 0 {
                g.set_content_width(lw + 24);
            }
        });
    }

    let text_view_weak = text_view.downgrade();
    gutter.set_draw_func(move |area, cr, _w, _h| {
        let Some(tv) = text_view_weak.upgrade() else { return };

        let visible = tv.visible_rect();
        let min_buf_y = visible.y();
        let max_buf_y = visible.y() + visible.height();

        let buffer = tv.buffer();
        let mut iter = buffer.start_iter();

        let area_w = area.width();
        let half_w = area_w / 2;
        let outer_pad = 6_i32;
        let inner_pad = 8_i32;

        // Pango layout based on the TextView's font so digits align with text rows.
        let layout = tv.create_pango_layout(None);

        let line_color = gdk::RGBA::new(0.5, 0.5, 0.5, 0.85);
        let sep_color = gdk::RGBA::new(0.5, 0.5, 0.5, 0.35);

        loop {
            let line = iter.line() as usize;
            let (buf_y, buf_h) = tv.line_yrange(&iter);

            if buf_y > max_buf_y {
                break;
            }

            if buf_y + buf_h < min_buf_y {
                if !iter.forward_line() {
                    break;
                }

                continue;
            }

            let (_, win_y) = tv.buffer_to_window_coords(gtk::TextWindowType::Left, 0, buf_y);

            if let Some(&(old, new)) = gutter_lines.get(line) {
                cr.set_source_rgba(
                    line_color.red() as f64,
                    line_color.green() as f64,
                    line_color.blue() as f64,
                    line_color.alpha() as f64,
                );

                if let Some(n) = old {
                    layout.set_text(&format!("{n:>w$}", w = max_digits));

                    let (lw, _) = layout.pixel_size();

                    cr.move_to((half_w - inner_pad - lw) as f64, win_y as f64);
                    pangocairo::functions::show_layout(cr, &layout);
                }

                if let Some(n) = new {
                    layout.set_text(&format!("{n:>w$}", w = max_digits));

                    let (lw, _) = layout.pixel_size();

                    cr.move_to((area_w - outer_pad - lw) as f64, win_y as f64);
                    pangocairo::functions::show_layout(cr, &layout);
                }

                cr.set_source_rgba(
                    sep_color.red() as f64,
                    sep_color.green() as f64,
                    sep_color.blue() as f64,
                    sep_color.alpha() as f64,
                );

                cr.set_line_width(1.0);
                cr.move_to(half_w as f64 + 0.5, win_y as f64);
                cr.line_to(half_w as f64 + 0.5, (win_y + buf_h) as f64);
                let _ = cr.stroke();
            }

            if !iter.forward_line() {
                break;
            }
        }
    });

    text_view.set_gutter(gtk::TextWindowType::Left, Some(&gutter));

    if let Some(vadj) = text_view.vadjustment() {
        let g_weak = gutter.downgrade();
        vadj.connect_value_changed(move |_| {
            if let Some(g) = g_weak.upgrade() {
                g.queue_draw();
            }
        });
    }
}

fn gutter_sample(digits: usize) -> String {
    let single = "9".repeat(digits);
    format!("{single} {single}")
}
