#!/usr/bin/env python2
# coding: utf-8


from __future__ import division
from datetime import datetime
import os
import psutil
import re
from time import sleep
import socket
from subprocess import Popen, PIPE
import sys

from mpd import MPDClient
import requests
import xcffib
from xcffib.xproto import Atom, PropMode

try:
    import sensors
    sensors.init()
    SENSORS_LOADED = True
except:
    pass
    SENSORS_LOADED = False

XCB_CONN = xcffib.connect('')

COLORS = {
    'black':          '#263238',
    'red':            '#dd2c00',
    'green':          '#8bc34a',
    'yellow':         '#ff9800',
    'blue':           '#2196f3',
    'magenta':        '#e91e63',
    'cyan':           '#26a69a',
    'white':          '#5f5f5f',
    'bright_black':   '#8a8a8a',
    'bright_green':   '#ff8a65',
    'bright_yellow':  '#DCE775',
    'bright_blue':    '#ffcc80',
    'bright_magenta': '#81d4fa',
    'bright_cyan':    '#ffab91',
    'bright_grey':    '#84ffff',
    'bright_white':   '#ffffff',
}
BAR_PIECES = [
    ('media', 'blue'),
    ('weather', 'green'),
    # NOT WORKING: ('temperature', 'cyan'),
    ('battery', 'red'),
    ('system_stats', 'magenta')
]


class Info():
    battery_capacity_path = '/sys/class/power_supply/BAT0/charge_full'
    battery_cur_path = '/sys/class/power_supply/BAT0/charge_now'
    battery_state_path = '/sys/class/power_supply/BAT0/status'

    weather_check_interval = 18000
    weather_check_url = 'http://rss.accuweather.com/rss/liveweather_rss.asp'
    weather_loc_code = '30032'
    weather_check_pattern = re.compile(
        '<title>Currently:\s(?P<type>.*):\s(?P<temp>\d+)[C,F]</title>')
    weather_icons = {
        # 'Rain': '',
        'Rain': '&#xf2cf;',
        'Showers': '&#xfe09;',
        'hunderstorm': '&#xf2e8;',
        'Cloud': '&#xf0c2;',
        'Fog': '&#xf0c2;',
        'Sun': '&#xf2e4;'
    }

    def _open_and_read_first_line(self, path):
        with open(path, 'rb') as f:
            contents = f.readline()
        return contents

    @staticmethod
    def _run_cmd(cmd):
        return Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()[0].strip()

    def battery(self):
        try:
            full = int(self._open_and_read_first_line(self.battery_capacity_path))
            cur = int(self._open_and_read_first_line(self.battery_cur_path))
            state = self._open_and_read_first_line(self.battery_state_path)
        except IOError:
            return None

        perc = int((cur / full) * 100)
        icon = '&#xf213;'
        if perc < 25:
            icon = '&#xf212;'
        elif perc < 50:
            icon = '&#xf215;'
        elif perc < 75:
            icon  = '&#xf214;'
        ret_str = '{}% ({})'.format(str(perc), state[0].upper())
        return (icon, ret_str)


    def media(self):
        to_ret = ''

        # MPV / ZTREAM
        mpv_pid = self._run_cmd(['pgrep', 'mpv'])
        ls_path = os.path.expanduser('~/.last_station')
        station = ''

        if mpv_pid and os.path.isfile(ls_path):
            with open(ls_path, 'r') as f:
                station = f.read().strip('\n')
            if station:
                to_ret = '{} Radio'.format(station.title())

        # MPD
        try:
            client = MPDClient()
            client.timeout = 1
            client.connect('localhost', 6600)
        except socket.error:
            to_ret = None
            client = None

        if client:
            cur_song_info = client.currentsong()
            artist = cur_song_info.get(
                'albumartist', cur_song_info.get('artist', ''))
            title = cur_song_info.get('title', '')
            if artist and title:
                to_ret = '{} - {}'.format(artist, title)

        if not to_ret:
            return None

        return ('&#xf025;', to_ret)

    def system_stats(self):
        usage = psutil.virtual_memory()
        ram_used_perc = int(((usage.used - usage.cached) / usage.total) * 100)
        cpu_perc = int(psutil.cpu_percent(None))
        ret_str = '{}% {}%'.format(cpu_perc, ram_used_perc)

        return ('&#xf237;', ret_str)


    def temperature(self):
        if not SENSORS_LOADED:
            return ''
        status = ''
        try:
            for chip in sensors.iter_detected_chips():
                for feature in chip:
                    if 'Core' in feature.label:
                        status += '{}° '.format(int(feature.get_value()))
        except Exception as e:
            print(e)

        return status.rstrip()


    def _check_weather(self):
        params = {'metric': 0, 'locCode': self.weather_loc_code}
        try:
            resp = requests.get(self.weather_check_url, params=params)
            resp.raise_for_status()
            match = re.search(self.weather_check_pattern, resp.text)
        except requests.exceptions.ConnectionError:
            match = None

        ret_str = 'Unavailable'
        icon = None

        if match:
            g_dict = match.groupdict()
            for substr, possible_icon in self.weather_icons.items():

                if substr in g_dict['type']:
                    icon = possible_icon
                    break

                if not icon:
                    icon = '&#xf2e4;'

            ret_str = '{type} - {temp}°'.format(**g_dict)

        if not icon:
            return None

        return (icon, ret_str)


    def weather(self):
        now = datetime.now()
        try:
            diff = now - self.last_check_time
            check = False
            if diff.seconds >= self.check_interval:
                check = True
        except AttributeError:
            self.last_check_time = now
            check = True

        if check:
            ret = self._check_weather()
            self.weather_status = ret
        else:
            ret = self.weather_status

        return ret

INFO = Info()


def update_text(text):
    s = XCB_CONN.get_setup()
    r = s.roots[0].root
    text = text.encode('utf-8', 'ignore')
    XCB_CONN.core.ChangeProperty(PropMode.Replace, r, Atom.WM_NAME, Atom.STRING,
                            8, len(text), str(text))
    XCB_CONN.flush()


def get_colored_statustext():
    output = ''
    for group in BAR_PIECES:
        piece, color_name = group
        try:
            func = getattr(Info, piece)
        except AttributeError:
            print >> sys.stderr, 'Couldn\'t find callable "{}". Dying...'.format(piece)
            exit(2)

        color = COLORS.get(color_name, None)
        if not color:
            print >> sys.stderr, 'Couldn\'t find color "{}". Unstyling'.format(color_name)

        piece_res = func(INFO)
        if piece_res:
            output += '<span color="{}">{} </span>{} '.format(color, *piece_res)

    output = unicode(output, 'utf-8')
    return output


if __name__ == '__main__':
    while True:
        text = get_colored_statustext()
        update_text(text)
        sleep(3)
