#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015-2016 Carlos Jenkins <carlos@jenkins.co.cr>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"""
Development Web Server.

Run me with `webdev [optional_path]`
"""

from __future__ import unicode_literals, absolute_import
from __future__ import print_function, division

import logging
from os import getcwd, chdir
from sys import version_info
from errno import EADDRINUSE
from os.path import isdir, abspath
from argparse import ArgumentParser
from socket import error as socketerror

if version_info[0] < 3:
    from BaseHTTPServer import HTTPServer
    from SimpleHTTPServer import SimpleHTTPRequestHandler
else:
    from http.server import HTTPServer, SimpleHTTPRequestHandler

__version__ = '1.1.0'

log = logging.getLogger(__name__)


FORMAT = '%(asctime)s:::%(levelname)s:::%(message)s'
V_LEVELS = {
    0: logging.ERROR,
    1: logging.WARNING,
    2: logging.INFO,
    3: logging.DEBUG,
}


class HTTPRequestHandler(SimpleHTTPRequestHandler):

    serving_path = None
    protocol_version = 'HTTP/1.0'
    server_version = 'DevelopmentServer/' + __version__

    def send_head(self):
        """
        Patched version of SimpleHTTPRequestHandler.send_head that allows
        to reload when source static folder is deleted (and maybe recreated).
        """
        try:
            chdir(HTTPRequestHandler.serving_path)
        except OSError:

            log.error('No such directory: {}'.format(
                HTTPRequestHandler.serving_path
            ))
            self.send_error(404, 'File not found')
            return None

        return SimpleHTTPRequestHandler.send_head(self)


def parse_args(argv=None):
    """
    Argument parsing routine.
    """
    parser = ArgumentParser(
        description='Development Web Server'
    )
    parser.add_argument(
        '-v', '--verbose',
        help='Increase verbosity level',
        default=0,
        action='count'
    )
    parser.add_argument(
        '--version',
        action='version',
        version='Development Web Server v{}'.format(__version__)
    )

    parser.add_argument(
        '-i', '--ip',
        help='IP to bind to. Default: 0.0.0.0',
        default='0.0.0.0'
    )
    parser.add_argument(
        '-p', '--port',
        help='Port to listen to. Default: 8080.',
        default=8080,
        type=int
    )
    parser.add_argument(
        '-f', '--force-port',
        help='Force the use of the given port.',
        action='store_true'
    )
    parser.add_argument(
        '-r', '--range',
        help='Number of ports to try from the base port',
        default=20,
        type=int
    )
    parser.add_argument(
        '-u', '--future',
        help='Ignore if the folder to server doesn\'t exists yet',
        action='store_true'
    )

    parser.add_argument(
        'path',
        help='Path to serve. Default: Current directory.',
        nargs='?',
        default=getcwd()
    )

    args = parser.parse_args(argv)

    # Setup and verify arguments
    level = V_LEVELS.get(args.verbose, logging.DEBUG)
    logging.basicConfig(format=FORMAT, level=level)
    log.debug('Raw arguments:\n{}'.format(args))

    args.path = abspath(args.path)
    if not args.future and not isdir(args.path):
        log.error('Not a directory: {}'.format(args.path))
        exit(1)

    if args.force_port:
        args.range = 0

    return args


if __name__ == '__main__':

    # Parse arguments
    args = parse_args()

    # Change to directory
    if not args.future:
        chdir(args.path)

    # Create webserver
    HTTPRequestHandler.serving_path = args.path

    # Look for available port
    for retry, port in enumerate(range(args.port, args.port + args.range + 1)):
        try:
            httpd = HTTPServer((args.ip, port), HTTPRequestHandler)
            break
        except socketerror as e:
            if e.errno != EADDRINUSE:
                raise e
            log.debug('Retry #{} on port {}.'.format(retry, port))
    else:
        log.error(
            'Port(s) {} - {} already in use.'.format(
                args.port, args.port + args.range
            )
        )
        exit(1)

    sa = httpd.socket.getsockname()
    print('WebDev server at {ip}:{port} serving {path}'.format(
        ip=sa[0], port=sa[1], path=args.path
    ))
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print('Good bye!')
