# -*- coding: utf-8 -*-
# Copyright (c) 2012 Geoff Wilson <gmwils@gmail.com>

from cihui import database_url
import json
import logging
import momoko
import os


# TODO(gmwils) refactor into multiple stores

# TODO(gmwils): Change to use a single dict for callbacks. For key in
# the db call, use something like 'id|rock', with a function to
# generate one, and a function to decode one. Then use the id to look
# up the callback and the rock for data that needs to be passed.

# TODO(gmwils): Ensure the dict doesn't grow forever. Compact somehow

class Database:
    def __init__(self, db_url, db=None):
        self.callback_counter = 0
        self.callbacks = {}

        if db is not None:
            self.db = db
        else:
            settings = database_url.build_settings_from_dburl(db_url)
            self.db = momoko.AsyncClient(settings)

    def build_id(self, rock=''):
        cb_id = self.callback_counter
        self.callback_counter += 1
        return '%d|%s' % (cb_id, rock)

    def get_callback(self, cb_id):
        callback = self.callbacks.get(cb_id, None)
        _, rock = cb_id.split('|', 2)

        if callback is not None:
            del self.callbacks[cb_id]

        return callback, rock

    def authenticate_api_user(self, user, passwd):
        valid_user = os.environ.get('API_USER', 'user')
        valid_passwd = os.environ.get('API_PASS', 'secret')

        return (user == valid_user and passwd == valid_passwd)

    def get_account(self, email, callback):
        cb_id = self.build_id(email)

        self.callbacks[cb_id] = callback
        self.db.batch({cb_id: ['SELECT * FROM account WHERE email = %s;', (email,)]},
                      callback=self._on_get_account_response)

    def _on_get_account_response(self, cursors):
        for key, cursor in cursors.items():
            callback, email = self.get_callback(key)

            if len(cursor) == 0:
                callback(None)
            else:
                # TODO(gmwils) build an account object
                callback(cursor.fetchall())

    def get_lists(self, cb):
        cb_id = self.build_id()
        self.callbacks[cb_id] = cb

        self.db.batch({cb_id: ['SELECT id, title FROM list ORDER BY created_at DESC;', ()]},
                      callback=self._on_get_lists_response)

    def _on_get_lists_response(self, cursors):
        for key, cursor in cursors.items():
            callback, _ = self.get_callback(key)

            if cursor is None or cursor.rowcount == 0:
                logging.warning('No lists found in database')
                callback(None)
            else:
                word_lists = []
                for word_list in cursor:
                    word_lists.append({'id': word_list[0], 'title': word_list[1]})

                callback(word_lists)

    def get_word_list(self, list_id, cb):
        cb_id = self.build_id(list_id)
        self.callbacks[cb_id] = cb

        self.db.batch({cb_id: ['SELECT id, title, words FROM list WHERE id = %s;',
                                 (list_id,)]},
                      callback=self._on_get_word_list_response)

    def _on_get_word_list_response(self, cursors):
        for key, cursor in cursors.items():
            callback, list_id = self.get_callback(key)

            if cursor.rowcount != 1:
                logging.warning('Invalid response for get_word_list(%s)', key)
                callback(None)
                continue

            result = cursor.fetchone()
            words = result[2]
            if words is not None:
                words = json.loads(words)

            word_list = {'id': result[0], 'title': result[1], 'words': words}
            callback(word_list)

    def create_list(self, list_name, list_elements, cb):
        cb_id = self.build_id(list_name)
        self.callbacks[cb_id] = cb

        self.db.batch({cb_id: ['INSERT INTO list (title, words) VALUES (%s, %s)',
                               (list_name, json.dumps(list_elements))]},
                      callback=self._on_create_list_response)

    def _on_create_list_response(self, cursors):
        for key, cursor in cursors.items():
            callback, list_name = self.get_callback(key)

            if callback is None:
                # XXX(gmwils) should log an error here
                continue

            # TODO(gmwils) determine success/fail of insert
            callback(True)

@@ -159,401 +159,582 @@
 s%0A%0A%0A
-# TODO(gmwils) refactor into multiple stores%0A%0A# TODO(gmwils): Change to use a single dict for callbacks. For key in%0A# the db call, use something like 'id%7Crock', with a function to%0A# generate one, and a function to decode one. Then use the id to look%0A# up the callback and the rock for data that needs to be passed.%0A%0A# TODO(gmwils): Ensure the dict doesn't grow forever. Compact somehow
+class BaseDatabase(object):%0A    def __init__(self):%0A        self.callback_counter = 0%0A        # TODO(gmwils): Ensure the dict doesn't grow forever. Compact somehow%0A        self.callbacks = %7B%7D%0A%0A    def build_id(self, rock=''):%0A        cb_id = self.callback_counter%0A        self.callback_counter += 1%0A        return '%25d%7C%25s' %25 (cb_id, rock)%0A%0A    def get_callback(self, cb_id):%0A        callback = self.callbacks.get(cb_id, None)%0A        _, rock = cb_id.split('%7C', 2)%0A%0A        if callback is not None:%0A            del self.callbacks%5Bcb_id%5D%0A%0A        return callback, rock%0A
 %0A%0Aclass 
 Data
@@ -729,24 +729,29 @@
 %0A%0Aclass 
+Async
 Database
 :%0A    de
@@ -742,16 +742,30 @@
 Database
+(BaseDatabase)
 :%0A    de
@@ -812,60 +812,44 @@
    s
-elf.callback_counter = 0%0A        self.callbacks = %7B%7D
+uper(AsyncDatabase, self).__init__()
 %0A%0A  
@@ -1038,239 +1038,141 @@
 s)%0A%0A
-    def build_id(self, rock=''):%0A        cb_id = self.callback_counter%0A        self.callback_counter += 1%0A        return '%25d%7C%25s' %25 (cb_id, rock)%0A%0A    def get_callback(self, cb_id):%0A        callback = self.callbacks.get(cb_id, 
+%0A# TODO(gmwils) refactor into multiple stores (Account, List)%0Aclass Database(AsyncDatabase):%0A    def __init__(self, db_url, db=
 None)
+:
 %0A   
@@ -1180,140 +1180,50 @@
     
-_, rock = cb_id.split('%7C', 2)%0A%0A        if callback is not None:%0A            del self.callbacks%5Bcb_id%5D%0A%0A        return callback, rock
+super(Database, self).__init__(db_url, db)
 %0A%0A  
@@ -3006,18 +3006,16 @@
 = %25s;',%0A
-  
         

Refactor into separate classes