#!/usr/bin/env python3
import http.server, os, re, socketserver, ssl

ROOT = os.environ.get('UGO_ROOT', '/opt/ugo')
PORT = int(os.environ.get('UGO_PORT', '8082'))
CERT = os.environ.get('UGO_CERT')
KEY = os.environ.get('UGO_KEY')


class H(http.server.SimpleHTTPRequestHandler):
    protocol_version = 'HTTP/1.1'

    def translate_path(self, path):
        p = path.split('?', 1)[0].split('#', 1)[0]
        p = re.sub(r'/+', '/', p)
        if p.startswith('/ugo/'):
            p = p[5:]
        elif p == '/ugo':
            p = '/'
        p = p.lstrip('/')
        fp = os.path.join(ROOT, p)
        if not os.path.exists(fp) and p.startswith('game-icons/'):
            fp = os.path.join(ROOT, 'game-icons', 'g1014.png')
        return fp

    def send_head(self):
        path = self.translate_path(self.path)
        if os.path.isdir(path):
            return super().send_head()
        try:
            f = open(path, 'rb')
        except OSError:
            self.send_error(404, 'File not found')
            return None
        fs = os.fstat(f.fileno())
        size = fs.st_size
        ctype = self.guess_type(path)
        rng = self.headers.get('Range')
        if rng and rng.startswith('bytes='):
            spec = rng[6:].split(',')[0].strip()
            a, _, b = spec.partition('-')
            start = None
            try:
                if a == '':
                    ln = int(b)
                    start = max(0, size - ln)
                    end = size - 1
                else:
                    start = int(a)
                    end = int(b) if b else size - 1
            except ValueError:
                start = None
            if start is not None and 0 <= start < size:
                end = min(end, size - 1)
                self.send_response(206)
                self.send_header('Content-Type', ctype)
                self.send_header('Accept-Ranges', 'bytes')
                self.send_header('Content-Range', 'bytes %d-%d/%d' % (start, end, size))
                self.send_header('Content-Length', str(end - start + 1))
                self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
                self.end_headers()
                if self.command != 'HEAD':
                    f.seek(start)
                    remaining = end - start + 1
                    while remaining > 0:
                        chunk = f.read(min(65536, remaining))
                        if not chunk:
                            break
                        self.wfile.write(chunk)
                        remaining -= len(chunk)
                f.close()
                return None
        self.send_response(200)
        self.send_header('Content-Type', ctype)
        self.send_header('Content-Length', str(size))
        self.send_header('Accept-Ranges', 'bytes')
        self.send_header('Last-Modified', self.date_time_string(fs.st_mtime))
        self.end_headers()
        return f

    def log_message(self, fmt, *args):
        pass


class S(socketserver.ThreadingTCPServer):
    allow_reuse_address = True
    daemon_threads = True


if __name__ == '__main__':
    httpd = S(('0.0.0.0', PORT), H)
    if CERT and KEY:
        ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ctx.load_cert_chain(CERT, KEY)
        httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
    print('serving', ROOT, 'on', PORT, 'tls' if (CERT and KEY) else 'plain', flush=True)
    httpd.serve_forever()
