<?php

namespace Kicken\Chat;


class 
ChatServer extends StreamSocketServer {
    
/** @var array */
    
private $mMessages = [];
    
/** @var ChatUser[] */
    
private $mUsers = [];
    
/** @var callable[] */
    
private $mNewMessageListeners = [];

    public function 
getUsers(){
        return 
$this->mUsers;
    }

    public function 
getUserByUID($uid){
        return isset(
$this->mUsers[$uid])?$this->mUsers[$uid]:null;
    }

    public function 
cancelOnNewMessage(callable $cb){
        foreach (
$this->mNewMessageListeners as $k => $cb2){
            if (
$cb === $cb2){
                unset(
$this->mNewMessageListeners[$k]);
            }
        }
    }

    public function 
onNewMessage(callable $cb){
        
$this->mNewMessageListeners[] = $cb;
    }

    public function 
registerNick($nick){
        if (
strlen($nick) == 0){
            throw new \
InvalidArgumentException('Nickname cannot be blank'ERR_NO_NICK);
        }

        
$inuse null;
        foreach (
$this->mUsers as $u){
            if (
$u->getNick() === $nick){
                
$inuse $u;
            }
        }

        if (
$inuse && !$inuse->isActive()){
            return 
$inuse->getUid();
        } else if (
$inuse){
            throw new \
InvalidArgumentException('Nickname already in use'ERR_NICK_TAKEN);
        } else {
            
$try 0;
            do {
                
$uid uniqid('u');
            } while (isset(
$this->mUsers[$uid]) && ++$try 25);

            if (isset(
$this->mUsers[$uid])){
                throw new \
RuntimeException('Unable to generate new unique id'ERR_SYSTEM);
            }

            
$user = new ChatUser($uid$this);
            
$user->setNick($nick);
            
$this->mUsers[$uid] = $user;

            return 
$uid;
        }
    }

    private function 
generateNewMessageId($json){
        if (!isset(
$json->timestamp)){
            throw new \
InvalidArgumentException('No timestamp on message object'ERR_NO_TIMESTAMP);
        }

        if (!isset(
$this->mMessages[$json->timestamp])){
            
$this->mMessages[$json->timestamp] = [];
        }

        
$idx count($this->mMessages[$json->timestamp]);

        return 
sprintf("%d:%d"$json->timestamp$idx);
    }

    public function 
saveMessage($json){
        
$newId $this->generateNewMessageId($json);
        list(
$ts$idx) = explode(':'$newId2);

        
$json->messageId $newId;
        
$this->mMessages[$ts][$idx] = $json;

        
$this->fireOnNewMessage($newId);

        return 
$newId;
    }

    private function 
fireOnNewMessage($id){
        foreach (
$this->mNewMessageListeners as $cb){
            
$cb($id);
        }
    }

    public function 
getNewestMessageId(){
        
$last end($this->mMessages);
        
$last end($last);

        return 
$last->messageId;
    }

    public function 
getMessagesAfter($id){
        
$output = [];
        list(
$ts$idx) = explode(':'$id?:'0:0'2);

        
$newerTs array_filter(array_keys($this->mMessages), function($k) use ($ts){
            return 
$k >= $ts;
        });

        foreach (
$newerTs as $k){
            
$list $this->mMessages[$k];

            if (
$k == $ts && isset($list[$idx 1])){
                
$slice array_slice($list$idx 1);
                
$output array_merge($output$slice);
            } else if (
$k $ts){
                
$output array_merge($output$list);
            }
        }

        return 
$output;
    }
}