"""Class for performing HTTP client requests via libcurl"""

from __future__ import absolute_import
from nilmdb.printf import *

import time
import sys
import re
import os
import simplejson as json

import nilmdb.httpclient

# Other functions expect to see these in the nilmdb.client namespace
from nilmdb.httpclient import ClientError, ServerError, Error

version = "1.0"

class Client(object):
    """Main client interface to the Nilm database."""

    client_version = version

    def __init__(self, url):
        self.http = nilmdb.httpclient.HTTPClient(url)

    def _json_param(self, data):
        """Return compact json-encoded version of parameter"""
        return json.dumps(data, separators=(',',':'))

    def close(self):
        self.http.close()

    def geturl(self):
        """Return the URL we're using"""
        return self.http.baseurl

    def version(self):
        """Return server version"""
        return self.http.get("version")

    def dbpath(self):
        """Return server database path"""
        return self.http.get("dbpath")

    def dbsize(self):
        """Return server database size as human readable string"""
        return self.http.get("dbsize")

    def stream_list(self, path = None, layout = None):
        params = {}
        if path is not None:
            params["path"] = path
        if layout is not None:
            params["layout"] = layout
        return self.http.get("stream/list", params)

    def stream_get_metadata(self, path, keys = None):
        params = { "path": path }
        if keys is not None:
            params["key"] = keys
        return self.http.get("stream/get_metadata", params)

    def stream_set_metadata(self, path, data):
        """Set stream metadata from a dictionary, replacing all existing
        metadata."""
        params = {
            "path": path,
            "data": self._json_param(data)
            }
        return self.http.get("stream/set_metadata", params)

    def stream_update_metadata(self, path, data):
        """Update stream metadata from a dictionary"""
        params = {
            "path": path,
            "data": self._json_param(data)
            }
        return self.http.get("stream/update_metadata", params)

    def stream_create(self, path, layout):
        """Create a new stream"""
        params = { "path": path,
                   "layout" : layout }
        return self.http.get("stream/create", params)

    def stream_insert(self, path, data):
        """Insert data into a stream.  data should be a file-like object
        that provides ASCII data that matches the database layout for path."""
        params = { "path": path }

        # See design.md for a discussion of how much data to send.
        # These are soft limits -- actual data might be rounded up.
        max_data = 1048576
        max_time = 30

        def sendit():
            result = self.http.put("stream/insert", send_data, params)
            params["old_timestamp"] = result[1]
            return result

        result = None
        start = time.time()
        send_data = ""
        for line in data:
            elapsed = time.time() - start
            send_data += line

            if (len(send_data) > max_data) or (elapsed > max_time):
                result = sendit()
                send_data = ""
                start = time.time()
        if len(send_data):
            result = sendit()

        # Return the most recent JSON result we got back, or None if
        # we didn't make any requests.
        return result

    def stream_intervals(self, path, start = None, end = None):
        """
        Return a generator that yields each stream interval.
        """
        params = {
            "path": path
        }
        if start is not None:
            params["start"] = repr(start)  # use repr to keep precision
        if end is not None:
            params["end"] = repr(end)
        return self.http.get_gen("stream/intervals", params, retjson = True)

    def stream_extract(self, path, start = None, end = None, count = False):
        """
        Extract data from a stream.  Returns a generator that yields
        lines of ASCII-formatted data that matches the database
        layout for the given path.

        Specify count=True to just get a count of values rather than
        the actual data.
        """
        params = {
            "path": path,
        }
        if start is not None:
            params["start"] = repr(start)  # use repr to keep precision
        if end is not None:
            params["end"] = repr(end)
        if count:
            params["count"] = 1

        return self.http.get_gen("stream/extract", params, retjson = False)
