#!/usr/bin/env python
# coding=utf-8
import logging
from datetime import datetime
from math import ceil
from os import listdir, path, stat
from platform import node
from sys import argv

from boto.exception import S3ResponseError
from boto.s3.connection import S3Connection, OrdinaryCallingFormat
from filechunkio import FileChunkIO

try:
    from configparser import SafeConfigParser, Error as parseError
except ImportError:
    from ConfigParser import SafeConfigParser, Error as parseError


class S3Pusher(object):
    def __init__(self, config):
        self.hostname = node()
        self.suffix = ".0.gz"
        self.key_suffix = ".gz"
        self.directory = "."
        self.filename = datetime.now().strftime("%Y-%m-%d")
        self.bucket = None
        self.map = {}
        self.chunk_size = 52428800
        self.connect(config)

    def connect(self, config):
        configs = SafeConfigParser()
        try:
            configs.read(config)
            logging.debug("conf (%s) parsed" % configs)
            self.directory = configs.get("logs", "directory")
            self.suffix = configs.get("logs", "suffix")
            if configs.has_option("S3", "chunk_size"):
                self.chunk_size = configs.getint("S3", "chunk_size")
            if configs.has_option("logs", "filename"):
                self.filename = configs.get("logs", "filename")
            if configs.has_option("logs", "key_suffix"):
                self.key_suffix = configs.get("logs", "key_suffix")
            self.map = dict(configs.items("map"))
            conn = S3Connection(
                aws_access_key_id=configs.get("S3", "access_key"),
                aws_secret_access_key=configs.get("S3", "secret_key"),
                host=configs.get("S3", "host"),
                calling_format=OrdinaryCallingFormat(),
                is_secure=True)
            bucket_name = configs.get("S3", "bucket")
            try:
                bucket = conn.get_bucket(bucket_name)
                self.bucket = bucket
            except S3ResponseError:
                logging.warning("Bucket %s not found. Creating." % bucket_name)
                bucket = conn.create_bucket(bucket_name)
                self.bucket = bucket

            logging.info("Using bucket %s" % self.bucket.name)

        except parseError as e:
            logging.error("Bad config file: %s" % e)
            exit(1)

    def list_candidates(self):
        candidates = [filename.lower() for filename in listdir(self.directory)
                      if filename.endswith(self.suffix) and
                      filename[:filename.find(self.suffix)].lower() in self.map]
        logging.info("Found %s candidates at %s"
                     % (len(candidates), self.directory))
        return candidates

    def push_file(self, filename):
        key_name = path.join(
            self.hostname,
            self.map[filename[:filename.find(self.suffix)]],
            self.filename + self.key_suffix
        )
        key = self.bucket.initiate_multipart_upload(key_name)
        source_path = path.join(self.directory, filename)
        source_size = stat(source_path).st_size
        chunk_count = int(ceil(source_size / float(self.chunk_size)))
        logging.debug("%s uploading as %s. %d chunks"
                      % (filename, key_name, chunk_count))
        for i in range(chunk_count):
            offset = self.chunk_size * i
            amount = min(self.chunk_size, source_size - offset)
            with FileChunkIO(source_path, 'r',
                             offset=offset, bytes=amount) as fp:
                key.upload_part_from_file(fp, part_num=i + 1)

        key.complete_upload()
        logging.debug("%s uploaded" % filename)

    def push_candidates(self):
        for filename in self.list_candidates():
            self.push_file(filename)


if __name__ == '__main__':
    if len(argv) > 1:
        if len(argv) > 2:
            if argv[1] == "-v":
                pusher = S3Pusher(argv[2])
                logging.getLogger().setLevel(logging.DEBUG)
            elif argv[2] == "-v":
                pusher = S3Pusher(argv[1])
                logging.getLogger().setLevel(logging.DEBUG)
            else:
                pusher = None
                logging.error("Usage %s [-v] config_file.conf" % argv[0])
                exit(2)
        else:
            pusher = S3Pusher(argv[1])

        pusher.push_candidates()
    else:
        logging.error("Usage %s [-v] config_file.conf" % argv[0])
        exit(1)
