#!/usr/bin/env python
# -*- coding: utf-8 -*-


"""(Tool) Bench Marking Tool

THis tool does some simple benchmaking of the circuits library.
"""

import sys
import math
import optparse

from time import sleep

if sys.platform == "win32":
    from time import clock as time
else:
    from time import time  # NOQA

try:
    import hotshot
    import hotshot.stats
except ImportError:
    hotshot = None  # NOQA

try:
    import psyco
except ImportError:
    psyco = None  # NOQA

from circuits import __version__ as systemVersion
from circuits import handler, Event, Component, Manager, Debugger

USAGE = "%prog [options]"
VERSION = "%prog v" + systemVersion


def duration(seconds):
    days = int(seconds / 60 / 60 / 24)
    seconds = (seconds) % (60 * 60 * 24)
    hours = int((seconds / 60 / 60))
    seconds = (seconds) % (60 * 60)
    mins = int((seconds / 60))
    seconds = int((seconds) % (60))
    return (days, hours, mins, seconds)


def parse_options():
    """parse_options() -> opts, args

    Parse the command-line options given returning both
    the parsed options and arguments.
    """

    parser = optparse.OptionParser(usage=USAGE, version=VERSION)

    parser.add_option(
        "-t", "--time",
        action="store", type="int", default=0, dest="time",
        help="Stop after specified elapsed seconds"
    )

    parser.add_option(
        "-e", "--events",
        action="store", type="int", default=0, dest="events",
        help="Stop after specified number of events"
    )

    parser.add_option(
        "-p", "--profile",
        action="store_true", default=False, dest="profile",
        help="Enable execution profiling support"
    )

    parser.add_option(
        "-d", "--debug",
        action="store_true", default=False, dest="debug",
        help="Enable debug mode"
    )

    parser.add_option(
        "-m", "--mode",
        action="store", type="choice", default="speed", dest="mode",
        choices=["sync", "speed", "latency"],
        help="Operation mode"
    )

    parser.add_option(
        "-s", "--speed",
        action="store_true", default=False, dest="speed",
        help="Enable psyco (circuits on speed!)"
    )

    parser.add_option(
        "-q", "--quiet",
        action="store_false", default=True, dest="verbose",
        help="Suppress output"
    )

    opts, args = parser.parse_args()

    return opts, args


class Stop(Event):
    """Stop Event"""


class Term(Event):
    """Term Event"""


class Hello(Event):
    """Hello Event"""


class Received(Event):
    """Received Event"""


class Base(Component):

    def __init__(self, opts, *args, **kwargs):
        super(Base, self).__init__(*args, **kwargs)

        self.opts = opts


class SpeedTest(Base):

    def received(self, message=""):
        self.fire(Hello("hello"))

    def hello(self, message):
        self.fire(Received(message))


class LatencyTest(Base):

    t = None

    def received(self, message=""):
        print("Latency: %0.9f us" % ((time() - self.t) * 1e6))
        sleep(1)
        self.fire(Hello("hello"))

    def hello(self, message=""):
        self.t = time()
        self.fire(Received(message))


class State(Base):

    done = False

    def stop(self):
        self.fire(Term())

    def term(self):
        self.done = True


class Monitor(Base):

    sTime = sys.maxsize
    events = 0
    state = 0

    @handler(filter=True)
    def event(self, *args, **kwargs):
        self.events += 1

        if self.events > self.opts.events:
            self.stop()


def main():
    opts, args = parse_options()

    if opts.speed and psyco:
        psyco.full()

    manager = Manager()

    monitor = Monitor(opts)
    manager += monitor

    state = State(opts)
    manager += state

    if opts.debug:
        manager += Debugger()

    if opts.mode.lower() == "speed":
        if opts.verbose:
            print("Setting up Speed Test...")
        manager += SpeedTest(opts)
        monitor.sTime = time()
    elif opts.mode.lower() == "latency":
        if opts.verbose:
            print("Setting up Latency Test...")
        manager += LatencyTest(opts)
        monitor.sTime = time()

    if opts.verbose:
        print("Setting up Sender...")
        print("Setting up Receiver...")

    monitor.sTime = time()

    if opts.profile:
        if hotshot:
            profiler = hotshot.Profile("bench.prof")
            profiler.start()

    manager.fire(Hello("hello"))

    while not state.done:
        try:
            manager.tick()

            if opts.events > 0 and monitor.events > opts.events:
                manager.fire(Stop())
            if opts.time > 0 and (time() - monitor.sTime) > opts.time:
                manager.fire(Stop())
        except KeyboardInterrupt:
            manager.fire(Stop())

    if opts.verbose:
        print()

    eTime = time()

    tTime = eTime - monitor.sTime

    events = monitor.events
    speed = int(math.ceil(float(monitor.events) / tTime))

    print("Total Events: %d (%d/s after %0.2fs)" % (events, speed, tTime))

    if opts.profile and hotshot:
        profiler.stop()
        profiler.close()

        stats = hotshot.stats.load("bench.prof")
        stats.strip_dirs()
        stats.sort_stats("time", "calls")
        stats.print_stats(20)

###
### Entry Point
###

if __name__ == "__main__":
    main()
