#!/usr/bin/python3
import sys, os, locale, signal, re
import gi, subprocess, time, threading
from locale import gettext as _
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, Pango
if os.path.exists('updaterlib.py'):
  from updaterlib import *
else:
  from update_applet.updaterlib import *

import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

INSTALL = True
SHOW_LOG = False
PULSE = False
DOMAIN = 'rosa-update-system'
LOCALE_DIR = '/usr/share/locale'
locale.bindtextdomain(DOMAIN, LOCALE_DIR)
locale.textdomain(DOMAIN)

UI = '/usr/share/update_applet/update_applet.ui'
iconpath='/usr/share/icons'
ICON = os.path.join(iconpath, 'rpmgrab.svg')

install_cmd='pkexec --disable-internal-agent env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY  dnf -y install '
reinstall_cmd='pkexec --disable-internal-agent env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY  dnf -y reinstall '

if os.path.exists('update_applet'):
  UI = './update_applet.glade'
  iconpath = os.path.abspath('./')

class Rpm:
  def __init__(self, rpm, app):
    start_pulse(app, True)
    self.cmd = install_cmd
    self.rpm = self.get_rpm(rpm, app)
    if not self.rpm or not os.path.exists(self.rpm):
      notify_send(_('Package not found:'), rpm, 'warning', None, None)
      self.rpm = None
      return None
    self.info = self.info_to_dict(self.rpm)
    echo(app, '==> ' + _('Package info: '), _('Package info: ') + self.rpm)
    app.window.set_title(f"RpmGrab: {self.info['Name']}")
    for key, val in self.info.items():
      echo(app, f'{key.upper()}: {val}', _("Rpm info"))
    self.req(app)
    self.prov(app)
    self.list(app)
    oldinfo = self.info_to_dict(self.info['Name'])
    if oldinfo:
      if f"{oldinfo['Version']}-{oldinfo['Release']}"  ==  f"{self.info['Version']}-{self.info['Release']}":
        self.cmd = reinstall_cmd
        app.install_button.set_label(_('Renstall package'))
      elif f"{oldinfo['Version']}-{oldinfo['Release']}"  <=  f"{self.info['Version']}-{self.info['Release']}":
        app.install_button.set_label(_('Update'))
      else:
        app.install_button.set_label(_('Downgrade package'))
        # без GLib.idle_add падает с ошибкой X сервера
        GLib.idle_add(lambda new=f"{self.info['Version']}-{self.info['Release']}", old=f"{oldinfo['Version']}-{oldinfo['Release']}" :
                     (dialog(first=_("Attention!\n new rpm version is less then installed"),
                             second=f"New: {new}\ninstalled: {old}"), False)[1])
    start_pulse(app, False)
    app.progress.set_fraction(0.0)
    echo(app, ' ', f"{app.install_button.get_label()} {self.rpm}")
    app.install_button.set_sensitive(True)

  def get_rpm(self, rpm, app):
    if rpm.endswith('.rpm'):
      rpmfile = os.path.abspath(rpm)
      return rpmfile
    echo(app, '==> ' + _('Download rpm:\n    ') + rpm, _('Download rpm: ') + rpm)
    if rpm.startswith('http'):
      rpmfile = download_file(rpm, '.rpm', '/tmp', app=app)
    else:
      result = subprocess.run(['dnf', 'download', '-q', '--url', '--disablerepo', '*686*', rpm], stdout=subprocess.PIPE, text=True)
      rpmfile = download_file(result.stdout.strip(), '.rpm', '/tmp', app=app)
    echo(app, '', _("Complete"))
    return rpmfile


  def info_to_dict(self, rpm_name):
    command = ['rpm', '-qi', rpm_name]
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    if result.returncode != 0:
      return None
    info_dict = {}
    lines = result.stdout.splitlines()
    i = 0
    while i < len(lines):
      line = lines[i]
      if line.startswith('Description'):
          key = 'Description'
          value_parts = [line.split(':', 1)[1].strip()] if ':' in line else []
          i += 1
          while i < len(lines):
            value_parts.append(lines[i].strip())
            i += 1
          info_dict[key] = '\n'.join(value_parts)
      else:
        match = re.match(r'^([^:]+)\s*:\s*(.*)$', line)
        if match:
            key = match.group(1).strip()
            value = match.group(2).strip()
            info_dict[key] = value
        i += 1
    return info_dict

  def info(self, app):
    runit(app, f'timeout 10s rpm -q --info "{self.rpm}"', _('Rpm info: '))

  def req(self, app):
    echo(app, '\n', _('Requires, recommends: ') + self.rpm)
    runit(app, f'timeout 10s rpm -q --requires --recommends "{self.rpm}"', _('Requires, recommends: '))

  def prov(self, app):
    echo(app, '\n', _('Provides: ') + self.rpm)
    runit(app, f'timeout 10s rpm -q --provides "{self.rpm}"', _('Provides: '))

  def list(self, app):
    echo(app, '\n', _('RPM list: ') + self.rpm)
    runit(app, f'timeout 10s rpm -ql "{self.rpm}"', _('RPM list: '))

  def install(self, app):
      GLib.idle_add(app.install_button.hide)
      if not runit(app, f'{self.cmd} "{self.rpm}"', _('Install: ') + self.rpm):
        echo(app, 'ERROR!!!', 'ERROR: ' + install_cmd)
        GLib.idle_add(app.install_button.show)
        start_pulse(app, False)
        app.progress.set_fraction(0.0)
        notify_send(self.rpm + _(': installation failed'), \
                    _('Try later, or use system package manager'), 'warning', None, None)
      else:
        echo(app, _('Complete'), _('Install complete: ') + self.rpm)
        start_pulse(app, False)
        app.progress.set_fraction(1.0)
        app.progress.set_text(_('Complete'))
        
      return False

def clear():
    global INSTALL
    app.window.set_title(_('RpmGrab - simple rpm installator gui'))
    app.textbuffer.set_text('\n')
    app.statusbar.push(app.context_id, '')
    app.progress.set_pulse_step(0.2)
    app.progress.set_text(_('Install'))
    app.text_window.hide()
    app.exit_button.set_label(_('Exit'))
    app.install_button.set_label(_('Install package'))
    app.install_button.set_sensitive(False)
    app.pwf_button.hide()
    app.install_button.show()
    start_pulse(app, False)
    app.log_chkbx.set_label(_('Info'))

    if SHOW_LOG:
        app.log_chkbx.set_active(True)
        app.text_window.show()
    else:
        app.log_chkbx.set_active(False)
        app.text_window.hide()

def exit(*args):
  Gtk.main_quit()

class WinHandler:
    def on_CHECK_HALT_activate(self, button):
        pass

    def on_show_log_toggled(self, button):
      global SHOW_LOG
      if app.log_chkbx.get_active():
          app.text_window.show()
          SHOW_LOG = True
      else:
          w, h = app.window.get_size()
          app.text_window.hide()
          app.window.resize(w, app.window.get_preferred_height()[1])
          SHOW_LOG = False

    def onSUBMIT(self, button):
        global INSTALL
        clear()
        bat = powercheck()
        if bat < 10:
            if not  dialog(first=_("Attention!\nThe battery charge status is low: ") + f'{bat}%',
                           second=_("Continue?"), btn=[Gtk.STOCK_YES, 1, Gtk.STOCK_NO, 0 ] ):
                return
        start_pulse(app, True)
        GLib.idle_add(lambda : rpm.install(app))

    def onEXIT(self, button):
        exit()

class Applet:
  def __init__(self):

    # window
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    builder = Gtk.Builder()
    self.builder = builder
    builder.set_translation_domain(DOMAIN)
    builder.add_from_file(UI)

    textaria = builder.get_object('TEXTAREA')
    textaria.override_font(Pango.FontDescription('Monospace 10'))
    self.textbuffer = textaria.get_buffer()

    self.progress = builder.get_object('PROGRESS')
    self.statusbar = builder.get_object('STATUS')
    self.text_window = builder.get_object('SCROLL')
    self.context_id = self.statusbar.get_context_id('updater')

    self.log_chkbx = builder.get_object('show_log')
    self.install_button = builder.get_object('SUBMIT')
    self.exit_button = builder.get_object('EXIT')
    self.pwf_button = builder.get_object('CHECK_HALT')
    builder.connect_signals(WinHandler())
    self.scroll = builder.get_object('SCROLL')
    self.v = self.scroll.get_vadjustment()
    self.h = self.scroll.get_hadjustment()

    self.window = builder.get_object('window_main')
    self.window.set_icon_from_file(ICON)
    self.window.connect("destroy", Gtk.main_quit)

  def run(self):
    self.window.show_all()
    self.pwf_button.hide()
    Gtk.main()

def scroll_back(app):
  app.v.set_value(0)
  app.h.set_value(0)
  return False

def HLP():
  name = os.path.basename(sys.argv[0])
  help_text = f"""
{name} - utility to install rpm packages with a simple GUI

Usage:
  {name}                           - run with an entry in a window
  {name} /path/to/file.rpm         - open local rpm file
  {name} http(s)://server/file.rpm - download and install
  {name} package                   - download from repo and install
  {name} --help                    - show this help message
"""
  print(help_text)


if __name__ == '__main__':
  notify_init('RpmGrab')
  app = Applet()
  clear()
  force = False
  if len(sys.argv) < 2 :
    rpmfile = True
    while type(rpmfile) is bool:
      rpmfile = dialog(first=_('Enter rpm file'), entry='yes')
  elif len(sys.argv) >= 2 :
    rpmfile = sys.argv[1]
    if rpmfile == '-h' or rpmfile == '--help':
      HLP()
      quit() 
    # если третий позиционный параметр --force, то запускать процесс не ожидая нажатие кнопки
    if len(sys.argv) == 3 and sys.argv[2] == '--force':
      force = True
  if not rpmfile:
    quit()

  rpm = None

  def rpm_init():
    global rpm
    global force
    rpm = threading_me(Rpm, rpmfile, app)
    if not rpm.rpm:
      exit()
    if not force:
      GLib.timeout_add_seconds(2, lambda : scroll_back(app))
    else:
      WinHandler().onSUBMIT(None)
    return False

  GLib.idle_add(rpm_init)
  app.run()

