// File: /workspace/github_cpp_makefile_star2_O1/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
movq %rsi, %rbx
movl %edi, %ebp
xorl %r15d, %r15d
cmpl $0x2, %edi
jl 0x65df
movl %ebp, %r14d
movl $0x1, %r12d
movq (%rbx,%r12,8), %rdi
callq 0x4190
addq $-0x7, %rax
cmpq $0x8, %rax
jbe 0x65e4
incq %r12
cmpq %r12, %r14
jne 0x65c4
jmp 0x6666
leaq 0xf271(%rip), %rsi      # 0x1585c
leaq 0x40(%rsp), %r14
movq %r14, %rdi
movl $0x10, %edx
callq 0x69fa
movq (%rbx,%r12,8), %r15
movq %r15, %rdi
callq 0x4190
leaq (%rax,%r15), %rsi
xorps %xmm0, %xmm0
leaq 0x70(%rsp), %rdx
movaps %xmm0, 0x10(%rdx)
movaps %xmm0, (%rdx)
movq %r15, %rdi
movq %r14, %rcx
xorl %r8d, %r8d
callq 0x1216f
movl %eax, %r14d
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x6647
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x4360
testb %r14b, %r14b
je 0x6996
movq (%rbx,%r12,8), %rax
movq %rax, 0x8(%rsp)
leaq 0x40(%rsp), %rdi
callq 0x6a62
movl %r12d, %r15d
testl %r15d, %r15d
je 0x6961
leaq 0xf27d(%rip), %r12      # 0x158f3
cmpl %ebp, %r15d
jge 0x669b
movl %r15d, %r13d
movl %ebp, %r14d
movq (%rbx,%r13,8), %r15
movq %r15, %rdi
callq 0x4190
cmpq $0x4, %rax
je 0x669e
incq %r13
cmpq %r13, %r14
jne 0x6681
movq %r12, %r15
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 0x8(%rsp), %rdi
movq %r15, %rsi
callq 0x4450
testl %eax, %eax
jne 0x696a
leaq 0x28(%rsp), %rdx
movl %ebp, %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, %r14d
testl %eax, %eax
js 0x6976
leaq 0x18(%rsp), %rcx
movl %r14d, %edi
movl $0x1, %esi
movl $0x2, %edx
movl $0x4, %r8d
callq 0x4630
testl %eax, %eax
js 0x697e
movq 0x10(%rsp), %rax
movq 0x18(%rax), %rsi
movl 0x10(%rax), %edx
movl %r14d, %edi
callq 0x4060
testl %eax, %eax
js 0x6985
leaq 0x19043(%rip), %rdi     # 0x1f7a0
xorl %esi, %esi
callq 0x4540
leaq 0x190ad(%rip), %rdi     # 0x1f818
xorl %esi, %esi
callq 0x4410
movl %r14d, %edi
movl $0x1f4, %esi            # imm = 0x1F4
callq 0x43b0
cmpl $-0x1, %eax
je 0x698c
cmpl $0x1, 0x2c(%rsp)
jne 0x6799
movl $0x1, 0x19117(%rip)     # 0x1f8b0
movslq 0x28(%rsp), %rbx
leaq (,%rbx,4), %rdi
callq 0x44b0
movq %rax, 0x8(%rsp)
shlq $0x3, %rbx
movq %rbx, %rdi
callq 0x44b0
movq %rax, 0x20(%rsp)
movl $0x1, %edi
callq 0x42a0
movl $0x0, 0x4(%rsp)
cmpl $0x1, 0x2c(%rsp)
je 0x68bb
xorl %ebx, %ebx
leaq 0x70(%rsp), %r15
leaq 0x18fb4(%rip), %r12     # 0x1f7a0
movl %r14d, %edi
movq %r15, %rsi
leaq 0x1c(%rsp), %rdx
callq 0x40f0
movl %eax, 0x4(%rsp)
movq 0x19099(%rip), %rsi     # 0x1f8a0
cmpq 0x1909a(%rip), %rsi     # 0x1f8a8
je 0x681f
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, 0x19083(%rip)     # 0x1f8a0
jmp 0x6830
leaq 0x19072(%rip), %rdi     # 0x1f898
leaq 0x4(%rsp), %rdx
callq 0x14760
movq 0x20(%rsp), %rdi
movq 0x8(%rsp), %rsi
leaq 0x28(%rsp), %rdx
callq 0x6121
movq %r12, %rdi
callq 0x4530
movq 0x19025(%rip), %r13     # 0x1f878
movq 0x18ffe(%rip), %rbp     # 0x1f858
movq 0x19037(%rip), %rsi     # 0x1f898
addq %rbx, %rsi
movq 0x1901d(%rip), %rax     # 0x1f888
addq $-0x4, %rax
cmpq %rax, %r13
je 0x6887
movl (%rsi), %eax
movl %eax, (%r13)
leaq 0x4(%r13), %rax
movq %rax, 0x18ff3(%rip)     # 0x1f878
jmp 0x6893
leaq 0x18fba(%rip), %rdi     # 0x1f848
callq 0x1454c
cmpq %rbp, %r13
jne 0x68a4
leaq 0x18f79(%rip), %rdi     # 0x1f818
callq 0x4120
movq %r12, %rdi
callq 0x42b0
addq $0x4, %rbx
cmpl $0x1, 0x2c(%rsp)
jne 0x67ec
leaq 0x1c(%rsp), %r13
leaq 0x28(%rsp), %rbp
leaq 0x18ed4(%rip), %rbx     # 0x1f7a0
movl %r14d, %r12d
movl %r14d, %edi
leaq 0x70(%rsp), %rsi
movq %r13, %rdx
callq 0x40f0
movl %eax, 0x4(%rsp)
movq 0x20(%rsp), %rdi
movq 0x8(%rsp), %rsi
movq %rbp, %rdx
callq 0x6121
movq %rbx, %rdi
callq 0x4530
movq 0x18f74(%rip), %r14     # 0x1f878
movq 0x18f4d(%rip), %r15     # 0x1f858
movq 0x18f76(%rip), %rax     # 0x1f888
addq $-0x4, %rax
cmpq %rax, %r14
je 0x692f
movl 0x4(%rsp), %eax
movl %eax, (%r14)
leaq 0x4(%r14), %rax
movq %rax, 0x18f4b(%rip)     # 0x1f878
jmp 0x6940
leaq 0x18f12(%rip), %rdi     # 0x1f848
leaq 0x4(%rsp), %rsi
callq 0x1454c
cmpq %r15, %r14
jne 0x6951
leaq 0x18ecc(%rip), %rdi     # 0x1f818
callq 0x4120
movq %rbx, %rdi
callq 0x42b0
movl %r12d, %r14d
jmp 0x68cc
leaq 0xef58(%rip), %rdi      # 0x158c0
jmp 0x699d
movq 0x10(%rsp), %rsi
movl %eax, %edi
callq 0x503c
movl %r14d, %edi
callq 0x4d60
movl %eax, %edi
callq 0x4e28
movl %eax, %edi
callq 0x4ea7
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x4f6f
leaq 0xef06(%rip), %rdi      # 0x158a3
callq 0x42e0
movl $0x1, %edi
callq 0x4420
movq %rax, %rbx
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x69c9
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x4360
leaq 0x40(%rsp), %rdi
callq 0x6a62
movq %rbx, %rdi
callq 0x4610
nop