import websocket
import threading
import urllib.request
import json
from time import sleep
from common import User

#websocket.enableTrace(True)

def get_server_url(url, port):
    return url + ":" + port

class MessageFactory:
    def get_state():
        message = { 'type': 'GET_STATE' }
        return json.dumps(message)

    def recv_client(user_name):
        message = { 'type' : 'RECV_CLIENT', 'name' : user_name }  
        return json.dumps(message)

    def update_user(user):
        message = { 'type' : 'UPDATE_USER', 'user' : user.toJson() }
        return json.dumps(message)

    def user_disconnect(name):
        message = { 'type' : 'USER_DISCONNECT', 'name' : name }
        return json.dumps(message)

def send_post(data, server_url, endpoint):
    req = urllib.request.Request("{}/{}".format(server_url, endpoint))
    if data != None:
        post_data = json.dumps(data).encode('utf-8')
        req.add_header('Content-Type', 'application/json;charset=utf-8')
        req.add_header('Content-Length', len(post_data))
        urllib.request.urlopen(req, post_data)
    else:
        urllib.reqest.urlopen(req)

class DataSender:
    def __init__(self, state, config):
        self.state = state
        self.server_url = "http://" + get_server_url(config['server'], config['port'])
        print(self.server_url)

    def update_user(self, user):
        send_post({ "user": user.__dict__ }, self.server_url, 'update') 
    
    def register_self(self, user):
        send_post(user.__dict__, self.server_url, 'register')
    
    def get_state(self):
        url = "{}/state".format(self.server_url)
        print(url)
        content = urllib.request.urlopen(url).read().decode("UTF-8")
        response = json.loads(content) 

        self.state.lock.acquire()
        for user in response:
            updated_user = User(user['name'], user['song'], user['artist'], user['playing'])
            self.state.add_or_modify_user(updated_user)
        self.state.lock.release()

class Heartbeat(threading.Thread):
    def __init__(self, config, user_name):
        self.__stop = False
        self.server_url = "http://" + get_server_url(config['server'], config['port'])
        self.user_name = user_name;
        self.heartbeat_frequency = config['heartbeat_frequency'];
        threading.Thread.__init__(self)

    def run(self):
        while True:
            if(self.__stop):
                return
            send_post({ 'user': self.user_name }, self.server_url, 'heartbeat')
            sleep(self.heartbeat_frequency)
            
class DataReceiver(threading.Thread):
    def __init__(self, state, config):
        self.state = state
        self.websocket_url = "ws://" + get_server_url(config['server'], config['port']) + "/websocket"
        print(self.websocket_url)
        self.ws = websocket.create_connection(self.websocket_url)
        
        self.ws.send(MessageFactory.recv_client(state.current_user_name))
        self.__stop = False

        threading.Thread.__init__(self)
    
    def stop(self):
        self.ws.close()
        self.__stop = True

    def run(self):
        while True:
            if(self.__stop):
                return
            message = self.ws.recv()
            print("message was: {}".format(message))
            mdict = json.loads(message)
            message_type = mdict['type']
        
            #KNOWN PROBLEM:  This model will miss messages due to lock contention
            if message_type == "UPDATE_USER":
                print("Received UPDATE_USER")
                user_dict = mdict['user']
                updated_user = User(user_dict['name'], user_dict['song'], user_dict['artist'], user_dict['playing'])
                self.state.lock.acquire()
                self.state.add_or_modify_user(updated_user)
                self.state.lock.release()
            
            elif message_type == "USER_DISCONNECT":
                print("Received USER_DISCONNECT")
                self.state.lock.acquire()
                self.state.remove_user(mdict['name'])
                self.state.lock.release()

            else:
                print("Received unrecognized message");
                self.state.lock.acquire()
                self.state.add_log_entry("Received unrecognized message: {}".format(message))
                self.state.lock.release()

if __name__ == '__main__':
    print("Creating connections")
    
    config_file = open("config.json")
    config = json.loads(config_file.read())

    class dummy_state:
        def __init__(self):
            self.user_name = "DUMMY"

    receiver = DataReceiver(dummy_state(), config)
    receiver.start()
    
    sender = DataSender(dummy_state(), config)
    
    while True:
        sender.send_message("Hello, world!")
        input("")
