#!/usr/local/bin/python3
#+
# Copyright 2014 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
#####################################################################


import argh
import json
import sys
import datastore
from bson import json_util
from datastore.config import ConfigStore


DEFAULT_CONFIGFILE = '/usr/local/etc/middleware.conf'
ds = None


def strtoint(v):
    # If string is quoted, strip quotes and return
    if v[0] == '"' and v[-1] == '"':
        return v[1:-1]

    try:
        v = int(v)
        return v
    except ValueError:
        pass

    return v


def init_datastore(filename, tcp=False):
    global ds

    try:
        ds = datastore.get_datastore(filename, tcp=tcp)
    except datastore.DatastoreException as err:
        print("Cannot initialize datastore: {0}".format(str(err)), file=sys.stderr)
        sys.exit(1)


def json_select(obj, selector=None):
    result = []
    if selector is None:
        return obj

    for path in selector.split(','):
        ptr = obj
        path = path.strip()

        for elem in path.split('.'):
            if not elem in ptr:
                result.append(None)
                break

            ptr = ptr[elem]

        result.append(ptr)

    return result


@argh.arg('--pkey')
@argh.arg('--type')
def create(name, pkey='uuid', type='config'):
    try:
        ds.collection_create(name, pkey_type=pkey, attributes={'type': type})
    except datastore.DatastoreException as e:
        print("Cannot create collection: {0}".format(str(e)), file=sys.stderr)
        sys.exit(1)


def list():
    global ds
    for coll in ds.collection_list():
        print(coll)


@argh.arg('--select')
def get(name, id, select=None):
    obj = ds.get_by_id(id)
    if obj is None:
        print("Object {0} not found in collection {1}".format(id, name), file=sys.stderr)
        sys.exit(1)

    print(json.dumps(json_select(obj, select)))


@argh.arg('--select')
@argh.arg('--sort')
@argh.arg('--limit', type=int)
@argh.arg('filter', nargs='*')
def query(name, filter, select=None, sort=None, limit=None):
    params = []
    for i in filter:
        params.append(i.split())

    for row in ds.query(name, *params, sort=sort, limit=limit):
        print(json.dumps(json_select(row, select), default=json_util.default))


def config_list(key=None):
    cfg = ConfigStore(ds)
    for item in cfg.list_children(key):
        print("{0} = {1}".format(item['id'], json.dumps(item['value'])))


def config_get(key):
    cfg = ConfigStore(ds)
    print(json.dumps(cfg.get(key)))


def config_set(key, value):
    cfg = ConfigStore(ds)
    cfg.set(key, json.loads(value))


@argh.arg('fields', nargs='*')
def update(name, pkey, fields):
    replace = {}
    pkey = strtoint(pkey)

    for k, v in zip(fields[::2], fields[1::2]):
        v = strtoint(v)
        replace[k] = v

    item = ds.get_by_id(name, pkey)
    item.update(replace)
    ds.update(name, pkey, item)


def main():
    parser = argh.ArghParser()
    parser.add_argument('-c', metavar='CONFIG', default=DEFAULT_CONFIGFILE, help='Config file name')
    parser.add_argument('--tcp', action='store_true', default=False, help='Use TCP connection')
    parser.add_commands([create, list, get, query, update, config_list, config_get, config_set])
    args = parser.parse_args()
    init_datastore(args.c, tcp=args.tcp)
    parser.dispatch()


if __name__ == '__main__':
    main()
