#!/usr/bin/env python
#
# Public Domain 2014-present MongoDB, Inc.
# Public Domain 2008-2014 WiredTiger, Inc.
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# [TEST_TAGS]
# ignored_file
# [END_TAGS]

# hook_demo.py
#   Demonstration of hooks.  Run via:
#     python run.py --hook demo=N base01
#
#  These hooks are set up:
#  - alter wiredtiger_open arguments (in a benign way)
#  - report after wiredtiger_open is called.
#  - notify on session.open_cursor
#  - intercept the session.create call
#
#   With N == 0, the session.create call reports its arguments and calls original session.create.
#   with N == 1, it does an additional session.drop call (which should cause tests to fail);
#   with N == 2, it does an additional session.create after the drop call (which should work).
#
#   Note that notify hooks don't have to simply report, they can call other methods,
#   set attributes on objects, etc.  For example, one can save the open_cursor
#   config string as an attribute on the cursor object, and examine it in another
#   hooked method.
from __future__ import print_function

import os, sys, wthooks
from wttest import WiredTigerTestCase

# Print to /dev/tty for debugging, since anything extraneous to stdout/stderr will
# cause a test error.
def tty(s):
    WiredTigerTestCase.tty(s)

# These are the hook functions that are run when particular APIs are called.

# Called to manipulate args for wiredtiger_open
def wiredtiger_open_args(ignored_self, args):
    tty('>>> wiredtiger_open, adding cache_size')
    args = list(args)    # convert from a readonly tuple to a writeable list
    args[-1] += ',,,cache_size=500M,,,'   # modify the last arg
    return args

# Called to notify after successful wiredtiger_open
def wiredtiger_open_notify(ignored_self, ret, *args):
    tty('>>> wiredtiger_open({}) returned {}'.format(args, ret))

# Called to notify after successful Session.open_cursor
def session_open_cursor_notify(self, ret, *args):
    tty('>>> session.open_cursor({}) returned {}, session is {}'.format(args, ret, self))

# Called to replace Session.create
# We do different things (described above) as indicated by our command line argument.
def session_create_replace(arg, orig_session_create, session_self, uri, config):
    tty('>>> session.create({},{}), session is {}'.format(uri, config, session_self))
    if arg == 0:
        # Just do a regular create
        return orig_session_create(session_self, uri, config)
    elif arg == 1:
        # Do a regular create, followed by a drop.  This will cause test failures.
        ret = orig_session_create(session_self, uri, config)
        # We didn't replace drop, so we can call it as a method
        tty('>>> session.drop({})'.format(uri))
        session_self.drop(uri)
        return ret
    elif arg == 2:
        # Do a regular create, followed by a drop, then another create.  Should work.
        ret = orig_session_create(session_self, uri, config)
        # We didn't replace drop, so we can call it as a method
        tty('>>> session.drop({})'.format(uri))
        session_self.drop(uri)
        tty('>>> session.create({},{})'.format(uri, config))
        orig_session_create(session_self, uri, config)
        return ret

# Every hook file must have one or more classes descended from WiredTigerHook
# This is where the hook functions are 'hooked' to API methods.
class DemoHookCreator(wthooks.WiredTigerHookCreator):
    def __init__(self, arg=0):
        # An argument may alter the test
        if arg == None:
            self.arg = 0
        else:
            self.arg = int(arg)

    # We have an opportunity to filter the list of tests to be run.
    # For this demo, we don't filter.
    def filter_tests(self, tests):
        print('Filtering: ' + str(tests))
        return tests

    def setup_hooks(self):
        tty('>> SETUP HOOKS RUN')
        orig_session_create = self.Session['create']     # gets original function
        self.wiredtiger['wiredtiger_open'] = (wthooks.HOOK_ARGS, wiredtiger_open_args)
        self.wiredtiger['wiredtiger_open'] = (wthooks.HOOK_NOTIFY, wiredtiger_open_notify)
        self.Session['create'] = (wthooks.HOOK_REPLACE, lambda s, uri, config:
          session_create_replace(self.arg, orig_session_create, s, uri, config))
        self.Session['open_cursor'] = (wthooks.HOOK_NOTIFY, session_open_cursor_notify)

# Every hook file must have a top level initialize function,
# returning a list of WiredTigerHook objects.
def initialize(arg):
    return [DemoHookCreator(arg)]
