#!/usr/bin/env python
""" Main module of replayer.
"""

import argparse
import logging
import signal
import sys
import os.path
from datetime import datetime
import time
import multiprocessing
from Queue import Full

import apachelog

import replayer
from replayer.config_reader import ConfigReader
from replayer.http_worker import HTTPWorker
from replayer.url_filter import URLFilter
from replayer.url_builder import URLBuilder
from replayer.config_constants import ConfigConstants
from replayer.inspector import Inspector


# global variables
worker_list = []
killed = False


# signal handler to handle ctrl-c
def signal_handler(signal, frame):
    logging.info('[%s] SIGINT received. Stopping...', multiprocessing.current_process().name)
    global killed
    killed = True
    map(lambda w: w.kill(), worker_list)


def print_infos(logfile):
    logging.info('This is replayer, Version %s', replayer.__version__)
    logging.info('Copyright 2014 Andreas Buechele, https://github.com/buechele/replayer')
    logging.info('replayer is released under the terms of the MIT license.')
    logging.info('')

    now = datetime.now()
    logging.info('[%s] Replaying %s (be patient)...', now.strftime('%d/%m/%Y %H:%M:%S'), logfile)


def print_results(config, inspector):
    logging.info('')
    logging.info('Host:\t\t\t' + config[ConfigConstants.HOST])
    logging.info('')
    logging.info(inspector)


def parse_datetime(data):
    log_time, zone = data.split()
    log_time = log_time.translate(None, "[]")
    return datetime.strptime(log_time, '%d/%b/%Y:%H:%M:%S')


def main():
    # register signal handler
    signal.signal(signal.SIGINT, signal_handler)

    # parse command line args
    parser = argparse.ArgumentParser(description='Replay an Apache access-log.')
    parser.add_argument('-f', dest=ConfigConstants.LOGFILE, help='Apache access.log', required=True)
    parser.add_argument('-c', dest=ConfigConstants.CONFIGFILE, help='Configuration file to use', required=False)
    parser.add_argument('-n', dest=ConfigConstants.WORKERCOUNT, type=int, help='Concurrency level', default=1,
                        required=False)
    parser.add_argument('--delay', dest=ConfigConstants.DELAY, type=int,
                        help='Delay between requests in milliseconds', default=0,
                        required=False)
    parser.add_argument('-v', dest=ConfigConstants.VERBOSE, help='Increase output verbosity', action='store_true',
                        required=False)
    parser.add_argument('--timed', dest=ConfigConstants.TIMED, action='store_true')
    parser.set_defaults(timed=False)
    args = vars(parser.parse_args())

    # read config file
    config_reader = ConfigReader(args[ConfigConstants.CONFIGFILE])
    config_reader.merge_dict(args)
    config = config_reader.config

    # configure logging
    logging.getLogger('requests').setLevel(logging.WARNING)
    if config[ConfigConstants.VERBOSE]:
        logging.basicConfig(level=logging.DEBUG, format='%(message)s')
    else:
        logging.basicConfig(level=logging.INFO, format='%(message)s')

    # check if access log exists
    if not os.path.isfile(config[ConfigConstants.LOGFILE]):
        logging.error('log file ' + config[ConfigConstants.LOGFILE] + ' does not exist!')
        sys.exit(1)

    # check if host is configured
    if not config[ConfigConstants.HOST]:
        logging.error('Host is not configured!')
        sys.exit(1)

    # initialize worker objects
    parser = apachelog.parser(config[ConfigConstants.LOGFORMAT])
    url_queue = multiprocessing.Queue(config[ConfigConstants.WORKERCOUNT] * 10)
    url_builder = URLBuilder('http', config[ConfigConstants.HOST], 80, config[ConfigConstants.TRANSFORM])
    if config.has_key(ConfigConstants.FILTER):
        url_filter = URLFilter(config[ConfigConstants.FILTER], config[ConfigConstants.FILTERRULE])
    else:
        url_filter = URLFilter({})

    # general information
    print_infos(config[ConfigConstants.LOGFILE])

    # initialize result queue
    result_queue = multiprocessing.Queue()

    # initialize and start threads
    for i in range(config[ConfigConstants.WORKERCOUNT]):
        name = 'Worker ' + str(i + 1)
        worker = HTTPWorker(name, config[ConfigConstants.HEADER], url_queue, url_filter, url_builder, result_queue,
                            config[ConfigConstants.DELAY])
        worker_list.append(worker)
        worker.start()

    first_line = True

    # read access log
    for line in open(config[ConfigConstants.LOGFILE]):
        if killed:
            break
        try:
            data = parser.parse(line)

            if config[ConfigConstants.TIMED]:
                if first_line:
                    first_line = False
                    last_date = int(parse_datetime(data['%t']).strftime('%s'))
                else:
                    current_date = int(parse_datetime(data['%t']).strftime('%s'))
                    wait_time = current_date - last_date
                    last_date = current_date
                    if wait_time > 0:
                        time.sleep(wait_time)

            url_queue.put(data, True, 2)
        except Full:
            logging.error('Queue is full')
        except:
            logging.error('Unable to parse %s: %s', line, sys.exc_info())

    # signal exit to all workers
    map(lambda w: w.exit(), worker_list)

    # join all workers
    map(lambda w: w.join(), worker_list)

    # sum up the results
    result = Inspector()
    while not result_queue.empty():
        result += result_queue.get_nowait()

    now = datetime.now()
    logging.info('[%s] Done', now.strftime('%d/%m/%Y %H:%M:%S'))
    logging.info('')

    # print results
    print_results(config, result)


if __name__ == '__main__':
    main()