#! /usr/bin/env python
import argparse
import pydbus
from xml.etree import ElementTree
import cmd
import traceback


class CoiotDBus:
    def __init__(self):
        self.bus = pydbus.SystemBus()
        self.coiot_devices = self.bus.get('org.coiot', '/org/coiot/device')
        self.devices = {}
        for n in [e.attrib['name']
                  for e
                  in ElementTree.fromstring(self.coiot_devices.Introspect())
                  if e.tag == 'node']:
            self.devices[n] = self.bus.get('org.coiot',
                                           '/org/coiot/device/{}'.format(n))
            for nc in [e.attrib['name']
                       for e
                       in ElementTree.fromstring(self.devices[n].Introspect())
                       if e.tag == 'node']:
                assert(nc not in self.devices)
                path = '/org/coiot/device/{}/{}'.format(n, nc)
                self.devices[nc] = self.bus.get('org.coiot', path)


class CoiotCmd(cmd.Cmd):
    def __init__(self, coiot):
        super().__init__()
        self.prompt = '# '
        self.coiot = coiot

    def do_list(self, line):
        """
        List all COIoT devices
        """
        for ID, d in sorted(self.coiot.devices.items()):
            print('Device {}:'.format(ID))
            for f in dir(d):
                if not f[0].isupper():
                    continue
                v = getattr(d, f)
                if callable(v):
                    continue

                print('    {} = {}'.format(f, v))

    def do_set(self, line):
        """
        Set a COIoT device property.

        # set device.Property = value
        """
        splitline = line.split('=')
        lh, val = splitline[0].strip(), '='.join(splitline[1:]).strip()
        dev, f = lh.split('.')
        if (val[0] == '"' or val[0] == "'") and val[-1] == val[0]:
            val = val[1:-1]
        elif val in ("False", "True"):
            val = (val == "True")
        elif '.' in val:
            val = float(val)
        else:
            val = int(val)
        setattr(self.coiot.devices[dev], f, val)

    def complete_getset(self, text, line, begidx, endidx):
        if not text:
            return list(self.coiot.devices.keys())

        if '.' in text:
            split = text.split('.')
            text, ftgt = split[0], '.'.join(split[1:])
        else:
            ftgt = ''
        if text.strip() in self.coiot.devices.keys():
            return [text + '.' + f
                    for f in dir(self.coiot.devices[text])
                    if f[0].isupper() and f.startswith(ftgt)]

    def complete_get(self, *args):
        return self.complete_getset(*args)

    def complete_set(self, text, *args):
        if '=' not in text:
            cgs = self.complete_getset(text, *args)
            if not cgs:
                return []

            if '.' in cgs[0]:
                return [t + ' = ' for t in cgs]
            else:
                return cgs
        return [text]

    def do_get(self, line):
        """
        Get a COIoT device property.

        # get device
            Returns all properties and functions of the device
        # get device.Property
            Returns value of the given device property
        """
        if '.' in line:
            dev, f = line.split('.')
            print(getattr(self.coiot.devices[dev], f))
        else:
            device = self.coiot.devices[line]
            attrs = [f for f in dir(device)
                     if f[0].isupper()]
            for f in attrs:
                try:
                    v = getattr(device, f)
                    if not callable(v):
                        print('{}: {}'.format(f, v))
                    else:
                        print('{}()'.format(f))
                except Exception as e:
                    print("Error getting {}".format(f))
                    traceback.print_exc()

    def do_EOF(self, line):
        """
        Exit
        """
        return True


if __name__ == "__main__":
    ap = argparse.ArgumentParser(description='coiotd command-line client')

    CoiotCmd(CoiotDBus()).cmdloop()
