#!/usr/bin/python3
import gi, signal, locale, importlib, sys, tarfile, os, shutil, subprocess
from locale import gettext as _
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from barium_iniedit import iniparser
DOMAIN = 'iniedit'
LOCALE_DIR = '/usr/share/locale'
UI = '/usr/share/barium_iniedit/iniedit.ui'
MODULES_DIR = 'barium_iniedit.'
ICON = '/usr/share/icons/iniedit.svg'

#local tests block #####################################
if os.path.isfile('./iniedit.ui'):
  UI = './iniedit.ui'
  ICON = './iniedit.svg'
  LOCALE_DIR='./locale'
#########################################################

locale.bindtextdomain(DOMAIN, LOCALE_DIR)
locale.textdomain(DOMAIN)

def get_ini():
  try:
    ini = sys.argv[1]
  except:
    with open('/etc/initvars', 'r') as initvars:
      for line in initvars.readlines():
        if line.strip().split('=')[0] == 'PATHINI':
          ini = line.strip().split('=')[1]
  return ini

def error_msg( first, second):
  parent = builder.get_object('window_main')
  messagedialog = Gtk.MessageDialog(transient_for=parent,
						destroy_with_parent=True,
						modal=True,
						message_type=Gtk.MessageType.ERROR,
						buttons=Gtk.ButtonsType.OK,
						text=first)
  messagedialog.format_secondary_text(second)
  response = messagedialog.run()
  if response == Gtk.ResponseType.OK or response == Gtk.ResponseType.DELETE_EVENT:
    messagedialog.destroy()
    
def yes_no(first, second):
  parent = builder.get_object('window_main')
  messagedialog = Gtk.MessageDialog(transient_for=parent,
						destroy_with_parent=True,
						modal=True,
						message_type=Gtk.MessageType.WARNING,
						buttons=Gtk.ButtonsType.YES_NO,
						text=first)
  messagedialog.format_secondary_text(second)
  response = messagedialog.run()
  messagedialog.destroy()
  if response == Gtk.ResponseType.YES:
    return True
  elif response == Gtk.ResponseType.DELETE_EVENT:
    return None
  return False

# functions for header bar buttons
class Top_buttons:
  def __init__(self):
    global show_add, show_del, show_com, show_dis
    show_add = False
    show_del = False
    show_com = True
    show_dis = True
    
  def on_show_add(*args):
    global show_add
    if show_add:
      show_add = False
      menuitem_add.set_label(_('Show "add" buttons'))
    else: 
      show_add = True
      menuitem_add.set_label(_('Hide "add" buttons'))
    refresh()

  def on_show_dis(*args):
    global show_dis
    if show_dis:
      show_dis = False
      menuitem_dis.set_label(_('Show disabled lines'))
    else: 
      show_dis = True
      menuitem_dis.set_label(_('Hide disabled lines'))
    refresh()

  def on_show_del(*args):
    global show_del
    if show_del:
      menuitem_del.set_label(_('Show "delete" buttons'))
      show_del = False
    else:
      menuitem_del.set_label(_('Hide "delete" buttons'))
      show_del = True
    refresh()

  def on_show_com(*args):
    global show_com
    if show_com:
      menuitem_com.set_label(_('Show commented lines'))
      show_com = False
    else:
      menuitem_com.set_label(_('Hide commented lines'))
      show_com = True
    refresh()

  def on_save_button_clicked(*args):
    global ini, newini, backini, mainbox_dict
    try: 
      shutil.copy(ini, bakini)
    except:
      print(_('cannot save') + ': ' + bakini)
    iniparser.iniwrite(config, newini)
    try: 
      shutil.move(newini, ini)
    except:
      error_msg(_('ERROR: save ini file'), ini )
      return
    for a in range(index):
      for b in mainbox_dict[a]:
        mainbox_dict[a][b].destroy()
    mainbox_dict[0] = {}
    mainbox_dict[0]['box'] = Gtk.Box(expand=True, hexpand=True)
    mainbox_dict[0]['label'] = Gtk.Label(halign='center')
    mainbox_dict[0]['label'].set_markup(
    '<b>' + _('File ') + ' ' + ini + ' ' + _('successfully saved!') + '</b>\n' +
    _('The previous state of the ini file is backed up,') + '\n' +
    _('you can still load it into the editor window from the menu "Open".')
    )
    mainbox_dict[0]['box'].pack_start(mainbox_dict[0]['label'], True, True, 0)
    mainbox.add(mainbox_dict[0]['box'])
    window.show_all()
   

  def on_exit_button_clicked(*args):
    global ini, newini
    if os.path.isfile(newini):
      ans = yes_no(_('Save settings in to:'), ini + ' ?') 
      if ans:
        Top_buttons.on_save_button_clicked()
      elif ans == None:
        return
    Gtk.main_quit()
  
  def on_open_def(*args):
    global newini
    with tarfile.open('/.memory/layer-base/0/defaults.tar.bz2') as tar:
      content = tar.extractfile('./ROSA-DATA/ROSA.ini').read().decode('utf-8')
    try:
      with open(newini, 'w') as f:
        f.write(content)
    except:
      error_msg(_('ERROR: save ini file'), newini )
    refresh(write=False)
  
  def on_open_cur(*args):
    global newini, ini
    try:
      shutil.copy(ini, newini)
    except:
      error_msg(_('ERROR: save ini file'), newini )
    refresh(write=False)

  def on_open_bak(*args):
    global newini
    global bakini
    try:
      shutil.copy(bakini, newini)
    except:
      error_msg(_('ERROR: save ini file'), newini )
    refresh(write=False)
    
  def on_open_in_editor(ID, inifile):
    global ini, newini
    if yes_no(_('Continue?'),_( 
    'Iniedit will be closed so as not to create collisions.'  
    'You can run it again after editing is complete.' )):
      f = ini if inifile == 'ini' else newini 
      with subprocess.Popen([ 'xdg-open', f ], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) as check:
        print(check.wait() )
      Gtk.main_quit()  
     
def draw_button (icon_name, label):
  box_ = Gtk.Box(expand=False, hexpand=True)
  box_.set_border_width(2)
  image_ = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
  label_ = Gtk.Label(label=label)
  box_.pack_start(image_, False, False, 3)
  box_.pack_start(label_, False, False, 3)
  return box_
  
def empty_button_clicked (button, key, n):
  global config 
  global mainbox_dict
  global index
  text = mainbox_dict[n]['empty_entry'].get_text()
  config[key].append(text)
  refresh()

def del_button_clicked (button, key, i):
  global config 
  global mainbox_dict
  global index
  config[key].pop(i)
  refresh()

def toggle_section (button, config, key ):
  if config[key][0][1]:
    config[key][0][1] = False
  else:
    config[key][0][1] = True
  refresh()

def refresh(write=True, destroy=True):
  '''
  function generate editable elements for main window of iniedit
  write - write config to a temporary ini
  destroy - destroy old elements before create new
  '''
  global index
  global mainbox_dict
  global config
  global newini
  if write:
    iniparser.iniwrite(config, newini)
  if destroy:
    for a in range(index):
      for b in mainbox_dict[a]:
        mainbox_dict[a][b].destroy()
  rawconf = iniparser.raw_conf(newini)
  config = iniparser.iniread(rawconf)  
  index = 0
  mainbox_dict = {}
  for key in config:
    if not key == '_header':
      mainbox_dict[index] = {}
      mainbox_dict[index]['box'] = Gtk.Box(spacing=10, halign='start')
      mainbox_dict[index]['label'] = Gtk.Label(halign='start')
      mainbox_dict[index]['label'].set_markup('<b>' + key + '</b>')
      mainbox_dict[index]['_perms'] = Gtk.Label(halign='start', label=config[key][1][1])
      mainbox_dict[index]['_run_as'] = Gtk.Label(halign='start', label=config[key][2][1])
      mainbox_dict[index]['flag'] = Gtk.CheckButton(halign='start')
      if config[key][0][1]:
        mainbox_dict[index]['flag'].set_active(True)  
      mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['flag'], True, True, 0)
      mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['label'], True, True, 0)
      mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['_perms'], True, True, 0)
      mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['_run_as'], True, True, 0)
      mainbox_dict[index]['flag'].connect("toggled", toggle_section,  config, key )
      mainbox.add(mainbox_dict[index]['box'])
      index += 1
      if show_add:
        mainbox_dict[index] = {}
        mainbox_dict[index]['empty_box'] = Gtk.Box(expand=True, hexpand=True)
        mainbox_dict[index]['empty_entry'] = Gtk.Entry(halign='fill', hexpand=True, expand=True)
        mainbox_dict[index]['empty_button'] = Gtk.Button(label=_('add'), halign='end')
        mainbox_dict[index]['empty_box'].pack_start(mainbox_dict[index]['empty_entry'], True, True, 0)
        mainbox_dict[index]['empty_box'].pack_start(mainbox_dict[index]['empty_button'], True, True, 0)
        mainbox.add(mainbox_dict[index]['empty_box'])
        mainbox_dict[index]['empty_button'].connect("clicked", empty_button_clicked, key, index)
        index += 1
    start_range = 0 if key == '_header' else 3 
    for i in range(start_range, len(config[key])):
      if (isinstance(config[key][i], str)):
        if config[key][i].isspace():
          continue
        if config[key][i].startswith('#') and not show_com:
          continue
        mainbox_dict[index]  = {}
        mainbox_dict[index]['box'] = Gtk.Box(expand=True, hexpand=True)
        mainbox_dict[index]['label'] = Gtk.Label(halign='start', label=config[key][i])
        mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['label'], True, True, 0)
        mainbox.add(mainbox_dict[index]['box'])
        if show_del:      
          mainbox_dict[index]['del_button'] = Gtk.Button(stock=Gtk.STOCK_DELETE, halign='end')
          mainbox_dict[index]['box'].pack_start(mainbox_dict[index]['del_button'], True, True, 0)
          mainbox_dict[index]['del_button'].connect("clicked", del_button_clicked, key, i)
      else:
        if config[key][i][0].startswith('#') and not show_dis:
          continue
        try:
          module = (key.strip('/#').replace('/', '_') + '_' + config[key][i][0].strip('# ') )
          helper = importlib.import_module(MODULES_DIR + module)
        except:
          try:
            module = (key.strip('/#').replace('/', '_'))
            helper = importlib.import_module(MODULES_DIR + module)
          except:
            module = 'std_helper'
            helper = importlib.import_module(MODULES_DIR + module)
        mainbox_dict[index] = helper.item_helper(Gtk=Gtk, Gdk=Gdk, config=config, refresh=refresh,  
                              key=key, i=i, del_button_clicked=del_button_clicked, _=_,  
                              show_com=show_com, show_del=show_del, show_dis=show_dis) 
        mainbox.add(mainbox_dict[index]['box'])
      index += 1
  window.show_all()

# get ini finename
ini = get_ini()
if not os.access(ini, os.W_OK):
  print(ini + ': ' + _('permissions denied'))
  quit()

newini = ini + '.new'
bakini = ini + '.bak'         
if not os.path.isfile(newini):
  try:
    shutil.copy(ini, newini)
  except:
    print(_('cannot create') + ': ' + newini)
    quit()

signal.signal(signal.SIGINT, signal.SIG_DFL)
builder = Gtk.Builder()
builder.set_translation_domain(DOMAIN)
builder.add_from_file(UI)
mainbox = builder.get_object('mainbox')
window = builder.get_object('window_main')
window.connect('delete-event', Top_buttons.on_exit_button_clicked, '')
window.set_icon_from_file(ICON)

builder.connect_signals(Top_buttons())

open_menu = builder.get_object('open_menu')
upd_button = builder.get_object('upd_button')
def_button = builder.get_object('def_button')
save_button = builder.get_object('save_button')

# view menu
menu_view = Gtk.Menu()
menuitem_add = Gtk.MenuItem(label=_('Show "add" buttons'))
menuitem_add.connect("activate", Top_buttons.on_show_add, '')
menuitem_add.show()
menu_view.append(menuitem_add)

menuitem_del = Gtk.MenuItem(label=_('Show "delete" buttons'))
menuitem_del.connect("activate", Top_buttons.on_show_del, '')
menuitem_del.show()
menu_view.append(menuitem_del)

menuitem_dis = Gtk.MenuItem(label=_('Hide disabled lines'))
menuitem_dis.connect("activate", Top_buttons.on_show_dis, '')
menuitem_dis.show()
menu_view.append(menuitem_dis)

menuitem_com = Gtk.MenuItem(label=_('Hide commented lines'))
menuitem_com.connect("activate", Top_buttons.on_show_com, '')
menuitem_com.show()
menu_view.append(menuitem_com)

menu_view.show_all()
view_menu = builder.get_object('view_menu')
view_box_button = draw_button('find', _('View'))
view_menu.add(view_box_button)
view_menu.set_popup(menu_view)

# open menu
menu_open = Gtk.Menu()
menuitem_cur = Gtk.MenuItem(label=_('Open current ROSA.ini'))
menuitem_cur.connect("activate", Top_buttons.on_open_cur, '')
menuitem_cur.show()
menu_open.append(menuitem_cur)

menuitem_def = Gtk.MenuItem(label=_('Open default ROSA.ini'))
menuitem_def.connect("activate", Top_buttons.on_open_def, '')
menuitem_def.show()
menu_open.append(menuitem_def)

if os.path.isfile(bakini):
  menuitem_bak = Gtk.MenuItem(label=_('Open ROSA.ini backup'))
  menuitem_bak.connect("activate", Top_buttons.on_open_bak, '')
  menuitem_bak.show()
  menu_open.append(menuitem_bak)

# submenu open in
menuitem_openin = Gtk.MenuItem(label=_('Show in a text editor'))
submenu_openin = Gtk.Menu()
menuitem_openin.set_submenu(submenu_openin)
menu_open.append(menuitem_openin)
menuitem_1 = Gtk.MenuItem(label='ROSA.ini')
menuitem_1.connect("activate", Top_buttons.on_open_in_editor, 'ini')
menuitem_1.show()
submenu_openin.append(menuitem_1)

menuitem_2 = Gtk.MenuItem(label='ROSA.ini.new')
menuitem_2.connect("activate", Top_buttons.on_open_in_editor, 'newini')
menuitem_2.show()
submenu_openin.append(menuitem_2)

submenu_openin.show()

menu_open.show_all()
open_menu = builder.get_object('open_menu')
open_box_button = draw_button('document-open', _('Open'))
open_menu.add(open_box_button)
open_menu.set_popup(menu_open)

refresh(destroy=False, write=False)
Gtk.main()
