#!/usr/sbin/python3

# cpupower-gui.in
#
# Copyright 2019-2020 Evangelos Rigas
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import gettext
import os
import signal
import sys

VERSION = "1.0.0"
pkgdatadir = "/usr/share/cpupower-gui"
localedir = "/usr/share/locale"

sys.path.insert(1, pkgdatadir)

from cpupower_gui.config import CpuPowerConfig
from cpupower_gui.helper import (
    apply_balanced,
    apply_configuration,
    apply_cpu_profile,
    apply_energy_preference,
    apply_performance,
    get_cpu_frequencies,
    set_cpu_max_freq,
    set_cpu_min_freq,
    set_cpu_offline,
    set_cpu_online,
)
from cpupower_gui.utils import (
    cpus_available,
    cpus_offline,
    cpus_online,
    is_energy_pref_avail,
    parse_core_list,
    read_available_energy_prefs,
    read_energy_pref,
)

signal.signal(signal.SIGINT, signal.SIG_DFL)
gettext.install("cpupower-gui", localedir)

CPUS = cpus_available()
ENERGY_PREF_AVAIL = is_energy_pref_avail(0)


def set_config(args):
    """Set cpupower-gui config

    Args:
        args: Command line arguments

    """
    conf = CpuPowerConfig()
    if args.apply:
        print("Applying configuration... ")
        ret = apply_configuration(conf)
        sys.exit(ret)


def set_energy(args):
    """Set energy performance preferences

    Args:
        args: Command line arguments

    """
    if args.pref:
        pref = args.pref
        print("Setting energy performance preference to:", pref)
        apply_energy_preference(pref)
        sys.exit(0)

    energy_prefs = args.list_energy_preferences
    if energy_prefs is None:
        energy_prefs = "{}-{}".format(CPUS[0], CPUS[-1])

    try:
        cpus = parse_core_list(energy_prefs)
    except ValueError:
        print("Could not parse the CPU list")
        exit(1)
    print("The available energy performance preferences are:")
    for cpu in cpus:
        prefs = read_available_energy_prefs(cpu)
        current_pref = read_energy_pref(cpu)
        print("CPU {}:".format(cpu))
        if prefs:
            for pref in prefs:
                if pref == current_pref:
                    print("\t- {} (Current)".format(pref))
                else:
                    print("\t- {}".format(pref))
        else:
            print("No prefereneces were found!")
    sys.exit(0)


def set_profile(args):
    """Set cpupower-gui profile

    Args:
        args: Command line arguments

    """
    conf = CpuPowerConfig()
    if args.list or (args.apply is None):  # List profiles
        profiles = conf.profiles
        if profiles:
            print("The available profiles are:")
            for prof in profiles:
                print("\t- {}".format(prof))
            sys.exit(0)

        print("No profiles were found!")
        sys.exit(0)

    if args.apply:
        prof = args.apply
        if prof in conf.profiles:
            print("Applying profile: ", prof)
            apply_cpu_profile(conf.get_profile(prof))
            sys.exit(0)
        else:
            print("Profile not found!")
            sys.exit(1)


def set_offline(args):
    """Set CPUs offline

    Args:
        args: Command line arguments

    """
    conf = CpuPowerConfig()
    if args.list or (args.apply is None):  # List offline
        offline = "{}".format(cpus_offline())
        print("The following CPUs are offline:", offline)
        sys.exit(0)

    if args.apply:
        try:
            cpus = parse_core_list(args.apply)
        except ValueError:
            print("Could not parse the CPU list")
            exit(-1)
        for core in cpus:
            print("Setting CPU{} offline...".format(core))
            set_cpu_offline(core)
        sys.exit(0)


def set_online(args):
    """Set CPUs online

    Args:
        args: Command line arguments

    """
    conf = CpuPowerConfig()
    if args.list or (args.apply is None):  # List online
        online = "{}".format(cpus_online())
        print("The following CPUs are online:", online)
        sys.exit(0)

    if args.apply:
        try:
            cpus = parse_core_list(args.apply)
        except ValueError:
            print("Could not parse the CPU list")
            exit(-1)
        for core in cpus:
            print("Setting CPU{} online...".format(core))
            set_cpu_online(core)
        sys.exit(0)


def _print_cpu_freq(cpu):
    """Helper function to print frequencies"""
    msg = "CPU{}:\n\tFreqs (MHz): {}, Limits: {}"
    freqs, lims = get_cpu_frequencies(cpu)
    print(msg.format(cpu, freqs, lims))


def set_freqs(args):
    """Set CPUs frequencies

    Args:
        args: Command line arguments

    """
    if args.apply is None:
        for cpu in cpus_available():
            _print_cpu_freq(cpu)
        sys.exit(0)

    if (args.apply is not None) and (args.min or args.max):
        try:
            cpus = parse_core_list(args.apply)
        except ValueError:
            print("Could not parse the CPU list")
            exit(1)
        for core in cpus:
            print("Setting CPU{} frequency...".format(core))
            if args.min:
                set_cpu_min_freq(core, args.min)
            if args.max:
                set_cpu_max_freq(core, args.max)
            _print_cpu_freq(core)

        sys.exit(0)


# Add argparse options
parser = argparse.ArgumentParser(
    prog="cpupower-gui",
    description="cpupower-gui - Set the scaling frequencies and governor of a CPU",
    formatter_class=argparse.RawDescriptionHelpFormatter,
)

parser.add_argument(
    "--version", action="version", version="%(prog)s {}".format(VERSION)
)

# Add subparsers
metavar = (
    "{[co]nfig, [freq]uency, [pr]ofile, [off]line, [on]line}"
    if not ENERGY_PREF_AVAIL
    else "{[co]nfig, [freq]uency, [ene]rgy, [pr]ofile, [off]line, [on]line}"
)
subparsers = parser.add_subparsers(
    title="subcommands",
    description="Configuration commands",
    help="Change the settings from the command line",
    metavar=metavar,
)

# Profile commands
profile_sub = subparsers.add_parser("profile", aliases=["pr"])
profile_sub.add_argument(
    "-l", "--list", action="store_true", help="list available cpupower profiles",
)

profile_sub.add_argument(
    "apply", nargs="?", type=str, metavar="PROFILE", help="apply a cpupower profile",
)

profile_sub.set_defaults(func=set_profile)

# Config commands
config_sub = subparsers.add_parser("config", aliases=["co"])
config_sub.add_argument(
    "apply", action="store_true", help="apply cpupower configuration",
)
config_sub.set_defaults(func=set_config)

# Offline commands
offline_sub = subparsers.add_parser("offline", aliases=["off"])
offline_sub.add_argument(
    "-l", "--list", action="store_true", help="list offline CPUs",
)

offline_sub.add_argument(
    "apply", nargs="?", type=str, metavar="LIST OF CPUS", help="set CPUs offline",
)

offline_sub.set_defaults(func=set_offline)

# Online commands
online_sub = subparsers.add_parser("online", aliases=["on"])
online_sub.add_argument(
    "-l", "--list", action="store_true", help="list online CPUs",
)

online_sub.add_argument(
    "apply", nargs="?", type=str, metavar="LIST OF CPUS", help="set CPUs online",
)

online_sub.set_defaults(func=set_online)

# Frequency commands
freq_sub = subparsers.add_parser("frequency", aliases=["freq"])
freq_sub.add_argument(
    "--max", type=int, help="maximum frequency",
)

freq_sub.add_argument(
    "--min", type=int, help="minimum frequency",
)
freq_sub.add_argument(
    "apply", nargs="?", type=str, metavar="LIST OF CPUS", help="set CPUs frequency",
)

freq_sub.set_defaults(func=set_freqs)


# Optional arguments
parser.add_argument(
    "-b", "--balanced", action="store_true", help="change governor to balanced",
)
parser.add_argument(
    "-p", "--performance", action="store_true", help="change governor to performance",
)

parser.add_argument(
    "--gapplication-service", action="store_true", help="start gui from gapplication",
)


if ENERGY_PREF_AVAIL:
    energy_sub = subparsers.add_parser("energy", aliases=["ene"])
    cmd_group = energy_sub.add_mutually_exclusive_group()

    # Enable flags if hardware is compatible
    cmd_group.add_argument(
        "--pref",
        type=str,
        choices=read_available_energy_prefs(0),
        help="set a global energy profile",
    )
    cmd_group.add_argument(
        "--list-energy-preferences",
        type=str,
        nargs="?",
        metavar="LIST OF CPUS",
        const="{}-{}".format(CPUS[0], CPUS[-1]),
        help="list available energy performance preferences (Default: all cpus)",
    )
    energy_sub.set_defaults(func=set_energy)


if __name__ == "__main__":
    args = parser.parse_args()
    if "func" in args:
        args.func(args)

    if args.balanced:
        apply_balanced()
        sys.exit(0)

    if args.performance:
        apply_performance()
        sys.exit(0)

    import gi
    from gi.repository import Gio

    resource = Gio.Resource.load(os.path.join(pkgdatadir, "cpupower-gui.gresource"))
    resource._register()

    from cpupower_gui import main

    sys.exit(main.main(VERSION))


# vim:set filetype=python shiftwidth=4 softtabstop=4 expandtab:
