// File: /workspace/github_cpp_makefile_star2_O2/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 $0xe8, %rsp
movq %rsi, %rbx
movl %edi, %ebp
movslq %edi, %r12
pushq $0x1
popq %r13
cmpq %r12, %r13
jge 0x63be
movq (%rbx,%r13,8), %rdi
callq 0x4190
addq $-0x7, %rax
cmpq $0x8, %rax
jbe 0x6126
incq %r13
jmp 0x6105
leaq 0xd72f(%rip), %rsi      # 0x1385c
leaq 0x30(%rsp), %r14
pushq $0x10
popq %rdx
movq %r14, %rdi
callq 0x6442
movq (%rbx,%r13,8), %rdi
movq %r14, %rsi
xorl %edx, %edx
callq 0x64a8
testb %al, %al
je 0x63d2
movq (%rbx,%r13,8), %r14
leaq 0x30(%rsp), %rdi
callq 0x64d0
cmpq %r12, %r13
jge 0x617d
movq (%rbx,%r13,8), %r15
movq %r15, %rdi
callq 0x4190
incq %r13
cmpq $0x4, %rax
jne 0x6161
jmp 0x6184
leaq 0xd76f(%rip), %r15      # 0x138f3
movl $0x1, 0x10(%rsp)
xorps %xmm0, %xmm0
leaq 0xb0(%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 0x8(%rsp), %rcx
movq %r14, %rdi
movq %r15, %rsi
callq 0x4480
testl %eax, %eax
jne 0x63db
leaq 0x18(%rsp), %rdx
movl %ebp, %edi
movq %rbx, %rsi
callq 0x487c
movq 0x8(%rsp), %rax
movl 0x4(%rax), %edi
movl 0x8(%rax), %esi
movl 0xc(%rax), %edx
callq 0x40c0
movl %eax, %ebx
testl %eax, %eax
js 0x63e7
pushq $0x1
popq %rsi
pushq $0x2
popq %rdx
leaq 0x10(%rsp), %rcx
pushq $0x4
popq %r8
movl %ebx, %edi
callq 0x4670
testl %eax, %eax
js 0x63ee
movq 0x8(%rsp), %rax
movq 0x18(%rax), %rsi
movl 0x10(%rax), %edx
movl %ebx, %edi
callq 0x4060
testl %eax, %eax
js 0x63f5
leaq 0x19575(%rip), %rdi     # 0x1f7b0
xorl %esi, %esi
callq 0x4580
leaq 0x195df(%rip), %rdi     # 0x1f828
xorl %esi, %esi
callq 0x4440
movl %ebx, %edi
movl $0x1f4, %esi            # imm = 0x1F4
callq 0x43f0
cmpl $-0x1, %eax
je 0x63fc
movl 0x1c(%rsp), %ebp
cmpl $0x1, %ebp
jne 0x6278
movl $0x1, 0x19648(%rip)     # 0x1f8c0
movslq 0x18(%rsp), %r15
leaq (,%r15,4), %rdi
callq 0x44f0
movq %rax, %r14
shlq $0x3, %r15
movq %r15, %rdi
callq 0x44f0
movq %rax, %r15
pushq $0x1
popq %rdi
callq 0x42d0
andl $0x0, (%rsp)
cmpl $0x1, %ebp
movl %ebx, 0x4(%rsp)
jne 0x6323
leaq 0x30(%rsp), %rbp
leaq 0x194f3(%rip), %r12     # 0x1f7b0
movl %ebx, %edi
movq %rbp, %rsi
leaq 0x14(%rsp), %rdx
callq 0x4100
movl %eax, (%rsp)
movq %r15, %rdi
movq %r14, %rsi
leaq 0x18(%rsp), %rdx
callq 0x5d46
movq %r12, %rdi
callq 0x4570
movq 0x1959a(%rip), %rbx     # 0x1f888
movq 0x19573(%rip), %r13     # 0x1f868
leaq 0x1955c(%rip), %rdi     # 0x1f858
movq %rsp, %rsi
callq 0x12608
cmpq %r13, %rbx
jne 0x6315
leaq 0x19518(%rip), %rdi     # 0x1f828
callq 0x4120
movq %r12, %rdi
callq 0x42e0
movl 0x4(%rsp), %ebx
jmp 0x62bd
xorl %r13d, %r13d
leaq 0x19483(%rip), %r12     # 0x1f7b0
movl %ebx, %edi
leaq 0x30(%rsp), %rsi
leaq 0x14(%rsp), %rdx
callq 0x4100
movl %eax, (%rsp)
leaq 0x19560(%rip), %rdi     # 0x1f8a8
movq %rsp, %rsi
callq 0x6526
movq %r15, %rbp
movq %r15, %rdi
movq %r14, %r15
movq %r14, %rsi
leaq 0x18(%rsp), %rdx
callq 0x5d46
movq %r12, %rdi
callq 0x4570
movq 0x19513(%rip), %r14     # 0x1f888
movq 0x194ec(%rip), %rbx     # 0x1f868
movq 0x19525(%rip), %rsi     # 0x1f8a8
addq %r13, %rsi
leaq 0x194cb(%rip), %rdi     # 0x1f858
callq 0x12608
cmpq %rbx, %r14
jne 0x63a3
leaq 0x1948a(%rip), %rdi     # 0x1f828
callq 0x4120
movq %r12, %rdi
callq 0x42e0
addq $0x4, %r13
movl 0x4(%rsp), %ebx
movq %r15, %r14
movq %rbp, %r15
jmp 0x632d
leaq 0xd4fb(%rip), %rdi      # 0x138c0
callq 0x4310
pushq $0x1
popq %rdi
callq 0x4450
leaq 0xd4ca(%rip), %rdi      # 0x138a3
jmp 0x63c5
movq 0x8(%rsp), %rsi
movl %eax, %edi
callq 0x4db9
movl %ebx, %edi
callq 0x4af4
movl %eax, %edi
callq 0x4bad
movl %eax, %edi
callq 0x4c26
pushq $-0x1
popq %rdi
callq 0x4cdf
movq %rax, %rbx
leaq 0x30(%rsp), %rdi
callq 0x64d0
movq %rbx, %rdi
callq 0x4650
nop