// File: 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
movq %rsp, %rbp
subq $0x180, %rsp            # imm = 0x180
movl $0x0, -0x4(%rbp)
movl %edi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
leaq -0x30(%rbp), %rax
movq %rax, -0x18(%rbp)
movl $0x0, -0x44(%rbp)
movl $0x1, -0x48(%rbp)
movl -0x48(%rbp), %eax
cmpl -0x8(%rbp), %eax
jge 0x878a
movq -0x10(%rbp), %rax
movslq -0x48(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
callq 0x51f0
cmpq $0x7, %rax
jb 0x877a
movq -0x10(%rbp), %rax
movslq -0x48(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
callq 0x51f0
cmpq $0xf, %rax
ja 0x877a
leaq 0x2d23a(%rip), %rsi     # 0x35912
leaq -0x68(%rbp), %rdi
movq %rdi, -0x178(%rbp)
movl $0x10, %edx
callq 0x8c60
movq -0x178(%rbp), %rsi
movq -0x10(%rbp), %rax
movslq -0x48(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
xorl %edx, %edx
callq 0x8d00
movb %al, -0x16d(%rbp)
jmp 0x870f
movb -0x16d(%rbp), %al
andb $0x1, %al
movb %al, -0x69(%rbp)
movb -0x69(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x8762
movq -0x10(%rbp), %rax
movslq -0x48(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movq %rax, -0x40(%rbp)
movl -0x48(%rbp), %eax
movl %eax, -0x44(%rbp)
leaq -0x68(%rbp), %rdi
callq 0x8d50
jmp 0x878a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
leaq -0x68(%rbp), %rdi
callq 0x8d50
jmp 0x8b78
leaq 0x2d1f0(%rip), %rdi     # 0x35959
callq 0x53f0
jmp 0x8770
movl $0x1, %edi
callq 0x5580
jmp 0x877c
movl -0x48(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x48(%rbp)
jmp 0x868f
cmpl $0x0, -0x44(%rbp)
jne 0x87a6
leaq 0x2d1df(%rip), %rdi     # 0x35976
callq 0x53f0
movl $0x1, %edi
callq 0x5580
leaq 0x2d1fc(%rip), %rax     # 0x359a9
movq %rax, -0x38(%rbp)
movl -0x44(%rbp), %eax
movl %eax, -0x80(%rbp)
movl -0x80(%rbp), %eax
cmpl -0x8(%rbp), %eax
jge 0x87f5
movq -0x10(%rbp), %rax
movslq -0x80(%rbp), %rcx
movq (%rax,%rcx,8), %rdi
callq 0x51f0
cmpq $0x4, %rax
jne 0x87e8
movq -0x10(%rbp), %rax
movslq -0x80(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movq %rax, -0x38(%rbp)
jmp 0x87f5
jmp 0x87ea
movl -0x80(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x80(%rbp)
jmp 0x87b7
movl $0x1, -0x140(%rbp)
leaq -0x130(%rbp), %rdi
xorl %esi, %esi
movl $0x30, %edx
callq 0x5260
movl $0x2, -0x12c(%rbp)
movl $0x1, -0x128(%rbp)
movl $0x6, -0x124(%rbp)
movq -0x40(%rbp), %rdi
movq -0x38(%rbp), %rsi
leaq -0x130(%rbp), %rdx
leaq -0x138(%rbp), %rcx
callq 0x55c0
movl %eax, -0x148(%rbp)
cmpl $0x0, %eax
je 0x8868
movl -0x148(%rbp), %edi
movq -0x138(%rbp), %rsi
callq 0x6520
movl -0x8(%rbp), %edi
movq -0x10(%rbp), %rsi
leaq -0x30(%rbp), %rdx
callq 0x5a60
movq -0x138(%rbp), %rax
movl 0x4(%rax), %edi
movq -0x138(%rbp), %rax
movl 0x8(%rax), %esi
movq -0x138(%rbp), %rax
movl 0xc(%rax), %edx
callq 0x50f0
movl %eax, -0x144(%rbp)
cmpl $0x0, -0x144(%rbp)
jge 0x88b5
movl -0x144(%rbp), %edi
callq 0x5f40
movl -0x144(%rbp), %edi
movl $0x1, %esi
movl $0x2, %edx
leaq -0x140(%rbp), %rcx
movl $0x4, %r8d
callq 0x5830
movl %eax, -0x148(%rbp)
cmpl $0x0, %eax
jge 0x88ed
movl -0x148(%rbp), %edi
callq 0x60c0
movl -0x144(%rbp), %edi
movq -0x138(%rbp), %rax
movq 0x18(%rax), %rsi
movq -0x138(%rbp), %rax
movl 0x10(%rax), %edx
callq 0x5080
movl %eax, -0x148(%rbp)
cmpl $0x0, %eax
jge 0x8923
movl -0x148(%rbp), %edi
callq 0x61b0
leaq 0x46f76(%rip), %rdi     # 0x4f8a0
xorl %eax, %eax
movl %eax, %esi
callq 0x56e0
leaq 0x46fde(%rip), %rdi     # 0x4f918
xorl %eax, %eax
movl %eax, %esi
callq 0x5570
movl -0x144(%rbp), %edi
movl $0x1f4, %esi            # imm = 0x1F4
callq 0x5520
movl %eax, -0x148(%rbp)
cmpl $-0x1, %eax
jne 0x8969
movl -0x148(%rbp), %edi
callq 0x6330
movl $0x10, %edi
callq 0x5640
movq %rax, -0x150(%rbp)
movq -0x18(%rbp), %rax
cmpl $0x1, 0x4(%rax)
jne 0x8995
movq -0x18(%rbp), %rax
movl 0x4(%rax), %ecx
movq -0x150(%rbp), %rax
movl %ecx, 0x8(%rax)
movq -0x18(%rbp), %rax
cmpl $0x1, 0x4(%rax)
jne 0x89a9
movl $0x1, 0x47007(%rip)     # 0x4f9b0
movq -0x18(%rbp), %rax
movslq (%rax), %rdi
shlq $0x2, %rdi
callq 0x5640
movq %rax, -0x158(%rbp)
movq -0x18(%rbp), %rax
movslq (%rax), %rdi
shlq $0x3, %rdi
callq 0x5640
movq %rax, -0x160(%rbp)
movl $0x1, %edi
callq 0x53a0
movl $0x0, -0x164(%rbp)
movl $0x0, -0x168(%rbp)
movq -0x18(%rbp), %rax
cmpl $0x1, 0x4(%rax)
jne 0x8aa6
jmp 0x8a05
movl -0x144(%rbp), %edi
leaq -0x100(%rbp), %rsi
leaq -0x13c(%rbp), %rdx
callq 0x5140
movl %eax, -0x164(%rbp)
movq -0x160(%rbp), %rdi
movq -0x158(%rbp), %rsi
leaq -0x30(%rbp), %rdx
callq 0x7ef0
leaq 0x46e5e(%rip), %rdi     # 0x4f8a0
callq 0x56d0
movl $0x0, -0x16c(%rbp)
leaq 0x46ef0(%rip), %rdi     # 0x4f948
callq 0x8da0
testb $0x1, %al
jne 0x8a63
jmp 0x8a6d
movl $0x1, -0x16c(%rbp)
leaq 0x46ed4(%rip), %rdi     # 0x4f948
leaq -0x164(%rbp), %rsi
callq 0x9000
cmpl $0x0, -0x16c(%rbp)
je 0x8a95
leaq 0x46e88(%rip), %rdi     # 0x4f918
callq 0x5160
leaq 0x46e04(%rip), %rdi     # 0x4f8a0
callq 0x53b0
jmp 0x8a05
movl -0x144(%rbp), %edi
leaq -0x100(%rbp), %rsi
leaq -0x13c(%rbp), %rdx
callq 0x5140
movl %eax, -0x164(%rbp)
leaq 0x46ecc(%rip), %rdi     # 0x4f998
leaq -0x164(%rbp), %rsi
callq 0x9030
movq -0x160(%rbp), %rdi
movq -0x158(%rbp), %rsi
leaq -0x30(%rbp), %rdx
callq 0x7ef0
leaq 0x46daa(%rip), %rdi     # 0x4f8a0
callq 0x56d0
movl $0x0, -0x16c(%rbp)
leaq 0x46e3c(%rip), %rdi     # 0x4f948
callq 0x8da0
testb $0x1, %al
jne 0x8b17
jmp 0x8b21
movl $0x1, -0x16c(%rbp)
movslq -0x168(%rbp), %rsi
leaq 0x46e69(%rip), %rdi     # 0x4f998
callq 0x90a0
movq %rax, %rsi
leaq 0x46e0a(%rip), %rdi     # 0x4f948
callq 0x9000
cmpl $0x0, -0x16c(%rbp)
je 0x8b58
leaq 0x46dc5(%rip), %rdi     # 0x4f918
callq 0x5160
leaq 0x46d41(%rip), %rdi     # 0x4f8a0
callq 0x53b0
movl -0x168(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x168(%rbp)
jmp 0x89f5
movq -0x78(%rbp), %rdi
callq 0x5800
nopw %cs:(%rax,%rax)
nopl (%rax,%rax)