##
#  Copyright : Copyright (c) MOSEK ApS, Denmark. All rights reserved.
#
#  File :      test_async.py
#
#  Purpose :   Demonstrates how to submit an optimization problem
#              to the MOSEK OptServer and solve it in asynchronous mode.
##

import http.client
import sys, time
try:
    import ssl
except:
    pass

# A debug method which prints out the HTTP(S) response 
# and exits in case of error
def check_status(resp):
    print('\tHTTPResponse: %s / %s' % (resp.status,resp.reason))
    for k,v in  resp.getheaders():
        print('\t%s: %s' % (k,v))
            
    if resp.status not in [http.client.OK, http.client.NO_CONTENT]:
        raise Exception('An error in connection')

# A helper method which opens a connection
def openConnection(host, port, useHttps):
    if useHttps:
        return http.client.HTTPSConnection(host, port, context=ssl._create_unverified_context())
    else:
        return http.client.HTTPConnection(host, port)

if __name__ == '__main__':
    try:
        # OptServer address
        host, port = sys.argv[1], int(sys.argv[2])
        # Protocol (HTTP/HTTPS)
        useHttps = (sys.argv[3] == "HTTPS")
        # Name of file with input data
        probfile = sys.argv[4]        
        # Input and output file type
        intype, outtype = sys.argv[5], sys.argv[6]
        # Number of solution polls
        maxPolls = int(sys.argv[7])
        # Jobname (for demonstration)
        jobname = sys.argv[8] 
        # Authentication token
        headers = {}
        if len(sys.argv) == 10:
            headers = {"X-Mosek-Access-Token": sys.argv[9]}
    except:
        print("Usage  : python3 test_async.py host            port  protocol  probfile intype outtype    maxPolls jobname     [accestoken]")
        print("Example: python3 test_async.py solve.mosek.com 38000 HTTPS     lo1.mps  mps    text/plain 5        SimpleTask  ...")
        sys.exit(1)

    token = ""
    
    # Create a connection for problem submission
    con = openConnection(host, port, useHttps)
    try:
        with open(probfile,'rb') as probdata:
            ## Submit job
            print('POST /api/submit')
            # POST problem data
            con.request('POST', '/api/submit?jobname=' + jobname, 
                                probdata, 
                                headers = dict(headers, **{"Content-Type": "application/x-mosek-{}".format(intype)}))
            resp = con.getresponse()
            check_status(resp)
            # Recover a token identifying the job
            token = resp.read().decode('ascii')      
            
            ## Start solving end close connection
            print("GET /api/solve-background")
            con.request("GET","/api/solve-background?token=" + token, headers = headers)
            resp = con.getresponse()
            check_status(resp)
    finally:
        con.close()
        print("Submit connection closed")

    # Begin waiting for the solution
    solved = False
    pollCount = 0
    logOffset = 0

    while not solved:
        con = openConnection(host, port, useHttps)
        pollCount += 1
        print("GET /api/solution")
        con.request("GET", "/api/solution?token=" + token, 
                           headers = dict(headers, **{"Accept": outtype}))
        resp = con.getresponse()
        check_status(resp)

        # Is the solution available?
        if resp.status == http.client.NO_CONTENT:
            print("Solution not available in poll %d, continuing" % pollCount)
            time.sleep(1.0)
        elif resp.status == http.client.OK:    
            solved = True
            res = resp.getheader('X-Mosek-Res-Code',None)
            trm = resp.getheader('X-Mosek-Trm-Code',None)
            print("\tMOSEK response: %s" % res)
            print("\t      trm resp: %s" % trm)
        
            print("Solution (as plain text):")
            print(resp.read().decode('ascii', errors = 'ignore'))
        con.close()

        # After too many tries we indicate the solver to stop
        if not solved and pollCount >= maxPolls:
            con = openConnection(host, port, useHttps)
            print("GET /api/break")
            con.request("GET","/api/break?token=" + token, headers = headers)
            resp = con.getresponse()
            check_status(resp)
            con.close()        

        # Demonstrate how to retrieve the last part of solver log
        con = openConnection(host, port, useHttps)
        print("GET /api/log")
        con.request("GET","/api/log?token={0}&offset={1}".format(token, logOffset), headers = headers)
        resp = con.getresponse()
        check_status(resp)
        # Show the latest log entries
        lastLog = resp.read().decode('ascii', errors = 'ignore')
        print(lastLog)
        # Update the log offset by the size received
        logOffset += len(lastLog)
        con.close()        

