########################################
# WFServer class
# Wifi Server
# Used to manage a WiFi server and receive commands from a remote client
# 
########################################
from time import sleep
from machine import Pin
import network
import socket

__APssid = None
__APpwd = None
__wlan = None
__serverSocket = None
__conn = None

# UDP_SERVER = "192.168.4.1"
SERVER_PORT = 23
CONN_TIMEOUT=30.0


DEBUG=True
LED = Pin(2, Pin.OUT)   # onboard LED is PIN 2

class WFServer:
    
    #####################################################################
    # Constructor for the server
    # 
    #####################################################################
    def __init__(self, ssid, pwd):
        if DEBUG:
            print("constructor "+str(ssid)+" / "+str(pwd))
            
        self.__APssid = ssid
        self.__APpwd = pwd
        #flash the LED
        for x in range(4):
            LED.value(False)
            sleep(0.3)
            LED.value(True)
        

    ########################################
    # Create & Activate the AccessPoint using parameters fro the constructor
    ########################################
    def createAccessPoint(self):
        
        LED.value(True)
        if DEBUG:
            print("Creating AP at "+str(self.__APssid)+" / "+str(self.__APpwd))
        
        # setup an access point
        ap_if = network.WLAN(network.AP_IF)
        ap_if.active(True)       # activate the interface
        ap_if.config(essid=self.__APssid, password=self.__APpwd)

        # wait until setup
        while ap_if.active() == False:
              pass
        if DEBUG:
            print('AP setup successfully' + str(ap_if.ifconfig()))
        
        LED.value(False )
        self.__wlan = ap_if

    ########################################
    # isAPConnected
    ########################################
    def isAPConnected(self):
        if self.__wlan is None:
            return False
        
        return self.__wlan.isconnected()
    
    ########################################
    # getIPAddress
    ########################################
    def getIPAddress(self):
        if self.__wlan is None:
            return None
        ifconfig = self.__wlan.ifconfig()
        
        return ifconfig[0]
    
    ########################################
    # Create a Socket
    ########################################
    def createServerSocket(self):
        # open a socket on the SERVER_PORT port
        addr = socket.getaddrinfo('0.0.0.0', SERVER_PORT)[0][-1]
        
        if DEBUG:
            print("Create Socket at:"+str(addr))

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        if DEBUG:
            print("Got socket, binding:"+str(s))
        s.bind(addr)
        if DEBUG:
            print("Listening:"+str(s))
        s.listen(1)
        
        if DEBUG:
            print("Exit createSocket:"+str(s))
            
        self.__serversocket = s

    ########################################
    # Wait for connection
    ########################################
    def waitForConnection(self):
        if self.__serversocket is None:
            self.__conn = None
            raise Exception("__serversocket not set")

        if DEBUG:
            print('Accept connection on '+str(self.__serversocket))
        try:
            self.__serversocket.settimeout(CONN_TIMEOUT)
            conn,addr = self.__serversocket.accept()
            if DEBUG:
                print("client connected from %s" % str(addr)) 
            self.__conn = conn
        except Exception as e:
            self.__conn = None
            text = str(e)
            print("waitForConnection Exception: "+text)

    ########################################
    # Do we have a connection ?
    ########################################
    def hasConnection(self):
        if self.__conn is None:
            return False
        
        return True

    ########################################
    # close connection
    ########################################
    def closeConnection(self):
        try:
            if self.__conn is not None:
                self.__conn.close()
            
            self.__conn = None
        except Exception as e:
            text = str(e)
            print("closeConnection Exception: "+text)

    ########################################
    # Get a command from the connection - expect a single character
    # There is a timeout on the command will throw exception if we timeout
    ########################################
    def getCommand(self):
        
        if self.__conn is None:
            raise Exception("No connection")

        command = None 
        self.__conn.setblocking(True)
        self.__conn.settimeout(CONN_TIMEOUT)
        if DEBUG:
            print('wait for content on %s' % str(self.__conn))

        msgbytes = self.__conn.recv(16)
        
        if msgbytes is not None:
            command = msgbytes.decode("utf-8")
            if DEBUG:
                print('command = %s' % command)
            
        return command


    ########################################
    # Get a Message from a Connection - waiting until we get the "endChar"
    ########################################
    def getMessage(self, endChar):
        #if DEBUG:
        #    print('Get command From '+str(self.__conn))
        msg = ""
        
        try:
            # Loop until we get a newline
            while True:
                # try to get 1 byte - but break if we dont get any
                data = self.__conn.recv(1)
                strData = data.decode("utf-8")
                if len(strData) < 1:
                    print("No bytes received")
                    break;
                    
                if strData[0]==endChar:
                    break
                sleep(0.1)
                
                msg = msg + strData
        except Exception as e:
            text = str(e)
            print("getMessage Exception: "+text)
            raise e

        return msg

