#!/usr/bin/env python

'''
Copyright 2023 PySimpleSoft, Inc. and/or its licensors. All rights reserved.

Redistribution, modification, or any other use of PySimpleGUI or any portion thereof is subject
to the terms of the PySimpleGUI License Agreement available at https://eula.pysimplegui.com.

You may not redistribute, modify or otherwise use PySimpleGUI or its contents except pursuant
to the PySimpleGUI License Agreement.
'''

import PySimpleGUI as sg
import sys
import psutil

"""
    Desktop floating widget - CPU Cores 
    Right click the window to exit
    Uses psutil to display:
        CPU usage of each individual core
    CPU utilization is updated every 500 ms by default
    Utiliziation is shown as a scrolling area graph
    To achieve a "rainmeter-style" of window, these featurees were used:
      An alpha-channel setting of 0.8 to give a little transparency
      No titlebar
      Grab anywhere, making window easy to move around
    Note that the keys are tuples, with a tuple as the second item
        ('-KEY-', (row, col))      
"""

GRAPH_WIDTH = 120       # each individual graph size in pixels
GRAPH_HEIGHT = 40
TRANSPARENCY = .8       # how transparent the window looks. 0 = invisible, 1 = normal window
NUM_COLS = 4
POLL_FREQUENCY = 2500    # how often to update graphs in milliseconds

colors = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29')

# DashGraph does the drawing of each graph
class DashGraph(object):
    def __init__(self, graph_elem, text_elem, starting_count, color):
        self.graph_current_item = 0
        self.graph_elem = graph_elem        # type: sg.Graph
        self.text_elem = text_elem
        self.prev_value = starting_count
        self.max_sent = 1
        self.color = color
        self.line_list = []                 # list of currently visible lines. Used to delete oild figures

    def graph_percentage_abs(self, value):
        self.line_list.append(self.graph_elem.draw_line(            # draw a line and add to list of lines
            (self.graph_current_item, 0),
            (self.graph_current_item, value),
            color=self.color))
        if self.graph_current_item >= GRAPH_WIDTH:
            self.graph_elem.move(-1,0)
            self.graph_elem.delete_figure(self.line_list[0])        # delete the oldest line
            self.line_list = self.line_list[1:]                     # remove line id from list of lines
        else:
            self.graph_current_item += 1

    def text_display(self, text):
        self.text_elem.update(text)

def main():
    # A couple of "User defined elements" that combine several elements and enable bulk edits
    def Txt(text, **kwargs):
        return(sg.Text(text, font=('Helvetica 8'), **kwargs))

    def GraphColumn(name, key):
        return sg.Column([[Txt(name, size=(10,1), key=('-TXT-', key))],
                          [sg.Graph((GRAPH_WIDTH, GRAPH_HEIGHT), (0, 0), (GRAPH_WIDTH, 100), background_color='black', key=('-GRAPH-', key))]], pad=(2, 2))

    num_cores = len(psutil.cpu_percent(percpu=True))        # get the number of cores in the CPU

    sg.theme('Black')

    layout = [[sg.Text('CPU Core Usage', justification='c', expand_x=True)] ]

    # add on the graphs
    for rows in range(num_cores//NUM_COLS+1):
        # for cols in range(min(num_cores-rows*NUM_COLS, NUM_COLS)):
        layout += [[GraphColumn('CPU '+str(rows*NUM_COLS+cols), (rows, cols)) for cols in range(min(num_cores-rows*NUM_COLS, NUM_COLS))]]

    # ----------------  Create Window  ----------------
    window = sg.Window('CPU Cores Usage Widget', layout,
                       keep_on_top=True,
                       grab_anywhere=True,
                       no_titlebar=True,
                       return_keyboard_events=True,
                       alpha_channel=TRANSPARENCY,
                       use_default_focus=False,
                       finalize=True,
                       margins=(1,1),
                       element_padding=(0,0),
                       border_depth=0,
                       enable_close_attempted_event=True,
                       auto_save_location=True,
                       right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)


    graphs = []
    for rows in range(num_cores//NUM_COLS+1):
        for cols in range(min(num_cores-rows*NUM_COLS, NUM_COLS)):
            graphs += [DashGraph(window[('-GRAPH-', (rows, cols))],
                                 window[('-TXT-',  (rows, cols))],
                                 0, colors[(rows*NUM_COLS+cols)%len(colors)])]


    # ----------------  main loop  ----------------
    while True :
        # --------- Read and update window once every Polling Frequency --------
        event, values = window.read(timeout=POLL_FREQUENCY)
        if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):         # Be nice and give an exit
            break
        elif event == sg.WIN_CLOSED:
            break
        elif event == 'Edit Me':
            sg.execute_editor(__file__)
        elif event == 'Version':
            sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location())
        # read CPU for each core
        stats = psutil.cpu_percent(percpu=True)

        sg.timer_start()
        # update each graph
        for i, util in enumerate(stats):
            graphs[i].graph_percentage_abs(util)
            graphs[i].text_display('{} CPU {:2.0f}'.format(i, util))
        time_elapsed = sg.timer_stop()
    window.close()

if __name__ == "__main__":
    main()