#!/usr/bin/python3
import gi, re
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import cairo

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='Histogram / Bar Chart')
        self.set_default_size(640, 520)
        self.data = []
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        vbox.set_margin_top(8); vbox.set_margin_bottom(8)
        vbox.set_margin_start(8); vbox.set_margin_end(8)
        self.add(vbox)
        vbox.add(Gtk.Label(label='Enter numbers (one per line or comma-separated):', xalign=0))
        sw = Gtk.ScrolledWindow(); sw.set_size_request(-1, 100)
        self.input_tv = Gtk.TextView()
        self.input_tv.get_buffer().set_text('5\n12\n3\n8\n15\n7\n10\n2\n20\n6\n14\n9')
        sw.add(self.input_tv)
        vbox.pack_start(sw, False, False, 0)
        btn = Gtk.Button(label='Plot Histogram')
        btn.connect('clicked', self.plot)
        vbox.pack_start(btn, False, False, 0)
        self.da = Gtk.DrawingArea()
        self.da.connect('draw', self.draw)
        vbox.pack_start(self.da, True, True, 0)
        self.plot()
    def plot(self, *_):
        buf = self.input_tv.get_buffer()
        text = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
        nums = re.findall(r'-?\d+\.?\d*', text)
        self.data = [float(n) for n in nums]
        self.da.queue_draw()
    def draw(self, w, cr):
        alloc = w.get_allocation()
        W, H = alloc.width, alloc.height
        cr.set_source_rgb(0.97, 0.97, 0.97); cr.paint()
        if not self.data: return
        pad = 50
        chart_w = W - pad*2; chart_h = H - pad*2
        n = len(self.data)
        max_val = max(self.data) if self.data else 1
        min_val = min(0, min(self.data))
        val_range = max_val - min_val or 1
        bar_w = chart_w / n
        # axes
        cr.set_source_rgb(0.3,0.3,0.3); cr.set_line_width(2)
        cr.move_to(pad, pad); cr.line_to(pad, H-pad)
        cr.line_to(W-pad, H-pad); cr.stroke()
        # zero line
        zero_y = H - pad - (-min_val / val_range * chart_h)
        cr.set_source_rgba(0.6,0.6,0.6,0.5); cr.set_line_width(1)
        cr.move_to(pad, zero_y); cr.line_to(W-pad, zero_y); cr.stroke()
        # bars
        colors = [(0.2,0.5,0.9),(0.9,0.3,0.3),(0.2,0.8,0.4),(0.9,0.7,0.1),(0.7,0.2,0.9)]
        for i, val in enumerate(self.data):
            bh = abs(val / val_range * chart_h)
            x = pad + i * bar_w + 2
            if val >= 0:
                y = zero_y - bh
            else:
                y = zero_y
            cr.set_source_rgb(*colors[i % len(colors)])
            cr.rectangle(x, y, bar_w-4, bh)
            cr.fill()
            cr.set_source_rgb(0.3,0.3,0.3); cr.set_line_width(1)
            cr.rectangle(x, y, bar_w-4, bh); cr.stroke()
            # value label
            cr.set_font_size(9)
            lbl = str(int(val)) if val == int(val) else f'{val:.1f}'
            ext = cr.text_extents(lbl)
            lx = x + (bar_w-4)/2 - ext.width/2
            ly = (y - 4) if val >= 0 else (y + bh + 12)
            cr.move_to(lx, ly); cr.show_text(lbl)
        # y-axis labels
        cr.set_source_rgb(0.3,0.3,0.3); cr.set_font_size(10)
        for tick in range(6):
            tv = min_val + tick * val_range / 5
            ty = H - pad - ((tv - min_val) / val_range * chart_h)
            cr.move_to(pad-4, ty); cr.line_to(pad, ty); cr.stroke()
            cr.move_to(4, ty+4); cr.show_text(f'{tv:.0f}')

win = App()
win.connect('destroy', Gtk.main_quit)
win.show_all()
Gtk.main()
