#!/usr/bin/env python2
# vim:fileencoding=utf-8
from __future__ import (unicode_literals, division, absolute_import,
                        print_function)

__license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'

from itertools import izip_longest
from collections import namedtuple, OrderedDict
from operator import attrgetter
from functools import partial

Option = namedtuple('Option', 'name default longdoc shortdoc choices')

class Choices(frozenset):
    def __new__(cls, *args):
        self = super(Choices, cls).__new__(cls, args)
        self.default = args[0]
        return self

raw_options = (

    'Path to the SSL certificate file',
    'ssl_certfile', None,
    None,

    'Path to the SSL private key file',
    'ssl_keyfile', None,
    None,

    'Time (in seconds) after which an idle connection is closed',
    'timeout', 120.0,
    None,

    'Total time in seconds to wait for clean shutdown',
    'shutdown_timeout', 5.0,
    None,

    'Enable/disable socket pre-allocation, for example, with systemd socket activation',
    'allow_socket_preallocation', True,
    None,

    'Max. size of single HTTP header (in KB)',
    'max_header_line_size', 8.0,
    None,

    'Max. allowed size for files uploaded to the server (in MB)',
    'max_request_body_size', 500.0,
    None,

    'Minimum size for which responses use data compression (in bytes)',
    'compress_min_size', 1024,
    None,

    'Number of worker threads to use to process requests',
    'worker_count', 10,
    None,

    'The port on which to listen for connections',
    'port', 8080,
    None,

    'A prefix to prepend to all URLs',
    'url_prefix', None,
    'Useful if you wish to run this server behind a reverse proxy.',

    'Advertise OPDS feeds via BonJour',
    'use_bonjour', True,
    'Advertise the OPDS feeds via the BonJour service, so that OPDS based'
    ' reading apps can detect and connect to the server automatically.',

    'The interface on which to listen for connections',
    'listen_on', '0.0.0.0',
    'The default is to listen on all available interfaces. You can change this to, for'
    ' example, "127.0.0.1" to only listen for connections from the local machine, or'
    ' to "::" to listen to all incoming IPv6 and IPv4 connections.',

    'Fallback to auto-detected interface',
    'fallback_to_detected_interface', True,
    'If for some reason the server is unable to bind to the interface specified in'
    ' the listen_on option, then it will try to detect an interface that connects'
    ' to the outside world and bind to that.',

    'Enable/disable zero copy file transfers for increased performance',
    'use_sendfile', True,
    'This will use zero-copy in-kernel transfers when sending files over the network,'
    ' increasing performance. However, it can cause corrupted file transfers on some'
    ' broken filesystems. If you experience corrupted file transfers, turn it off.',

    'Max. log file size (in MB)',
    'max_log_size', 20,
    'The maximum size of log files, generated by the server. When the log becomes larger'
    ' than this size, it is automatically rotated.',
)
assert len(raw_options) % 4 == 0

options = []

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(*args, fillvalue=fillvalue)

for shortdoc, name, default, doc in grouper(4, raw_options):
    choices = None
    if isinstance(default, Choices):
        choices = default
        default = default.default
    options.append(Option(name, default, doc, shortdoc, choices))
options = OrderedDict([(o.name, o) for o in sorted(options, key=attrgetter('name'))])
del raw_options

class Options(object):

    __slots__ = tuple(name for name in options)

    def __init__(self, **kwargs):
        for opt in options.itervalues():
            setattr(self, opt.name, kwargs.get(opt.name, opt.default))

def opt_to_cli_help(opt):
    ans = opt.shortdoc
    if not ans.endswith('.'):
        ans += '.'
    if opt.longdoc:
        ans += '\n\t' + opt.longdoc
    return ans

def boolean_option(add_option, opt):
    name = opt.name.replace('_', '-')
    help = opt_to_cli_help(opt)
    add_option('--enable-' + name, action='store_true', help=help)
    add_option('--disable-' + name, action='store_false', help=help)

def opts_to_parser(usage):
    from calibre.utils.config import OptionParser
    parser =  OptionParser(usage)
    for opt in options.itervalues():
        add_option = partial(parser.add_option, dest=opt.name, help=opt_to_cli_help(opt), default=opt.default)
        if opt.default is True or opt.default is False:
            boolean_option(add_option, opt)
        elif opt.choices:
            name = '--' + opt.name.replace('_', '-')
            add_option(name, choices=opt.choices)
        else:
            name = '--' + opt.name.replace('_', '-')
            otype = 'string'
            if isinstance(opt.default, (int, long, float)):
                otype = type(opt.default).__name__
            add_option(name, type=otype)

    return parser
