// File: /workspace/github_cpp_makefile_star2_O3/Cristian-Castillo[P]Multithreaded-HTTP-Server/httpserver.cpp
// Function: main
int main(int argc, char **argv){

    /* Command Line Struct Data */
    struct command_line_inputs *cmd,get_cmd;
    cmd = &get_cmd;

    /* Port Number and Configurations */
    const char *portNumber;
    const char *host_address;

    int counter_left_off = 0;
    for(int j = 1; j< argc; j++){  
        if (strlen(argv[j]) >= 7 && strlen(argv[j]) <= 15){
            
            std::regex ea("(localhost|[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3}[.]{1}[0-9]{1,3})"); 
            bool match = regex_match(argv[j], ea);
            if (match == 1){
                host_address = argv[j];
                counter_left_off = j;
                break;
            }
            else{
                perror("Invalid <ip address> format\n");
                exit(EXIT_FAILURE);
            }
        }
    }
    if(counter_left_off == 0){
        perror("Invalid <ip address> or <ip address> not entered.\n");
        exit(EXIT_FAILURE);
    }
    portNumber = "80";
    for(int j = counter_left_off; j < argc; j++){    
        if (strlen(argv[j]) == 4){
            portNumber = argv[j];
            break;
        }
    }

    /*Get structs rdy */
    struct sockaddr_storage client_addr;
    struct addrinfo hints, *res;  
    socklen_t addr_size;
    int enable = 1; 
    int serverSocket,ret;
    
    /* Configuration host and port number */
    memset(&hints,0,sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    
    if((ret = getaddrinfo(host_address,portNumber,&hints,&res)) != 0){ f_void_getaddrinfo_error(ret,res);}

    /* Flag Parse CMD Line Args */
    f_getopt(argc, argv, &get_cmd);
   
    /* Create socket */
    serverSocket = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
    if(serverSocket < 0){ f_void_socket_error(serverSocket);}   

    /* Reuse socket addr */
    if((ret=setsockopt(serverSocket, SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(int))) < 0){
        f_void_setsocket_error(ret);
    }
    
    /* Bind socket */
    if((ret = bind(serverSocket,res->ai_addr,res->ai_addrlen)) < 0 ){ f_void_bind_error(ret); }

    /* Initialize client queues, mutexes and conditions */
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&client_in_queue, NULL);

    /* Listen to sock & backlog */
    if((ret = listen(serverSocket,BACKLOG)) == -1){ f_void_listen_error(ret);}

    /* Marshall the struct - get rdy to pack redundancy message */
    struct marshall *message = (struct marshall *)malloc(sizeof(marshall)); 
   
    if(cmd->Redundancy == 1){message->is_redundant = cmd->Redundancy;}
    if(cmd->Redundancy == 1){Redundency = 1;}

    /* Allocate Threads on the fly */
    int *threader = ((int*)malloc(sizeof(int)*cmd->N_threads));
    pthread_t *dispatcher_thread = ((pthread_t*)malloc(sizeof(pthread_t)*cmd->N_threads));

    /* Let the program breath */
    sleep(double(1.5));
    int clientSocket = 0;
    int counter = 0;
    int flagSignal;

    while(LIVE){

 ///////////////////////////////////////////////////////////////////////// Redundancy ///////////////////////////////////////////////////////////////////////////

        if(cmd->Redundancy == 1){
       
            
            while(LIVE){

                    clientSocket = accept(serverSocket,(struct sockaddr *)&client_addr, &addr_size);
                    create_worker(dispatcher_thread,threader,&get_cmd);
                  
                    /* Put that client to the queue, waiting for worker threads to handle it */
                    pthread_mutex_lock(&mutex);
                    {
                        flagSignal = 0;
                        /* Workers are waiting -> Send Signal */
                        if(items.empty())
                            flagSignal = 1;
                        
                        items.push(clientSocket);

                        if(flagSignal)
                            pthread_cond_broadcast(&client_in_queue);
                    }
                    pthread_mutex_unlock(&mutex);
                    continue;
            }
        }

///////////////////////////////////////////////////////////////////////// Multithreading ///////////////////////////////////////////////////////////////////////////
       
        clientSocket = accept(serverSocket,(struct sockaddr *)&client_addr, &addr_size);
        connections.push_back(clientSocket);
        create_worker(dispatcher_thread,threader,&get_cmd);


        pthread_mutex_lock(&mutex);
        {
            flagSignal = 0;
            /* Workers are waiting -> Send Signal */
            if(items.empty())
                flagSignal = 1;
            
            items.push(connections[counter]);

            if(flagSignal)
                pthread_cond_broadcast(&client_in_queue);
        }
        pthread_mutex_unlock(&mutex); 
        counter++;          
    }       

    freeaddrinfo(res);
    exit(EXIT_SUCCESS);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xf8, %rsp
cmpl $0x2, %edi
jl 0x64f7
movq %rsi, %rbx
movl %edi, %ebp
movl %edi, %r14d
movl $0x1, %r13d
movq (%rbx,%r13,8), %rdi
callq 0x4190
addq $-0x7, %rax
cmpq $0x8, %rax
jbe 0x650d
incq %r13
cmpq %r13, %r14
jne 0x64dc
leaq 0xf3c2(%rip), %rdi      # 0x158c0
callq 0x42e0
movl $0x1, %edi
callq 0x4420
leaq 0xf348(%rip), %rsi      # 0x1585c
leaq 0x40(%rsp), %r15
movq %r15, %rdi
movl $0x10, %edx
callq 0x68e4
movq (%rbx,%r13,8), %r12
movq %r12, %rdi
callq 0x4190
leaq (%rax,%r12), %rsi
xorps %xmm0, %xmm0
leaq 0x70(%rsp), %rdx
movaps %xmm0, 0x10(%rdx)
movaps %xmm0, (%rdx)
movq %r12, %rdi
movq %r15, %rcx
xorl %r8d, %r8d
callq 0x1207b
movl %eax, %r15d
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x6570
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x4360
testb %r15b, %r15b
je 0x685f
movq (%rbx,%r13,8), %r15
leaq 0x40(%rsp), %rdi
callq 0x694c
leaq 0xf365(%rip), %r12      # 0x158f3
cmpl %ebp, %r13d
jge 0x65b3
movq %r12, %rbp
movq (%rbx,%r13,8), %r12
movq %r12, %rdi
callq 0x4190
cmpq $0x4, %rax
je 0x65b3
incq %r13
cmpq %r13, %r14
jne 0x6596
movq %rbp, %r12
movl $0x1, 0x18(%rsp)
xorps %xmm0, %xmm0
leaq 0x40(%rsp), %rdx
movaps %xmm0, (%rdx)
movaps %xmm0, 0x10(%rdx)
movaps %xmm0, 0x20(%rdx)
movabsq $0x100000002, %rax      # imm = 0x100000002
movq %rax, 0x4(%rdx)
movl $0x6, 0xc(%rdx)
leaq 0x10(%rsp), %rcx
movq %r15, %rdi
movq %r12, %rsi
callq 0x4450
testl %eax, %eax
jne 0x686b
leaq 0x28(%rsp), %rdx
movl %r14d, %edi
movq %rbx, %rsi
callq 0x4abc
movq 0x10(%rsp), %rax
movl 0x4(%rax), %edi
movl 0x8(%rax), %esi
movl 0xc(%rax), %edx
callq 0x40b0
movl %eax, %ebx
testl %eax, %eax
js 0x6877
leaq 0x18(%rsp), %rcx
movl %ebx, %edi
movl $0x1, %esi
movl $0x2, %edx
movl $0x4, %r8d
callq 0x4630
testl %eax, %eax
js 0x687e
movq 0x10(%rsp), %rax
movq 0x18(%rax), %rsi
movl 0x10(%rax), %edx
movl %ebx, %edi
callq 0x4060
testl %eax, %eax
js 0x6885
leaq 0x18132(%rip), %rdi     # 0x1e7a0
xorl %esi, %esi
callq 0x4540
leaq 0x1819c(%rip), %rdi     # 0x1e818
xorl %esi, %esi
callq 0x4410
movl %ebx, %edi
movl $0x1f4, %esi            # imm = 0x1F4
callq 0x43b0
cmpl $-0x1, %eax
je 0x688c
movl 0x2c(%rsp), %ebp
cmpl $0x1, %ebp
jne 0x66ab
movl $0x1, 0x18205(%rip)     # 0x1e8b0
movslq 0x28(%rsp), %r15
leaq (,%r15,4), %rdi
callq 0x44b0
movq %rax, 0x20(%rsp)
shlq $0x3, %r15
movq %r15, %rdi
callq 0x44b0
movq %rax, %r15
movl $0x1, %edi
callq 0x42a0
movl $0x0, 0xc(%rsp)
cmpl $0x1, %ebp
movl %ebx, %r14d
jne 0x678b
leaq 0x28(%rsp), %rbp
leaq 0x180a5(%rip), %r12     # 0x1e7a0
movl %ebx, %edi
leaq 0x70(%rsp), %rsi
leaq 0x1c(%rsp), %rdx
callq 0x40f0
movl %eax, 0xc(%rsp)
movq %r15, %rdi
movq 0x20(%rsp), %rsi
movq %rbp, %rdx
callq 0x6023
movq %r12, %rdi
callq 0x4530
movq 0x18149(%rip), %rbx     # 0x1e878
movq 0x18122(%rip), %r13     # 0x1e858
movq 0x1814b(%rip), %rax     # 0x1e888
addq $-0x4, %rax
cmpq %rax, %rbx
je 0x6759
movl 0xc(%rsp), %eax
movl %eax, (%rbx)
leaq 0x4(%rbx), %rax
movq %rax, 0x18121(%rip)     # 0x1e878
jmp 0x676a
leaq 0x180e8(%rip), %rdi     # 0x1e848
leaq 0xc(%rsp), %rsi
callq 0x143ec
cmpq %r13, %rbx
jne 0x677b
leaq 0x180a2(%rip), %rdi     # 0x1e818
callq 0x4120
movq %r12, %rdi
callq 0x42b0
movl %r14d, %ebx
jmp 0x66fb
xorl %r13d, %r13d
leaq 0x1800b(%rip), %r12     # 0x1e7a0
movl %ebx, %edi
leaq 0x70(%rsp), %rsi
leaq 0x1c(%rsp), %rdx
callq 0x40f0
movl %eax, 0xc(%rsp)
movq 0x180ef(%rip), %rsi     # 0x1e8a0
cmpq 0x180f0(%rip), %rsi     # 0x1e8a8
je 0x67c9
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, 0x180d9(%rip)     # 0x1e8a0
jmp 0x67da
leaq 0x180c8(%rip), %rdi     # 0x1e898
leaq 0xc(%rsp), %rdx
callq 0x14600
movq %r15, %rdi
movq 0x20(%rsp), %rsi
leaq 0x28(%rsp), %rdx
callq 0x6023
movq %r12, %rdi
callq 0x4530
movq 0x1807d(%rip), %rbp     # 0x1e878
movq 0x18056(%rip), %rbx     # 0x1e858
movq 0x1808f(%rip), %rsi     # 0x1e898
addq %r13, %rsi
movq 0x18075(%rip), %rax     # 0x1e888
addq $-0x4, %rax
cmpq %rax, %rbp
je 0x682e
movl (%rsi), %eax
movl %eax, (%rbp)
leaq 0x4(%rbp), %rax
movq %rax, 0x1804c(%rip)     # 0x1e878
jmp 0x683a
leaq 0x18013(%rip), %rdi     # 0x1e848
callq 0x143ec
cmpq %rbx, %rbp
jne 0x684b
leaq 0x17fd2(%rip), %rdi     # 0x1e818
callq 0x4120
movq %r12, %rdi
callq 0x42b0
addq $0x4, %r13
movl %r14d, %ebx
jmp 0x6795
leaq 0xf03d(%rip), %rdi      # 0x158a3
jmp 0x64fe
movq 0x10(%rsp), %rsi
movl %eax, %edi
callq 0x503b
movl %ebx, %edi
callq 0x4d5f
movl %eax, %edi
callq 0x4e27
movl %eax, %edi
callq 0x4ea6
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x4f6e
movq %rax, %rbx
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x68b3
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x4360
leaq 0x40(%rsp), %rdi
callq 0x694c
movq %rbx, %rdi
callq 0x4610
nop