#!/usr/bin/env python
# vim: set ts=4 sw=4 expandtab sts=4:
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# <geier@lostpackets.de> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return Christian Geier
# ----------------------------------------------------------------------------

"""
utility for querying the database
"""

import sys
import signal
from os import path
import argparse
from ConfigParser import SafeConfigParser

try:
    from termcolor import colored, cprint
    def print_bold(text):
        cprint(text, attrs=['bold'])
except:
    def print_bold(text):
        print(text)

try:
    import sqlite3
except ImportError:
    print "pysqlite3 not installed"
    sys.exit(1)



class VCard(list):
    """
    internal representation of the a VCard. This is mainly a list with some
    associated methods, each list element is a CardProperty
    h_ref: unique id (realy just the url) of the VCard
    db_path: database file from which to initialize the VCard
    """

    def __init__(self, h_ref = "", db_path=""): #TODO
        conn = sqlite3.connect(db_path)
        cur = conn.cursor()
        stuple = (h_ref,)
        cur.execute('SELECT * FROM properties WHERE href=(?)', stuple)
        result = cur.fetchall()
        for vcard_id, vcard_property, vcard_value, vcard_href in result:
            stuple = (vcard_id, )
            cur.execute('SELECT parameter, value FROM parameters WHERE property_id=(?)', stuple)
            parameters = cur.fetchall()
            self.append(CardProperty(vcard_property, vcard_value, parameters,),)
        conn.close()

    def get_prop(self, card_property): #TODO
        """
        returns a list of all CardProperties matching "card_property",
        making VCard work like a dict (and perhaps it better would be one)
        """
        collector = list()
        for prop in self:
            if prop.prop() == card_property:
                collector.append(prop)
        return collector

    def name(self):
        """
        returns the name of the contact (FN)
        fluff to make the rest more readable
        """
        return self.get_prop('FN')[0].value()

    def get_props(self):
        """
        returns a list of all properties (each property only once,
        even if it occurs multiple times) this vcard has
        """
        collector = list()
        for prop in self:
            collector.append(prop.prop())
        return list(set(collector))

class CardProperty(list):
    """
    A CardProperty object holds one VCard property including all parameters
    """
    def __init__(self, prop, value, params):
        self.append(prop)
        self.append(value)
        self.append(params)

    def prop(self):
        return self[0]

    def value(self):
        return self[1]

    def type_list(self):
        collector = list()
        for param in self[2]:
            if param[0] == "TYPE":
                collector.append(param[1])
        return collector

    def type_list_pretty(self):
        types = self.type_list()
        if len(types) == 0:
            return None
        else:
            return " (" + ", ".join(types) + ")"

class PcQuery(object):
    """Querying the addressbook database"""
    def __init__(self, db_path = "~/.pycacard/abook.db",
                 encoding = "utf-8", errors = "strict", debug = False):
        self.db_path = path.expanduser(db_path)
        self.encoding = encoding
        self.errors = errors
        self.debug = debug
        self.display_all = False
        self.search_string = ""
        self.print_function = "print_contact_info"

    def search(self):
        """
        this is the main method
        first we get the list of contact_ids matching the search string
        then these are printed using the different print functions
        """
        contact_ids = self.get_contact_id_from_string()
        while len(contact_ids) != 0:
            contact_id= contact_ids.pop()
            if self.print_function == "print_email":
                self.print_email(contact_id[0])
            else:
                self.print_contact_info(contact_id[0] )
                if len(contact_ids) > 0:
                    print ""

    def get_contact_id_from_string(self):
        """returns list of ids from db matching search_string"""
        conn = sqlite3.connect(self.db_path)
        c = conn.cursor()
        t = ('%'+ self.search_string +'%',)
        c.execute('SELECT href FROM properties WHERE value LIKE (?)', t)
        result = c.fetchall()
        result = list(set(result))
        conn.close()
        return result

    def print_prop(self, contact, prop):
        for line in contact.get_prop(prop.upper()):
            if len(line.type_list()) != 0:
                print unicode(prop.capitalize() + line.type_list_pretty() + u": " + line.value()).encode(self.encoding)
            else:
                print unicode(prop.capitalize() + u": " + line.value()).encode(self.encoding)

    def print_contact_info(self, contact_href):
        """new style contact card information printing"""
        contact = VCard(contact_href, self.db_path)

        print_bold(unicode("Name: " + contact.name()).encode(self.encoding))
        for prop in ("Email", "Tel",):
            self.print_prop(contact, prop)
        if self.display_all == True:
            for prop in contact.get_props():
                if not prop in ("EMAIL", "TEL"):
                    self.print_prop(contact, prop)

    def print_email(self, contact_href):
        """prints only name, email and type for use with mutt"""
        contact = VCard(contact_href, self.db_path)
        for email in contact.get_prop('EMAIL'):
            print unicode(email.value() + u"\t" + contact.name() + u"\t" + u", ".join(email.type_list())).encode(self.encoding)

def signal_handler(signal, frame):
    """
    tries to hide some ugly python backtraces from the user after
    pressing ctrl-c
    """
    sys.exit(0)

# MAIN
def main(argv):
    """main function, everything starts  here"""
    configfile = "~/.pycard/pycard.conf"

    parser = argparse.ArgumentParser(
        description = 'prints contacts cards matching a search string')
    parser.add_argument(
        "-c", "--config", action = "store", dest = "configfile",
        default = "~/.pycard/pycard.conf",
        help="defaults to ~/.pycard/pycard.conf")
    parser.add_argument("-v", "--version", action = "version", version = "0.3.4")
    parser.add_argument("-a", action = "store_true", dest = "display_all",
            default = "False", help = "prints the whole card, not only name, "
            "telephone numbers and email addresses")
    parser.add_argument("-m", dest = "print_function", action = "store_const",
            const = "print_email", default = "print_contact_info",
            help = "only prints email addresses, in a mutt friendly format")
    parser.add_argument("--debug", action = "store_true", dest = "debug",
            default = "False", help = "enable debugging")
    parser.add_argument("search_string", metavar = "SEARCHSTRING",
            help = "the string to search for")
    args = parser.parse_args()

    # let's try to hide some ugly python code, at least when hitting Ctrl-C
    signal.signal(signal.SIGINT, signal_handler)
    configfile = path.expanduser(args.configfile)

    parser = SafeConfigParser()
    parser.read(configfile)
    db_path = path.expanduser(parser.get('default', 'db_path'))
    # testing if the db exists
    if not path.exists(db_path):
        sys.exit(str(db_path) + " file does not exist, please sync with pycardsyncer first.")
    # testing for database version
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        cursor.execute('SELECT version FROM version')
        database_version = 1
        if not cursor.fetchone()[0] == database_version:
            sys.exit(str(db_path) + " is probably not a valid or an outdated database.\nYou should consider to remove it and sync again using pycardsyncer.")
    except sqlite3.OperationalError:
            sys.exit(str(db_path) + " is probably not a valid or an outdated database.\nYou should consider to remove it and sync again using pycardsyncer.")


    my_query = PcQuery()
    my_query.db_path = path.expanduser(db_path)
    my_query.search_string = args.search_string.decode(my_query.encoding,
                                                       my_query.errors)
    my_query.debug = args.debug
    my_query.print_function = args.print_function
    my_query.display_all = args.display_all



    print "searching for " + args.search_string + "..."
    my_query.search()


    return 0

if __name__ == "__main__":
    main(sys.argv[1:0])
