#!/usr/bin/python
#-*- coding: utf-8 -*-
#
# --BETA--
#################################################################
#    CPU-G version 0.9 beta, Fotis Tsamis, October 2009.        #
#################################################################
#    CPU-G is a program that displays information about your CPU,
#    RAM, Motherboard and some general information about your System.
#    Copyright © 2009  Fotis Tsamis <ftsamis at gmail dot com>.
#
#    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 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 os, re, sys, platform, subprocess
try:
    import pygtk, gtk, gtk.glade, gobject
    pygtk.require("2.0")
except:
    print 'You need to have PyGTK 2.10.0, GTK.Glade and GTK+ 2.10.0 or higher  \
           installed in your system in order to run CPU-G.'
    sys.exit(1)



class main:
    
    def readfile(self, name):
        try:
            f = file(name, 'r')
            data = f.read()
            f.close()
            return data
        except:
            return 'N/A'
    
    
    
    def logo(self, core):
        model = self.cpuinfo("model", core)
        vendor = self.cpuinfo("vendor", core)
        
        path = 'data/logos/'
        
        if vendor == 'AMD':
            label = 'amd.png'
        elif vendor == 'Intel':
            label = 'intel.png'
        
        #AMDs
        if re.match("AMD Athlon\(tm\) 64 X2.*",model) or re.match("AMD Athlon\(tm\) X2.*",model):
            label = 'AMD-AthlonX2.png'
        elif re.match("AMD Sempron\(tm\).*",model):
            label = 'AMD-Sempron.png'
        elif re.match("Mobile AMD Sempron\(tm\).*",model):
            label = 'AMD-Sempron-Mobile.png'
        elif re.match("Dual-Core AMD Opteron\(tm\).*",model) or re.match("AMD Opteron\(tm\).*",model):
            label = 'AMD-Opteron.png'
        elif re.match("AMD Athlon\(tm\) XP.*",model):
            label = 'AMD-AthlonXP.png'
        elif re.match("AMD Athlon\(tm\) 64 Processor.*",model):
            label = 'AMD-Athlon64.png'
        elif re.match("AMD Phenom\(tm\).*",model):
            label = 'AMD-Phenom.png'
        
        #Intels
        elif re.match("Intel\(R\) Core\(TM\)2 Duo.*",model):
            label = 'Intel-Core2Duo.png'
        elif re.match("Intel\(R\) Core\(TM\)2 Quad.*",model):
            label = 'Intel-Core2Quad.png'
        elif re.match("Intel\(R\) Core\(TM\)2 CPU.*",model):
            label = 'Intel-Core2Quad.png'
        elif re.match("Intel\(R\) Atom\(TM\) CPU.*",model):
            label = 'Intel-Atom.png'
        elif re.match("Intel\(R\) Core\(TM\)2 Extreme CPU.*",model):
            label = 'Intel-Core2Extreme.png'
        elif re.match("Intel\(R\) Xeon\(TM\).*",model):
            label = 'Intel-Xeon.png'
        elif re.match(".*Pentium II.*",model):
            label = 'Intel-Pentium2.png'
        elif re.match("Intel\(R\) Pentium\(R\) Dual CPU.*",model):
            label = 'Intel-PentiumDual.png'
        
        return path + label
    
    
    
    def cpuinfo(self, var, core):
        info = self.readfile("/proc/cpuinfo")
        
        if var == 'vendor':
            vendor = re.findall("vendor_id\s*:\s*(.*)",info)
            if vendor[core] == 'AuthenticAMD':
                vendor[core]='AMD'
            elif vendor[core] == 'GenuineIntel':
                vendor[core]='Intel'
            return vendor[core]
        elif var == 'corespeed':
            return re.findall("cpu MHz\s*:\s*(.*)",info)[core]+' MHz'
        elif var == 'model':
            return re.findall("model name\s*:\s*(.*)",info)[core]
        elif var == 'cache':
            return re.findall("cache size\s*:\s*(.*)",info)[core]
        elif var == 'modelnumber':
            return re.findall("model\s*:\s*(.*)",info)[core]
        elif var == 'family':
            return re.findall("cpu family\s*:\s*(.*)",info)[core]
        elif var == 'stepping':
            return re.findall("stepping\s*:\s*(.*)",info)[core]
        elif var == 'coresnum':
            return str(len(re.findall("processor\s*:\s*(.*)",info)))
        elif var == 'flags':
            return re.findall("flags\s*:\s*(.*)",info)[core]
        elif var == 'bogomips':
            return re.findall("bogomips\s*:\s*(.*)",info)[core]
        elif var == 'width':
            if re.findall(' lm(?![-a-zA-Z0-9_])', re.findall("flags\s*:(.*)",info)[core]):
                return '64-bit'
            else:
                return '32-bit'
            


    def sysdevcpu(self, core, level, kind):
        coresinsysdev = str(len(re.findall("'cpu[0-9]'", str(os.listdir("/sys/devices/system/cpu/")))))
        if coresinsysdev == self.cpuinfo('coresnum', 'foo'):
            cores_matching = True
        else:
            print "Error: Cannot decide if the cores are %s or %s.\nUsing the lowest value as the real cores number." %(cpuinfo('coresnum','foo'), coresinsysdev) #FIXME: Wrong text. (?)
            

        
        path = '/sys/devices/system/cpu/cpu%i/cache/' %(core)
        indexes = len(re.findall("'index[0-9]*'",str(os.listdir(path))))
        
        
        for index in range(indexes):
            levelpath = path+'index%i/level' %(index)
            typepath = path+'index%i/type' %(index)
            size = path+'index%i/size' %(index)
            #os.chdir(newpath)
            if self.readfile(levelpath).strip() == str(level) and self.readfile(typepath).strip() == kind:
                return self.readfile(size).strip()
            elif index == range(indexes)[-1]:
                return 'N/A'
    
    
        
    def distro(self):
        try:
            values = platform.linux_distribution()
        except AttributeError:
            values = platform.dist()
        
        if len(values) !=0:
            return "%s %s %s" %(values[0], values[1], values[2])
        else:
            return self.readfile('/etc/issue').strip()
        
        

    def gccver(self):
        gcc_version = os.popen('gcc -dumpversion').read().strip()
        if gcc_version != '':
            return gcc_version
        else:
            return 'N/A'
                
    def xver(self):
        command = subprocess.Popen(['Xorg', '-version'],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        stdout, stderr = command.communicate()
        return re.findall("X\.Org X Server (.*)", stderr)[0]
        

    
    def raminfo(self):
        data = self.readfile('/proc/meminfo')
        
        values = {'total' : int(re.findall('^MemTotal:\s*([0-9]*)',data, re.M)[0])/1024,
                  'free' : int(re.findall('^MemFree:\s*([0-9]*)',data, re.M)[0])/1024,
                  'buffers' : int(re.findall('^Buffers:\s*([0-9]*)',data, re.M)[0])/1024,
                  'cached' : int(re.findall('^Cached:\s*([0-9]*)',data, re.M)[0])/1024,
                  'used' : 0,
                  'active' : int(re.findall('^Active:\s*([0-9]*)',data, re.M)[0])/1024,
                  'inactive' : int(re.findall('^Inactive:\s*([0-9]*)',data, re.M)[0])/1024,
                  'cached' : int(re.findall('^Cached:\s*([0-9]*)',data, re.M)[0])/1024
                  }
        values['free'] = values['free'] + values['buffers'] + values['cached']
        values['used'] = values['total'] - values['free']
        
        return values
        
    def mobo(self, var):
        # var can be: board_vendor, board_name, bios_vendor, 
        # bios_version, bios_date, or chassis_type
        
        return self.readfile('/sys/devices/virtual/dmi/id/'+var).strip()
    
    def uptime(self):
        total = int(self.readfile('/proc/uptime').split('.')[0])
        days = int(total / 86400)
        hours = int((total / 3600) - (days * 24))
        minutes = int( (total / 60) - ((days * 1440) + (hours * 60)) )
        
        return "%i days, %i hours, %i minutes" %(days, hours, minutes)
    
    
    
    
    




class CPUG:
    """Description"""
    
    def __init__(self):
        self.gladefile = "cpu-g.glade"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(self.gladefile)
        
        dic = { 
                "on_close_clicked" : self.kill,
                "on_mainwindow_destroy" : self.kill,
                "on_core_move_active" : self.core_change
        }
        self.wTree.connect_signals(dic)


        # Processor selection combobox
        self.box = self.wTree.get_object("coreselection")
        add = gtk.ListStore(str)
        
        for i in range(int(main().cpuinfo("coresnum", 'foo'))):
            add.append(["cpu #" + str(i)])
            
            
        self.box.set_model(add)
        cell = gtk.CellRendererText()
        self.box.pack_start(cell)
        self.box.add_attribute(cell,'text',0)
        self.box.set_active(0)
        
        
        self.ram_update()
        gobject.timeout_add(1000, self.ram_update) # Update Free & Used RAM every 1 second
        
        
        # Motherboard tab
        # board_vendor, board_name, bios_vendor, bios_version, bios_date, chassis_type
        self.wTree.get_object("mobovendor").set_text(main().mobo('board_vendor'))
        self.wTree.get_object("moboname").set_text(main().mobo('board_name'))
        self.wTree.get_object("biosvendor").set_text(main().mobo('bios_vendor'))
        self.wTree.get_object("biosversion").set_text(main().mobo('bios_version'))
        self.wTree.get_object("biosdate").set_text(main().mobo('bios_date'))
        
        # System tab
        # os.uname()[0]=OS(Linux) [1]=hostname [2]=kernel version [4]=architecture
        self.wTree.get_object("gcc").set_text(main().gccver())
        self.uptime_update()
        gobject.timeout_add(1000 * 60, self.uptime_update)
        self.wTree.get_object("hostname").set_text(os.uname()[1])
        self.wTree.get_object("arch").set_text(os.uname()[4])
        self.wTree.get_object("kernel").set_text(os.uname()[0]+" "+os.uname()[2])
        self.wTree.get_object("distro").set_text(main().distro())
        self.wTree.get_object("xorg").set_text(main().xver())
        
    def uptime_update(self):
        self.wTree.get_object("uptime").set_text(main().uptime())
        return True
    
    def ram_update(self):
        # RAM tab
        values = main().raminfo()
        totalram = values['total']
        usedram = values['used']
        freeram = values['free']
        
        self.wTree.get_object("ramtotal").set_text(str(totalram) + ' MBytes')
        self.wTree.get_object("ramused").set_text(str(usedram) + ' MBytes')
        self.wTree.get_object("ramfree").set_text(str(freeram) + ' MBytes')
        
        # RAM progress bars
        usedfraction = (usedram * 1.00) / totalram
        usedpercentage = str(int(round(usedram * 100.0 / totalram))) + '%'
        self.wTree.get_object("ramusedprogbar").set_fraction(usedfraction)
        self.wTree.get_object("ramusedprogbar").set_text(usedpercentage)
        
        freefraction = (freeram * 1.00) / totalram
        freepercentage = str(int(round(freeram * 100.0 / totalram))) + '%'
        self.wTree.get_object("ramfreeprogbar").set_fraction(freefraction)
        self.wTree.get_object("ramfreeprogbar").set_text(freepercentage)
        
        self.wTree.get_object("ramactive").set_text(str(values['active']) + ' MBytes')
        self.wTree.get_object("raminactive").set_text(str(values['inactive']) + ' MBytes')
        self.wTree.get_object("ramcached").set_text(str(values['cached']) + ' MBytes')
        return True
            
    
    
    def core_change(self, widget):
        index = self.wTree.get_object("coreselection").get_active()
        print "cpu set to #" + str(index)
        
        # Processor tab
        self.wTree.get_object("vendor").set_text(main().cpuinfo("vendor",index))
        self.wTree.get_object("proclogo").set_from_file(main().logo(index))
        self.wTree.get_object("corespeed").set_text(main().cpuinfo("corespeed",index))
        self.wTree.get_object("model").set_text(main().cpuinfo("model",index))
        self.wTree.get_object("bogomips").set_text(main().cpuinfo("bogomips",index))
        self.wTree.get_object("modelnumber").set_text(main().cpuinfo("modelnumber",index))
        self.wTree.get_object("family").set_text(main().cpuinfo("family",index))
        self.wTree.get_object("stepping").set_text(main().cpuinfo("stepping",index))
        self.wTree.get_object("coresnum").set_text(main().cpuinfo("coresnum", 'foo'))
        self.wTree.get_object("flags").set_text(main().cpuinfo("flags", index))
        self.wTree.get_object("width").set_text(main().cpuinfo("width", index))
        self.wTree.get_object("l1data").set_text(main().sysdevcpu(index, 1, 'Data'))
        self.wTree.get_object("l1inst").set_text(main().sysdevcpu(index, 1, 'Instruction'))
        self.wTree.get_object("l2").set_text(main().sysdevcpu(index, 2, 'Unified'))
        self.wTree.get_object("l3").set_text(main().sysdevcpu(index, 3, 'Unified'))
        
        
        
        
    def kill(self, widget):
        print "Quit"
        sys.exit(0)
        

if __name__ == '__main__':
    CPUG()
    gtk.main()
    
    

