# LivePatchSocket.py
# -*- Mode: Python; indent-tabs-mode: nil; tab-width: 4; coding: utf-8 -*-
#
#  Copyright (c) 2017 Canonical
#
#  Author: Andrea Azzarone <andrea.azzarone@canonical.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 2 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, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

from gi.repository import GLib
import http.client
import socket
import threading
import yaml

HOST_NAME = '/var/snap/canonical-livepatch/current/livepatchd.sock'


class UHTTPConnection(http.client.HTTPConnection):

    def __init__(self, path):
        http.client.HTTPConnection.__init__(self, 'localhost')
        self.path = path

    def connect(self):
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(self.path)
        self.sock = sock


class LivePatchSocket(object):

    def __init__(self, http_conn=None):
        if http_conn is None:
            self.conn = UHTTPConnection(HOST_NAME)
        else:
            self.conn = http_conn

    def get_status(self, on_done):

        def do_call():
            try:
                self.conn.request('GET', '/status?verbose=True')
                r = self.conn.getresponse()
                active = r.status == 200
                data = yaml.safe_load(r.read())
            except Exception:
                active = False
                data = dict()
            check_state = LivePatchSocket.get_check_state(data)
            patch_state = LivePatchSocket.get_patch_state(data)
            fixes = LivePatchSocket.get_fixes(data)
            GLib.idle_add(lambda: on_done(
                active, check_state, patch_state, fixes))

        thread = threading.Thread(target=do_call)
        thread.start()

    @staticmethod
    def get_check_state(data):
        try:
            status = data['status']
            kernel = next((k for k in status if k['running']), None)
            return kernel['livepatch']['checkState']
        except Exception:
            return 'check-failed'

    @staticmethod
    def get_patch_state(data):
        try:
            status = data['status']
            kernel = next((k for k in status if k['running']), None)
            return kernel['livepatch']['patchState']
        except Exception:
            return 'unknown'

    @staticmethod
    def get_fixes(data):
        try:
            status = data['status']
            kernel = next((k for k in status if k['running']), None)
            fixes = kernel['livepatch']['fixes']
            return [LivePatchFix(f)
                    for f in fixes.replace('* ', '').split('\n') if len(f) > 0]
        except Exception:
            return list()


class LivePatchFix(object):

    def __init__(self, text):
        patched_pattern = ' (unpatched)'
        self.patched = text.find(patched_pattern) == -1
        self.name = text.replace(patched_pattern, '')

    def __eq__(self, other):
        if isinstance(other, LivePatchFix):
            return self.name == other.name and self.patched == other.patched
        return NotImplemented

    def __ne__(self, other):
        result = self.__eq__(other)
        if result is NotImplemented:
            return result
        return not result
