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

# thumbor imaging service
# https://github.com/thumbor/thumbor/wiki

# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license
# Copyright (c) 2011 globo.com thumbor@googlegroups.com

import gc
import sys
import logging
import logging.config
import schedule
import warnings

import os
import socket
from os.path import expanduser, dirname

import tornado.ioloop
from tornado.httpserver import HTTPServer

from thumbor.console import get_server_parameters
from thumbor.config import Config
from thumbor.importer import Importer
from thumbor.context import Context
from thumbor.utils import which

from PIL import Image

try:
    basestring
except NameError:
    basestring = str


def get_as_integer(value):
    try:
        return int(value)
    except (ValueError, TypeError):
        return None


def get_config(config_path):
    lookup_paths = [os.curdir,
                    expanduser('~'),
                    '/etc/',
                    dirname(__file__)]

    return Config.load(config_path, conf_name='thumbor.conf', lookup_paths=lookup_paths)


def configure_log(config, log_level):
    if (config.THUMBOR_LOG_CONFIG and config.THUMBOR_LOG_CONFIG != ''):
        logging.config.dictConfig(config.THUMBOR_LOG_CONFIG)
    else:
        logging.basicConfig(
            level=getattr(logging, log_level),
            format=config.THUMBOR_LOG_FORMAT,
            datefmt=config.THUMBOR_LOG_DATE_FORMAT
        )


def get_importer(config):
    importer = Importer(config)
    importer.import_modules()

    if importer.error_handler_class is not None:
        importer.error_handler = importer.error_handler_class(config)

    return importer


def validate_config(config, server_parameters):
    if server_parameters.security_key is None:
        server_parameters.security_key = config.SECURITY_KEY

    if not isinstance(server_parameters.security_key, basestring):
        raise RuntimeError(
            'No security key was found for this instance of thumbor. ' +
            'Please provide one using the conf file or a security key file.')

    if config.ENGINE or config.USE_GIFSICLE_ENGINE:
        # Error on Image.open when image pixel count is above MAX_IMAGE_PIXELS
        warnings.simplefilter('error', Image.DecompressionBombWarning)

    if config.USE_GIFSICLE_ENGINE:
        server_parameters.gifsicle_path = which('gifsicle')
        if server_parameters.gifsicle_path is None:
            raise RuntimeError(
                'If using USE_GIFSICLE_ENGINE configuration to True, the `gifsicle` binary must be in the PATH '
                'and must be an executable.'
            )


def get_context(server_parameters, config, importer):
    return Context(
        server=server_parameters,
        config=config,
        importer=importer
    )


def get_application(context):
    return context.modules.importer.import_class(context.app_class)(context)


def run_server(application, context):
    server = HTTPServer(application, xheaders=True)

    if context.server.fd is not None:
        fd_number = get_as_integer(context.server.fd)
        if fd_number is None:
            with open(context.server.fd, 'r') as sock:
                fd_number = sock.fileno()

        sock = socket.fromfd(fd_number,
                             socket.AF_INET | socket.AF_INET6,
                             socket.SOCK_STREAM)
        server.add_socket(sock)
    else:
        server.bind(context.server.port, context.server.ip)

    server.start(1)


def gc_collect():
    collected = gc.collect()
    if collected > 0:
        logging.warn('Garbage collector: collected %d objects.' % collected)


def main(arguments=None):
    '''Runs thumbor server with the specified arguments.'''
    if arguments is None:
        arguments = sys.argv[1:]

    server_parameters = get_server_parameters(arguments)
    config = get_config(server_parameters.config_path)
    configure_log(config, server_parameters.log_level.upper())

    validate_config(config, server_parameters)

    importer = get_importer(config)

    with get_context(server_parameters, config, importer) as context:
        application = get_application(context)
        run_server(application, context)

        if (config.GC_INTERVAL and config.GC_INTERVAL > 0):
            schedule.every(config.GC_INTERVAL).seconds.do(gc_collect)

        try:
            logging.debug('thumbor running at %s:%d' % (context.server.ip, context.server.port))
            tornado.ioloop.IOLoop.instance().start()
        except KeyboardInterrupt:
            sys.stdout.write('\n')
            sys.stdout.write("-- thumbor closed by user interruption --\n")


if __name__ == "__main__":
    main(sys.argv)
