import errno
import os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Pango
import tuna.tuna_sched as tuna_sched
import procfs
from tuna import tuna

class list_store_column:
    def __init__(self, name, type=GObject.TYPE_UINT):
        self.name = name
        self.type = type

def generate_list_store_columns_with_attr(columns):
    for column in columns:
        yield column.type
    for column in columns:
        yield GObject.TYPE_UINT

def set_store_columns(store, row, new_value):
    nr_columns = len(new_value)
    for col in range(nr_columns):
        col_weight = col + nr_columns
        cur_value = store.get_value(row, col)
        if cur_value == new_value[col]:
            new_weight = Pango.Weight.NORMAL
        else:
            new_weight = Pango.Weight.BOLD

        store.set(row, col, new_value[col], col_weight, new_weight)

def on_affinity_text_changed(self):
    new_affinity_text = self.affinity.get_text().strip()
    if self.affinity_text != new_affinity_text:
        try:
            for cpu in new_affinity_text.strip(",").split(","):
                new_affinity_cpu_entry = int(cpu, 16)
        except:
            try:
                new_affinity = tuna.cpustring_to_list(new_affinity_text)
            except:
                if len(new_affinity_text) > 0 \
                        and new_affinity_text[-1] != '-' \
                        and new_affinity_text[0:2] not in ('0x', '0X'):
                    # print "not a hex number"
                    self.affinity.set_text(self.affinity_text)
                    return
        self.affinity_text = new_affinity_text

def invalid_affinity():
    dialog = Gtk.MessageDialog(None, \
        Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \
        Gtk.MessageType.WARNING, \
        Gtk.ButtonsType.OK, \
        _("Invalid affinity, specify a list of CPUs!"))
    dialog.run()
    dialog.destroy()
    return False

def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus):
    pid = pid_info.pid
    changed = False
    curr_policy = os.sched_getscheduler(pid)
    curr_prio = int(pid_info["stat"]["rt_priority"])
    if new_policy == os.SCHED_OTHER:
        new_prio = 0
    if curr_policy != new_policy or curr_prio != new_prio:
        param = os.sched_param(new_prio)
        try:
            os.sched_setscheduler(pid, new_policy, param)
        except:
            dialog = Gtk.MessageDialog(None, \
                Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \
                Gtk.MessageType.WARNING, \
                Gtk.ButtonsType.OK, \
                _("Invalid parameters!"))
            dialog.run()
            dialog.destroy()
            return False

        curr_policy = os.sched_getscheduler(pid)
        if curr_policy != new_policy:
            print(_("couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!") % \
              {'pid': pid, 'cpol': tuna_sched.sched_str(curr_policy),
               'cpri': curr_prio,
               'npol': tuna_sched.sched_str(new_policy),
               'npri': new_prio})
        else:
            changed = True

    try:
        curr_affinity = os.sched_getaffinity(pid)
    except OSError as err:
        if err.args[0] == errno.ESRCH:
            return False
        raise err

    try:
        new_affinity = [int(a) for a in new_affinity.split(",")]
    except:
        try:
            new_affinity = tuna.cpustring_to_list(new_affinity)
        except:
            new_affinity = procfs.bitmasklist(new_affinity, nr_cpus)

    new_affinity.sort()

    if curr_affinity != new_affinity:
        try:
            os.sched_setaffinity(pid, new_affinity)
        except:
            return invalid_affinity()

        try:
            curr_affinity = os.sched_getaffinity(pid)
        except OSError as err:
            if err.args[0] == errno.ESRCH:
                return False
            raise err

        if curr_affinity != new_affinity:
            print(_("couldn't change pid %(pid)d from %(caff)s to %(naff)s!") % \
              {'pid':pid, 'caff':curr_affinity, 'naff':new_affinity})
        else:
            changed = True

    return changed
