import logging
import argparse
import threading
import time

from rc_exploit.utils.log import configure_logging
from rc_exploit.utils.load_plugin import load_plugin
from rc_exploit.utils.create_threads import create_threads


def main():
    args = parse_args()

    configure_logging(True)

    try:
        plugin_inst = load_plugin(args.plugin, *args.remainder)
    except Exception, e:
        err_args = (args.plugin, e)
        logging.error('Failed to load plugin "%s". Exception: "%s"' % err_args)
        return -1

    logging.debug('Successfully loaded plugin')

    send_last_byte = threading.Event()
    get_response = threading.Event()

    logging.info('Using %s threads/connections' % args.threads)
    all_threads = create_threads(args.threads, plugin_inst, send_last_byte,
                                 get_response)

    for thread in all_threads:
        logging.debug('Starting thread %s' % thread.num_thread)
        thread.start()

    ready_threads = []
    for _ in xrange(500):
        time.sleep(0.1)

        if len(ready_threads) == len(all_threads):
            break

        for thread in all_threads:
            if thread.ready and thread not in ready_threads:
                logging.debug('Thread %s is ready (core)' % thread.num_thread)
                ready_threads.append(thread)
    else:
        logging.debug('Timeout while waiting for threads to be ready')

    for thread in all_threads:
        if not thread.ready:
            msg = 'Thread %s is not ready but we continue anyways'
            logging.debug(msg % thread.num_thread)

    # All ready threads were waiting for this
    logging.info('Sending last byte in all threads/connections')
    send_last_byte.set()

    # Wait for the threads to send the last byte and get/analyze the response
    time.sleep(2)
    get_response.set()

    # Now we join threads and exit
    for thread in all_threads:
        thread.join()

    logging.info('All requests sent. Now you need to manually verify if the'
                 ' race condition was successfully exploited.')

    # Do any cleanup you require
    plugin_inst.end()

    return 0


def parse_args():
    """
    return: Parsed arguments
    """
    parser = argparse.ArgumentParser(description='Race condition exploiter')
    parser.add_argument('plugin', help='Python module to load requests from'
                                       ' (ie. paypal.hack will load python'
                                       ' module from paypal/hack.py)')
    parser.add_argument('--threads', type=int, default=50,
                        help='Number of threads/connections to use')
    parser.add_argument('remainder', nargs=argparse.REMAINDER)
    return parser.parse_args()

