using LightGraphs: degree, vertices, rem_vertex!, AbstractGraph
using MetaGraphs
using DelimitedFiles
using DataFrames

"""
source: https://discourse.julialang.org/t/get-the-argument-names-of-an-function/32902/3
I modified this function to retrieve the keyword arguments.
"""
function getkwargs(f)
    ms = collect(methods(f))
    m = last(ms)
    argnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), m.slot_syms)
    isempty(argnames) && return argnames
    return argnames[m.nargs + 1:end]
end


function get_valid_temp_file(instance_name)
    index =  0
    file =  "$instance_name.temp"
    while ispath(file)
        file = "$(instance_name)_$(index).temp"
        index += 1
    end
    return file
end


function load_matpower(filename)
    instance_name = split(basename(filename), '.')[1]
    temp_filename = get_valid_temp_file(instance_name)
    touch(temp_filename)
    f = open(filename)
    out = open(temp_filename, "w")
    # removing all ';' at end of lines
    while !eof(f)
        line = readline(f)
        if length(line) > 0 && line[1] != '%' && line[1] != 'f'
            s = split(line, ";")
            println(out, s[1])
        end
    end
    close(f)
    close(out)
    data = DelimitedFiles.readdlm(temp_filename)
    rm(temp_filename)
    return data
end


function find_numarray(i_start, data)
    i_debut = i_start
    while !isa(data[i_debut, 1], Int)
        i_debut += 1
    end
    i_fin=i_debut
    while !isa(data[i_fin,1], SubString)
        i_fin += 1
    end
    i_debut, i_fin - 1
end


function checkfor(data, line_ind, name) 
    (data[line_ind, 1] == name) || error("Expected $name at line $line_ind got $(data[line_ind,1]) instead.")
end


function get_names_buses(data, nb_bus)
    index = 1
    while data[index, 1] != "mpc.bus_name"
        index += 1
    end
    index += 1
    names = [join(data[i, :]) for i in index:index + nb_bus - 1]
    return names
end


function convertkeytosymbol(dict)
    symboldict = Dict()
    for (key, val) in dict
        if typeof(val) <: Dict
            symboldict[Symbol(key)] = Dict(Symbol(subkey) => subval for (subkey, subval) in dict[key])
        else
            symboldict[Symbol(key)] = val
        end
    end
    return symboldict
end


function parsecolumns!(type::Type, df::DataFrame, exclude::Vector{String}=["solver.nb_iter", "solver.solving_time"])
    for (index, name) in enumerate(names(df))
        name in exclude && continue
        try
            values = tryparse.(type, df[!,name])
            if !(typeof(values) in [Array{Union{Nothing,type},1}, Array{Nothing,1}])
                df[!,name] = values
            end
        catch e
            continue
        end
    end
end


function split_index(length, nb_split)
    if nb_split > length
        return [i:i for i in 1:length]
    end
    quotient = div(length, nb_split)
    remainder = length % nb_split
    indexes = []
    extra = remainder == 0 ? 0 : 1
    offset = 0
    for i in 1:nb_split
        start = (i - 1) * (quotient + extra) + 1 + offset
        stop = i * (quotient + extra) + offset
        if i == remainder
            offset = remainder
            extra = 0
        end
        push!(indexes, [start, stop])
    end
    return indexes
end


function format_options(options)
    return Dict(string(key) => Dict(string(key2) => val2 for (key2, val2) in val)
           for (key, val) in options)
end


function get_param_merge(path)
    open(path, "r") do io
        params = split(strip(read(io, String)), ',')
        return parse.(Float64, params)
    end
end


function get_nb_constraint(path)
    return length(readlines(path)) - 1
end
