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

class App(Gtk.Window):
    def __init__(self):
        super().__init__(title='JSON Formatter')
        self.set_default_size(800, 560)
        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)
        # toolbar
        tb = Gtk.Box(spacing=6)
        fmt_btn = Gtk.Button(label='Format / Prettify')
        fmt_btn.connect('clicked', self.format_json)
        tb.pack_start(fmt_btn, False, False, 0)
        mini_btn = Gtk.Button(label='Minify')
        mini_btn.connect('clicked', self.minify_json)
        tb.pack_start(mini_btn, False, False, 0)
        valid_btn = Gtk.Button(label='Validate Only')
        valid_btn.connect('clicked', self.validate_json)
        tb.pack_start(valid_btn, False, False, 0)
        copy_btn = Gtk.Button(label='Copy Output')
        copy_btn.connect('clicked', self.copy_output)
        tb.pack_start(copy_btn, False, False, 0)
        swap_btn = Gtk.Button(label='⇄ Swap')
        swap_btn.connect('clicked', self.swap)
        tb.pack_start(swap_btn, False, False, 0)
        vbox.pack_start(tb, False, False, 0)
        self.status = Gtk.Label(label='Paste JSON on the left and click Format')
        self.status.set_line_wrap(True)
        vbox.pack_start(self.status, False, False, 0)
        # panels
        hpaned = Gtk.Paned()
        sw1 = Gtk.ScrolledWindow()
        self.input_tv = Gtk.TextView()
        self.input_tv.override_font(Pango.FontDescription('Monospace 10'))
        self.input_tv.set_wrap_mode(Gtk.WrapMode.NONE)
        self.input_tv.get_buffer().set_text('{\n  "name": "example",\n  "version": 1,\n  "active": true,\n  "tags": ["json","formatter"],\n  "meta": {"created": "2024-01-01"}\n}')
        sw1.add(self.input_tv)
        hpaned.add1(sw1)
        sw2 = Gtk.ScrolledWindow()
        self.output_tv = Gtk.TextView()
        self.output_tv.set_editable(False)
        self.output_tv.override_font(Pango.FontDescription('Monospace 10'))
        self.output_tv.set_wrap_mode(Gtk.WrapMode.NONE)
        sw2.add(self.output_tv)
        hpaned.add2(sw2)
        vbox.pack_start(hpaned, True, True, 0)
        # error tag
        out_buf = self.output_tv.get_buffer()
        self.err_tag = out_buf.create_tag('err', foreground='red')
        self.ok_tag = out_buf.create_tag('ok', foreground='green')
    def get_input(self):
        buf = self.input_tv.get_buffer()
        return buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
    def set_output(self, text, is_error=False):
        buf = self.output_tv.get_buffer()
        buf.set_text('')
        it = buf.get_end_iter()
        if is_error:
            buf.insert_with_tags(it, text, self.err_tag)
        else:
            buf.insert(it, text)
    def parse_input(self):
        text = self.get_input().strip()
        try:
            return json.loads(text), None
        except json.JSONDecodeError as e:
            return None, str(e)
    def format_json(self, *_):
        data, err = self.parse_input()
        if err:
            self.status.set_markup(f'<span foreground="red">Error: {err}</span>')
            self.set_output(f'JSON Error:\n{err}', is_error=True)
        else:
            formatted = json.dumps(data, indent=2, ensure_ascii=False)
            self.set_output(formatted)
            lines = formatted.count('\n') + 1
            self.status.set_markup(f'<span foreground="green">Valid JSON — {lines} lines, {len(formatted)} chars</span>')
    def minify_json(self, *_):
        data, err = self.parse_input()
        if err:
            self.status.set_markup(f'<span foreground="red">Error: {err}</span>')
            self.set_output(f'JSON Error:\n{err}', is_error=True)
        else:
            minified = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
            self.set_output(minified)
            self.status.set_text(f'Minified: {len(minified)} chars')
    def validate_json(self, *_):
        _, err = self.parse_input()
        if err:
            self.status.set_markup(f'<span foreground="red">Invalid: {err}</span>')
            self.set_output(f'INVALID JSON\n\n{err}', is_error=True)
        else:
            self.status.set_markup('<span foreground="green" weight="bold">Valid JSON</span>')
            self.set_output('Valid JSON ✓')
    def copy_output(self, *_):
        from gi.repository import Gdk
        buf = self.output_tv.get_buffer()
        text = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), False)
        clipboard = self.get_clipboard(Gdk.SELECTION_CLIPBOARD)
        clipboard.set_text(text, -1)
    def swap(self, *_):
        out_buf = self.output_tv.get_buffer()
        out_text = out_buf.get_text(out_buf.get_start_iter(), out_buf.get_end_iter(), False)
        if out_text.strip():
            self.input_tv.get_buffer().set_text(out_text)
            out_buf.set_text('')

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