module Marcos

import GreyDecision.GreyNumbers: GreyNumber
import GreyDecision.Utility: unitize, weightize

function marcos(decisionMat::Array{GreyNumber{T}, 2}, weights::Array{Float64,1}, 
    fns::Array{Function,1}) where {T <: Real}

    row, col = size(decisionMat)

    w = unitize(weights)

    AAI = zeros(GreyNumber{T}, col)
    AI  = zeros(GreyNumber{T}, col)

    temp = [decisionMat; AI'; AAI']

    normalizedDecisionMat = similar(temp)

    @inbounds for i in 1:col
        if fns[i] == maximum
            AI[i] = maximum(decisionMat[:, i])
            temp[row + 1, i] = AI[i]
            AAI[i] = minimum(decisionMat[:, i])
            temp[row + 2, i] = AAI[i]
            normalizedDecisionMat[:, i] = map(x -> x / AI[i], temp[:, i])
        elseif fns[i] == minimum
            AI[i] = minimum(decisionMat[:, i])
            temp[row + 1, i] = AI[i]
            AAI[i] = maximum(decisionMat[:, i])
            temp[row + 2, i] = AAI[i]
            normalizedDecisionMat[:, i] = map(x -> AI[i] / x, temp[:, i])
        end
    end

    S  = zeros(GreyNumber{T}, col)

    for i in 1:(row + 2)
        S[i] = w .* normalizedDecisionMat[i, :] |> sum
    end

    KPlus  = map(x -> x / S[row + 1], S[1:row])
    KMinus = map(x -> x / S[row + 2], S[1:row])

    fKPlus = KPlus ./ (KPlus .+ KMinus)
    fKMinus = KMinus ./ (KPlus .+ KMinus)

    scores = zeros(GreyNumber{T}, row)

    for i in 1:row
        scores[i] = (KPlus[i] + KMinus[i]) / ((GreyNumber(1.0) + (GreyNumber(1.0) - fKPlus[i]) / fKPlus[i]) + ((GreyNumber(1.0) - fKMinus[i]) / fKMinus[i]))
    end

    rankings = sortperm(scores)

    bestIndex = rankings |> last

    result = Dict(
        :scores => scores, 
        :rankings => rankings,
        :bestIndex => bestIndex
    )

    return result 
end

end # end of module 