global _start

; constants
SYS_READ        equ 0
SYS_WRITE       equ 1
SYS_CLOSE       equ 3
SYS_POLL        equ 7
SYS_SENDFILE    equ 40
SYS_SOCKET      equ 41
SYS_ACCEPT      equ 43
SYS_SHUTDOWN    equ 48
SYS_BIND        equ 49
SYS_LISTEN      equ 50
SYS_EXIT        equ 60
SYS_FCNTL       equ 72
SYS_UNLINK      equ 87
STDOUT          equ 1
AF_UNIX         equ 1
SOCK_STREAM     equ 1
F_GETFL         equ 3
F_SETFL         equ 4
O_NONBLOCK      equ 2048
POLLIN          equ 1
POLLOUT         equ 4
POLLERR         equ 8
POLLNVAL        equ 32
; TODO(?) POLLPRI: see poll(2), tcp(7)
SHUT_RDWR       equ 2

;STATE_READING   equ 0
;STATE_WRITING   equ 1

; offsets into client struct
OFFSET_CLIENT_FD           equ 0
OFFSET_CLIENT_STATE        equ 4
OFFSET_CLIENT_BUFFER_LEN   equ 5
OFFSET_CLIENT_BUFFER       equ 6

pollfd_size         equ 4 + 2 + 2  ; $ man 2 poll
pollfds_capacity    equ 100

client_buffer_size  equ 255
; fd, state, buffer_len, buffer
client_size         equ 4 + 2 + client_buffer_size
clients_capacity    equ pollfds_capacity - 1

SECTION .data
server_path     db "server.sock", 0x00
server_path_len equ $ - server_path
sockaddr_size   equ 2 + 108  ; $ man 7 unix
goodbye         db "goodbye", 0x0a
goodbye_len     equ $ - goodbye
pollfds_len     dq 0
clients_len     dq 0

SECTION .bss
sockaddr        resb sockaddr_size
pollfds         resb pollfd_size * pollfds_capacity
clients         resb (4 + client_size) * clients_capacity

SECTION .text

%include "server.s"
%include "readline.s"
%include "clients.s"
%include "pollfds.s"
%include "client.s"

_start:
    call make_server
    mov rbx, rax  ; server fd
    mov rdi, rbx
    mov rsi, POLLIN
    call pollfds__append

poll:
    mov rax, SYS_POLL
    mov rdi, pollfds
    mov rsi, [pollfds_len]
    mov rdx, 5000
    syscall
    cmp rax, 0
    je poll
    jl exit  ; poll(2) returned error TODO

scan:
    mov r15, 0

; Variables:
; r15 - pollfds index
; r14w - poll(2) revents
; r13d - fd
; r12 - client memory address
scan__loop:
    cmp r15, [pollfds_len]
    jge poll
    mov r14w, [pollfds + r15 * pollfd_size + 6]
    cmp r14w, 0
    jne scan__found
    add r15, 1
    jmp scan__loop

scan__found:
    mov r13, 0
    mov r13d, [pollfds + r15 * pollfd_size]
    cmp r13d, ebx
    je scan__found__server

scan__found__client:
    mov rdi, r13
    call clients__search
    mov r12, rax
    cmp r12, 0
    jl _client__not_stored

    mov r10w, r14w
    and r10w, POLLERR | POLLNVAL
    cmp r10w, 0
    jne _client__error_or_eof

    mov r10w, r14w
    and r10w, POLLIN
    cmp r10w, 0
    jne _client__pollin

    mov r10w, r14w
    and r10w, POLLOUT
    cmp r10w, 0
    jne _client__pollout

    ; TODO what did poll(2) detect in this case?
    add r15, 1
    jmp scan__loop

_client__error_or_eof:
    mov rdi, r12
    call clients__remove

_client__not_stored:
    mov rdi, r13
    call client__shutdown_close
    mov rdi, r15
    call pollfds__remove
    jmp scan__loop

_client__pollin:
    mov rdi, r13
    mov rsi, r12
    mov rdx, r15
    call client__pollin
    cmp rax, 0
    jle _client__error_or_eof
    add r15, rax
    jmp scan__loop

_client__pollout:
    mov rdi, r13
    mov rsi, r12
    mov rdx, r15
    call client__pollout
    add r15, 1
    jmp scan__loop

scan__found__server:
    cmp r14w, POLLIN
    jne exit

    mov rax, SYS_ACCEPT
    mov rdi, r13
    mov rsi, 0
    mov rdx, 0
    syscall
    cmp rax, 0
    jl exit  ; accept(2) returned error TODO

    mov rdi, rax
    push rdi
    mov si, POLLIN
    call pollfds__append

    pop rdi
    call clients__append

    add r15, 1
    jmp scan__loop

return:
    ret

exit:
    mov rax, SYS_EXIT
    mov rdi, 255
    syscall