// RELATED SOFTWARE: Scilab 
// LICENSE   : CeCILL : http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
// FUNCTION  : orderfields()
// AUTHOR (C): Samuel GOUGEON - private individual
// COPYRIGHT : Samuel GOUGEON - 2015
// RELEASE   : 1.0
// DATE      : 2015-05-29
// DISTRIBUTION: http://fileexchange.scilab.org/toolboxes/349000
// HISTORY   :
//   1.0 - 2015-05-29: first publication on FileExchange
//
// DISCLAIMER: This script is provided as is, with NO WARRANTY of any kind.
//   Use it to your own risks & responsability.
//

function [So, k] = orderfields(S, pattern)
//
// Reorders primary fields of a (array of) structure according to a pattern
//
//CALLING SEQUENCES
//              orderfields         // displays this help
//              orderfields demo    // runs a demo in the console
//  [So, [k]] = orderfields(S)
//  [So, [k]] = orderfields(S, pattern)
//
//PARAMETERS
//  S : structure or array of structures whose primary fields must be re-ordered
//  pattern: another structure with same fields as S (in the desired order),
//        or vector of fields names of S, in the desired order,
//        or cell of fields names of S, in the desired order,
//        or vector of ranks in S of fields names of the reordered structure.
//  So : re-ordered structure. The re-ordering is not applied to sub-structures.
//  k  : row vector providing the ranks in S of So fields names: the field #i 
//       in So in met as field #k(i) in S.
//
//DESCRIPTION
//  So = orderfields(S) reorders the primary fields of S in alphabetical order,
//  and returns the corresponding structure So copied from S.
//  So = orderfields(S, pattern) reorders the primary fields of S according to
//   the order 
//   - of the fields of pattern, if pattern is a structure similar to S.
//   - of the fields names listed in pattern, if pattern is a vector of strings
//      or a cell of strings: these strings must be the fields names of S, in
//      the desired order)
//   - specified by their ranks in S, pattern being a vector of ranks 
//      = any permutation of 1 : size(fieldnames(S),"*").
//
//EXAMPLES
//   a = struct("txt","hello", "real",%pi, "bool",%f, "pol",%z)
//   b = struct("real",%e, "txt","bonjour", "pol",(%s-1)^2, "bool",%t)
//   [r, i] = orderfields(a, b)
//   a
//   [r, i] = orderfields(a, ["bool" "real" "pol" "txt"])
//   a
//   c = makecell([1,4], "bool", "pol", "txt", "real")
//   [r, i] = orderfields(a, c)
//   a
//   [r, i] = orderfields(a, [4 3 2 1])
//

    fname = "orderfields"
    // HELP
    // ----
    if argn(2)==0 then  // help = display the heading comments
        head_comments(fname)
        So  = []
        ind = []
        return
    end
    
    // DEMO
    // ----
    if type(S(1))==10 & S(1)=="demo"
        old_mode = mode()
        clc
        mode(6)
        mprintf("a = struct(""txt"",""hello"", ""real"",%%pi, ""bool"",%%f, ""pol"",%%z)")
        a = struct("txt","hello","real",%pi,"bool",%f,"pol",%z)
        mprintf("b = struct(""real"",%%e, ""txt"",""bonjour"", ""pol"",(%%s-1)^2, ""bool"",%%t)")
        b = struct("real",%e,"txt","bonjour","pol",(%s-1)^2,"bool",%t)
        mprintf("[r, i] = orderfields(a, b)")
        [r, i] = orderfields(a, b)
        mprintf("a, [r, i] = orderfields(a, [""bool"" ""real"" ""pol"" ""txt""])")
        a=a, [r, i] = orderfields(a, ["bool" "real" "pol" "txt"])
        mprintf("a, c = makecell([1,4], ""bool"", ""pol"", ""txt"", ""real"")")
        a=a, c = makecell([1,4], "bool", "pol", "txt", "real")
        mprintf("a, [r, i] = orderfields(a, c)")
        a=a, [r, i] = orderfields(a, c)
        mprintf("a, [r, i] = orderfields(a, [4 3 2 1])")
        a=a, [r, i] = orderfields(a, [4 3 2 1])

        mode(old_mode)
        So = []
        return
    end
    
    // CHECKING PARAMETERS + PRE-PROCESSING
    // ------------------------------------
    if ~isdef("S","l") then
        msg = _("%s: Wrong type for argument %d: Struct expected.\n")
        error(msprintf(msg, fname, 1))
    end

    sSfields = gsort(fieldnames(S),"g","i")     // column
    nfields = size(sSfields, "*")
    if argn(2)==1 then
        // Alphabetical sorting of fields names
        flist = sSfields
    else
        // Structure with same fields in the desired order
        if typeof(pattern)=="st"
            flist = fieldnames(pattern)
            if size(flist,"*")~=nfields | or(sSfields~=gsort(flist,"g","i"))
                msg = _("%s: structures in arguments #%d and #%d must have the same fields")
                error(msprintf(msg, fname, 1, 2))
            end

        // Vector of fields names in the right order
        elseif type(pattern)==10
            flist = pattern(:)
            if size(flist,"*")~=nfields | or(sSfields~=gsort(flist,"g","i"))
                msg = _("%s: strings in argument #2 must be the field names of argument #1.\n")
                error(msprintf(msg, fname))
            end
            
        // Cell of fields names in the right order
        elseif typeof(pattern)=="ce"
            if ~iscellstr(pattern)
                msg = _("%s: Wrong type for argument #%d: Cell of #%d strings expected.\n")
                error(msprintf(msg,fname,2, nfields))
            end
            flist = string(pattern)
            flist = strsubst(flist(:), """", "")
            if size(flist,"*")~=nfields | or(sSfields~=gsort(flist,"g","i"))
                msg = _("%s: strings in argument #2 must be the field names of argument #1.\n")
                error(msprintf(msg, fname))
            end
            
        // Vector of wished reordered indices
        elseif type(pattern)==1
            if or(gsort(pattern,"g","i")~=(1:nfields))
                msg = _("%s: argument #%d: a permutation of [1,..,%d] expected.\n")
                error(msprintf(msg, fname, 2, nfields))
            end
            flist = fieldnames(S)
            flist = flist(pattern)
        else
            msg = _("%s: Wrong type for argument #2: similar structure, vector of fields name, cell of strings, or permutation of [1,..,%d] expected.\n")
            error(msprintf(msg, fname, nfields))
        end
    end

    // RE-ORDERING FIELDS
    // ------------------
    So = struct()
    for i = 1:nfields
        So(flist(i)) = S(flist(i))
    end
    // Former indices of fields in S
    if argn(1)>1 then
        [tmp, k] = members(flist, fieldnames(S))
        k = k(:)'
    end

endfunction
