# robi_comm.py
#
# contains functions to:
#   communicate via web server to receive commands and send status information
#   parse received commands to update parameters and trigger actions
#
#
# initial version 2-Jul-2022 by wolf2018
# 13-Sep22: removed code to test this module stand alone
# 18-Jan23: removed unused code
#
# Remarks:
# data is presented to functions using a dictionary
# several print commands are commented out, un-comment for debugging as needed
#
# version date see version_date after imports
#
import _thread
import picoweb
import ujson
import gc
import cfg
import utime
from walking_positions import calc_positions

# from sys import exit

version_date = "18-Jan-2023"

# assign pointers to the global objects
status_dict = cfg.status_dict
w_parameter0 = cfg.w_parameter0
w_parameter1 = cfg.w_parameter1


app = picoweb.WebApp(__name__)


@app.route("/")
def index(req, resp):
    gc.collect()
    # un-comment print statements below as needed for debugging requests

    # determine action based on method and content
    method = req.method

    # check if request has content
    # print("headers is: "+str(req.headers)+"\n")
    if b"Content-Length" in req.headers:
        content_lenght = int(req.headers[b"Content-Length"])
    else:
        content_lenght = 0

    # print("received "+method+" request\n")
    # print("req has: "+str(dir(req))+"\n")
    # print("path is: "+req.path)
    # print("qs is: "+req.qs)
    # print("type of header is: "+str(type(req.headers))+"\n")
    # print("content lenghth is: "+str(l)+"   type l is: "+str(type(l))+"\n")

    # determine action based on method of request
    if method == "GET":
        if content_lenght > 0:  # GET request with content
            yield from req.read_form_data()
            print("received data from "+method+" is: "+str(req.form))

            # action on GET request's data here
            # currently the action is to send status back

        # serve GET request
        robi_status = {}

        robi_status["w_status"] = cfg.status_dict["w_status"]
        robi_status["action"] = cfg.status_dict["action"]
        robi_status["battery"] = cfg.read_batt()
        robi_status["memory"] = gc.mem_free()

        response = ujson.dumps(robi_status)
        if cfg.debug > 1:
            print("response: "+response+" "+str(type(response)))
        yield from picoweb.start_response(resp, status="201")
        yield from resp.awrite(response)

    elif method == "POST":    # walking parameters received

        if content_lenght == 0:  # POST request without content

            # send "received POST request without data" html error
            yield from picoweb.http_error(resp, "400")

        # process request
        else:
            yield from req.read_form_data()

            # extract the request data from request form
            req_data = ujson.loads(list(req.form.keys())[0])
            if cfg.debug > 0:
                print("Received data: "+str(req_data)+"    req_data type is: " + str(type(req_data)))

            # store parameters
            data = ujson.loads(req_data)
            cfg.parameters["CoM_height"] = float(data["CoM"])
            cfg.parameters["m_step"] = float(data["m_step"])
            cfg.parameters["delay"] = int(float(data["delay"]))
            cfg.parameters["friction"] = float(data["friction"])
            if cfg.debug > 0:
                print("parameters loaded: " + str(data)+"\n")

            # serve POST request

            yield from picoweb.start_response(resp)
            yield from resp.awrite(req_data)

    elif method == "PUT":    # walking planning values received

        if content_lenght == 0:  # PUT request without content

            # send "received Post without data" html error
            yield from picoweb.http_error(resp, "400")

        else:    # process request
            yield from req.read_form_data()

            # extract the request data from request form
            req_data = ujson.loads(list(req.form.keys())[0])

            if cfg.debug > 0:
                print("Received data: "+str(req_data)+"    req_data type is: "+ str(type(req_data)))

            # store walking planning values
            data = ujson.loads(req_data)
            cfg.w_parameter0["target"] = float(data["target"])
            cfg.w_parameter0["step"] = float(data["step"])
            cfg.w_parameter0["leading"] = data["leading"]
            if cfg.debug > 0:
                print("walking plannning data loaded: " + str(data))
            yield from picoweb.start_response(resp)
            yield from resp.awrite(req_data)

    else:
        print("method received: "+method)
        if content_lenght > 0:  # clear data cache
            yield from req.read_form_data()
            print("received data from "+method+" is: "+str(req.form))

        # send "unsupported method" html error
        yield from picoweb.http_error(resp, "405")


@app.route("/actions")
def set_action(req, resp):
    gc.collect()
    method = req.method

    # check if request has content
    if b"Content-Length" in req.headers:
        content_lenght = int(req.headers[b"Content-Length"])
    else:
        content_lenght = 0

    # print("received "+method+" request\n")
    # print("req has: "+str(dir(req))+"\n")
    # print("path is: "+req.path)
    # print("qs is: "+req.qs)
    # print("type of header is: "+str(type(req.headers))+"\n")
    # print("content lenghth is: "+str(l)+"   type l is: "+str(type(l))+"\n")

    # determine action based on method of request
    if method == "POST":

        if content_lenght == 0:  # POST request without content

            # send "received Post without data" html error
            yield from picoweb.http_error(resp, "400")

        # process request
        else:
            yield from req.read_form_data()

            # extract the request data from request form

            _req_rec = ujson.loads(list(req.form.keys())[0])
            req_data = ujson.loads(_req_rec)

            if cfg.debug > 0:
                print("req_form is: "+str(req.form))
                print("Received data: "+str(req_data)+"    req_data type is: " + str(type(req_data)))

            # action on POST request

            if req_data["action"] == "init":
                if cfg.debug > 0:
                    print("\n... performing initialization...")
                    print("memory before thread:", gc.mem_free())

                if cfg.core2:
                    _thread.start_new_thread((calc_positions), ("init", cfg.w_parameter0))
                else:
                    calc_positions("init", cfg.w_parameter0)

            if req_data["action"] == "ready":
                if cfg.debug > 0:
                    print("\n... performing a ready action...")
                    print("memory before thread:", gc.mem_free())

                if cfg.core2:
                    _thread.start_new_thread((calc_positions), ("ready", cfg.w_parameter0))
                else:
                    calc_positions("ready", cfg.w_parameter0)

            if req_data["action"] == "walk":
                if cfg.debug > 0:
                    print("\n...robi_comm: action is walk...")
                    print(cfg.w_parameter0)
                    print(cfg.parameters, "\n\n")

                if cfg.debug > 1:
                    input("press ENTER to start test walk")

                if cfg.core2:
                    _thread.start_new_thread((calc_positions), ("walk", cfg.w_parameter0))
                    thread_id = _thread.get_ident()
                    if cfg.debug > 0:
                        print("thread id is: " + str(thread_id))
                else:
                    calc_positions("walk", cfg.w_parameter0)

            if req_data["action"] == "stop":
                if cfg.debug > 0:
                    print("\n... performing a stop action...")

                _thread.exit()

            if req_data["action"] == "stand":
                if cfg.debug > 0:
                    print("\n... performing a stand action...")

                if cfg.core2:
                    _thread.start_new_thread((calc_positions), ("init", cfg.w_parameter0))
                else:
                    calc_positions("init", cfg.w_parameter0)

            # serve POST request
            # echo data
            response = _req_rec

            utime.sleep(1)

            yield from picoweb.start_response(resp)
            yield from resp.awrite(response)

    else:
        print("method received: "+method)
        if content_lenght > 0:  # clear data cache
            yield from req.read_form_data()
            print("received data from "+method+" is: "+str(req.form))

        # send "unsupported method" error
        yield from picoweb.http_error(resp, "405")

# end of module
