#!/usr/bin/env python3.6

import argparse
import sys
import json

from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn

import vyper

from vyper.parser.parser import parse_to_lll
from vyper import compiler, optimizer


parser = argparse.ArgumentParser(
    description='Serve Vyper compiler as an HTTP Service'
)
parser.add_argument('--version', action='version', version='{0}'.format(vyper.__version__))
parser.add_argument(
    '-b',
    help='Address to bind JSON server on, default: localhost:8000',
    default='localhost:8000',
    dest='bind_address'
)

args = parser.parse_args()


class VyperRequestHandler(BaseHTTPRequestHandler):

    def send_404(self):
        self.send_response(404)
        self.end_headers()
        return

    def do_GET(self):

        if self.path == '/':
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b'Vyper Compiler.\n')
        else:
            self.send_404()

        return

    def do_POST(self):

        if self.path == '/compile':
            content_len = int(self.headers.get('content-length'))
            post_body = self.rfile.read(content_len)
            data = json.loads(post_body)

            response, status_code = self._compile(data)

            self.send_response(status_code)
            self.send_header("Content-type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps(response).encode())

        else:
            self.send_404()

        return

    def _compile(self, data):
        code = data.get('code')
        if not code:
            return {'status': 'failed', 'message': 'No "code" key supplied'}, 400
        if not isinstance(code, str):
            return {'status': 'failed', 'message': '"code" must be a non-empty string'}, 400

        try:
            code = data['code']
            out_dict = {
                'abi': compiler.mk_full_signature(code),
                'bytecode': '0x' + compiler.compile(code).hex(),
                'ir': str(optimizer.optimize(parse_to_lll(code)))
            }
        except Exception as e:
            return {
                'status': 'failed', 'message': str(e)
            }, 500

        out_dict.update({'status': "success"})

        return out_dict, 200


class VyperHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""
    pass


def runserver(host='', port=8000):
    server_address = (host, int(port))
    httpd = VyperHTTPServer(server_address, VyperRequestHandler)
    print('Listening on http://{0}:{1}'.format(host, port))
    httpd.serve_forever()


if __name__ == '__main__':
    if ':' in args.bind_address:
        runserver(*args.bind_address.split(':'))
    else:
        print('Provide bind address in "{address}:{port}" format')
        sys.exit()
