"""versioning.py

GLUDB versioning implementation
"""

import json
import datetime

import json_delta  # External dependency

from .utils import now_field


# Yes, this could be an enum, but we're supporting Python 2.7
class VersioningTypes(object):
    NONE = "ver:none"
    DELTA_HISTORY = "ver:delta"


# Python 2&3 compatible string testing
def _isstr(s):
    try:
        _basestring = basestring
    except NameError:
        _basestring = str
    return isinstance(s, _basestring)


# If any parameter is a string, parse it as JSON
def _norm_json_params(*args):
    return tuple([
        json.loads(param) if _isstr(param) else param
        for param in args
    ])


def record_diff(old, new):
    """Return a JSON-compatible structure capable turn the `new` record back
    into the `old` record. The parameters must be structures compatible with
    json.dumps *or* strings compatible with json.loads. Note that by design,
    `old == record_patch(new, record_diff(old, new))`"""
    old, new = _norm_json_params(old, new)
    return json_delta.diff(new, old, verbose=False)


def record_patch(rec, diff):
    """Return the JSON-compatible structure that results from applying the
    changes in `diff` to the record `rec`. The parameters must be structures
    compatible with json.dumps *or* strings compatible with json.loads. Note
    that by design, `old == record_patch(new, record_diff(old, new))`"""
    rec, diff = _norm_json_params(rec, diff)
    return json_delta.patch(rec, diff, in_place=False)


def append_diff_hist(diff, diff_hist=list()):
    """Given a diff as generated by record_diff, append a diff record to the
    list of diff_hist records."""
    diff, diff_hist = _norm_json_params(diff, diff_hist)

    if not diff_hist:
        diff_hist = list()

    diff_hist.append({'diff': diff, 'diff_date': now_field()})
    return diff_hist


def parse_diff_hist(curr_obj, diff_hist):
    """Given a diff_hist as created, appended by append_diff_hist, yield the
    versions of the object start with curr_obj and working backwards in time.
    Each instance yielded is of the form (obj, date-string) where obj is the
    JSON version of the object created by applying a diff in the
    diff history and date-string is a string representing the date/time that
    the diff was taken"""
    curr_obj, diff_hist = _norm_json_params(curr_obj, diff_hist)

    yield (json.dumps(curr_obj), None)

    last_obj = curr_obj
    for one in reversed(diff_hist):
        last_obj = record_patch(last_obj, one['diff'])
        yield json.dumps(last_obj), one['diff_date']
