////////////////////////////////
// Functions: Command Line Parsing

MR4TH_SYMBOL MR4TH_READ_ONLY
CMDLN_Params cmdln__params_nil = {0};

#define cmdln_params_nil (CMDLN_Params*)(&cmdln__params_nil)

MR4TH_SYMBOL CMDLN*
cmdln_from_args(Arena *arena, String8List *args){
    CMDLN *cmdln = push_array(arena, CMDLN, 1);
    cmdln->raw = str8_list_copy(arena, args);
    
    String8Node *node = cmdln->raw.first;
    
    // first string is 'program'
    if (node != 0){
        cmdln->program = node->string;
        node = node->next;
    }
    
    // consume string nodes
    B32 forced_input = 0;
    for (;node != 0;){
        String8 string = str8_skip_chop_whitespace(node->string);
        node = node->next;
        
        // check if argument is a flag
        B32 is_flag = 0;
        if (!forced_input){
            is_flag = (string.size != 0 && string.str[0] == '-');
        }
        
        // parse flag
        if (is_flag){
            
            // long flag
            B32 is_long_flag = (string.size > 1 && string.str[1] == '-');
            if (is_long_flag){
                
                // end 'normal' mode
                B32 double_dash = (string.size == 2);
                if (double_dash){
                    forced_input = 1;
                }
                
                // parse long flag
                if (!double_dash){
                    String8 flag_whole = str8_skip(string, 2);
                    
                    // parameter delimter
                    U64 delim = flag_whole.size;
                    for (U8 *ptr = flag_whole.str, *opl = flag_whole.str + flag_whole.size;
                         ptr < opl; ptr += 1){
                        if (*ptr == '=' || *ptr == ':'){
                            delim = (U64)(ptr - flag_whole.str);
                            break;
                        }
                    }
                    
                    // split flag at delimiter
                    String8 flag_name = str8_prefix(flag_whole, delim);
                    String8 flag_param = str8_skip(flag_whole, delim + 1);
                    
                    // if have a param delimiter at end
                    // then use the next argument as the flag_param
                    if (delim == flag_whole.size - 1){
                        if (node != 0){
                            flag_param = node->string;
                            node = node->next;
                        }
                    }
                    
                    // parse parameters
                    CMDLN_Params *params = cmdln_params_from_string(arena, flag_param);
                    
                    // store flag node
                    {
                        CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
                        SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
                        cmdlnnode->string = flag_name;
                        cmdlnnode->params = params;
                        cmdln->flag_count += 1;
                    }
                }
            }
            
            // short flags
            if (!is_long_flag){
                String8 short_flags = str8_skip(string, 1);
                
                U8 *flag = short_flags.str;
                U8 *flagopl = short_flags.str + short_flags.size;
                for (; flag < flagopl; flag += 1){
                    U8 *flagptr = flag;
                    
                    // check for parameters
                    String8 flag_param = {0};
                    if (flag + 1 < flagopl &&
                        (flag[1] == '=' || flag[1] == ':')){
                        flag_param = str8_range(flag + 2, flagopl);
                        if (flag_param.size == 0){
                            if (node != 0){
                                flag_param = node->string;
                                node = node->next;
                            }
                        }
                        
                        // kill the flag loop after finding parameters
                        flag = flagopl;
                    }
                    
                    // parse parameters
                    CMDLN_Params *params = cmdln_params_from_string(arena, flag_param);
                    
                    // store flag node
                    {
                        CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
                        SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
                        cmdlnnode->string = CLiteral(String8){flagptr, 1};
                        cmdlnnode->params = params;
                        cmdln->flag_count += 1;
                    }
                }
            }
        }
        
        // parse input
        if (!is_flag){
            String8 input_string = string;
            
            // store input node
            {
                CMDLN_Node *cmdlnnode = push_array(arena, CMDLN_Node, 1);
                SLLQueuePush(cmdln->first, cmdln->last, cmdlnnode);
                cmdlnnode->string = input_string;
                cmdln->input_count += 1;
            }
        }
    }
    
    // pointer arrays
    cmdln->inputs = push_array(arena, CMDLN_Node*, cmdln->input_count);
    cmdln->flags  = push_array(arena, CMDLN_Node*, cmdln->flag_count);
    {
        CMDLN_Node **inputptr = cmdln->inputs;
        CMDLN_Node **flagptr  = cmdln->flags;
        for (CMDLN_Node *node = cmdln->first;
             node != 0;
             node = node->next){
            if (node->params == 0){
                *inputptr = node;
                inputptr += 1;
            }
            else{
                *flagptr = node;
                flagptr += 1;
            }
        }
    }
    
    return(cmdln);
}

MR4TH_SYMBOL CMDLN_Params*
cmdln_params_from_string(Arena *arena, String8 flag_param){
    CMDLN_Params *params = cmdln_params_nil;
    if (flag_param.size > 0){
        params = push_array(arena, CMDLN_Params, 1);
        params->raw = flag_param;
        params->list = str8_split(arena, flag_param, (U8*)",", 1);
    }
    return(params);
}

MR4TH_SYMBOL U64
cmdln_input_count(CMDLN *cmdln){
    return(cmdln->input_count);
}

MR4TH_SYMBOL String8
cmdln_input_from_idx(CMDLN *cmdln, U64 idx){
    String8 result = {0};
    if (idx < cmdln->input_count){
        result = cmdln->inputs[idx]->string;
    }
    return(result);
}

MR4TH_SYMBOL U64
cmdln_flag_count(CMDLN *cmdln){
    return(cmdln->flag_count);
}

MR4TH_SYMBOL CMDLN_Flag*
cmdln_flag_from_idx(CMDLN *cmdln, U64 idx){
    CMDLN_Flag *result = 0;
    if (idx < cmdln->flag_count){
        result = cmdln->flags[idx];
    }
    return(result);
}

MR4TH_SYMBOL CMDLN_Params*
cmdln_get_params(CMDLN *cmdln, String8 flagstr, char abbrev){
    CMDLN_Params *result = 0;
    for (CMDLN_Node *node = cmdln->first;
         node != 0;
         node = node->next){
        if (node->params != 0){
            if (str8_match(flagstr, node->string, 0) ||
                (node->string.size == 1 && node->string.str[0] == (U8)abbrev)){
                result = node->params;
                break;
            }
        }
    }
    return(result);
}

MR4TH_SYMBOL B32
cmdln_has_flag(CMDLN *cmdln, String8 flagstr, char abbrev){
    CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev);
    B32 result = (params != 0);
    return(result);
}

MR4TH_SYMBOL String8
cmdln_get_str8(CMDLN *cmdln, String8 flagstr, char abbrev){
    CMDLN_Params *params = cmdln_get_params(cmdln, flagstr, abbrev);
    String8 result = {0};
    if (params != 0){
        result = params->raw;
    }
    return(result);
}

MR4TH_SYMBOL S64
cmdln_get_s64(CMDLN *cmdln, String8 flagstr, char abbrev){
    String8 str = cmdln_get_str8(cmdln, flagstr, abbrev);
    S64 result = cmdln_s64_from_str8(str);
    return(result);
}

MR4TH_SYMBOL F64
cmdln_get_f64(CMDLN *cmdln, String8 flagstr, char abbrev){
    String8 str = cmdln_get_str8(cmdln, flagstr, abbrev);
    F64 result = cmdln_f64_from_str8(str);
    return(result);
}

MR4TH_SYMBOL S64
cmdln_s64_from_str8(String8 valstr){
    S64 result = s64_from_str8_c_syntax(valstr);
    return(result);
}

MR4TH_SYMBOL F64
cmdln_f64_from_str8(String8 valstr){
    F64 result = f64_from_str8(valstr);
    return(result);
}

MR4TH_SYMBOL void
cmdln_dump(Arena *arena, String8List *out, CMDLN *cmdln, U32 indent){
    // raw
    str8_list_pushf(arena, out, "%Nraw:\n", indent);
    for (String8Node *node = cmdln->raw.first;
         node != 0;
         node = node->next){
        str8_list_pushf(arena, out, "%N%S\n", indent + 1, node->string);
    }
    
    // program
    str8_list_pushf(arena, out, "%Nprogram: %S\n", indent, cmdln->program);
    
    // input nodes
    str8_list_pushf(arena, out, "%Nnodes:\n", indent);
    for (CMDLN_Node *cmdlnnode = cmdln->first;
         cmdlnnode != 0;
         cmdlnnode = cmdlnnode->next){
        if (cmdlnnode->params == 0){
            str8_list_pushf(arena, out, "%N[input] %S\n", indent + 1, cmdlnnode->string);
        }
        else{
            str8_list_pushf(arena, out, "%N[flag ] %S\n", indent + 1, cmdlnnode->string);
            for (String8Node *node = cmdlnnode->params->list.first;
                 node != 0;
                 node = node->next){
                str8_list_pushf(arena, out, "%N[param] %S\n", indent + 2, node->string);
            }
        }
    }
}
