// str2list.h (C) 2011 adolfo@di-mare.com

/** \file  str2list.h
    \brief Funciones para crear listas y matrices a partir de hileras.

    \author Adolfo Di Mare <adolfo@di-mare.com>
    \date   2011
*/

#ifndef   str2list_h
#define   str2list_h   ///< Evita la inclusión múltiple

#include <list>    // std::list<>
#include <string>  // std::string
#include <cstdlib> // atoi() atol() atof()

/** Retorna \c true para todos los caracters considerados como
    espacios en blanco por \c str2list().
    - { <code> , () <> {} [] </code> } && \c isspace().
    - El punto \c char('.') no es considerado espacio en blanco
      ( pues forma parte de un número flotante: \c 1.0e22 ).
    - <code> false == iswhitespace_str2list( '.' ) </code>.

    \dontinclude test_str2list.cpp
    \skipline    test::iswhitespace()
    \until       }}
    \see         test_str2list::test_iswhitespace()
*/
template <class CHAR>
bool iswhitespace_str2list( CHAR ch ) {
    if ( ch==',' || isspace(ch) ) { return true; }

    if ( ch=='(' || ch==')' ) { return true; }
    if ( ch=='<' || ch=='>' ) { return true; }
    if ( ch=='{' || ch=='}' ) { return true; }
    if ( ch=='[' || ch==']' ) { return true; }
    return false;
//  NOTA: implementado con plantillas para evitar usar el archivo str2list.cpp
}

/** Almacena en \c "L<>" la lista de hileras construida en base a \c "str".
    - En \c "str" están los valores que serán almacenados en \c "L<>" separados
      por varios delimitadores.
    - Como delimitadores se usan los caracteres blancos y la coma.
    - Como delimitadores se usan los caracteres \c ispace() y \c ','.
    - El punto \c '.' no se interpreta como delimitador.
    - Los caracteres { <code> , () <> {} [] </code> } son interpretados como
      blancos y no verifica que estén balanceados o en parejas.
    - Ignora el paréntesis del principio y del final de \c "str".
    - Si al principio encuentra el caracter \c '|' procesa la hilera hasta que
      encuentra el siguiente caracter \c '|'.
    - No examina lo que está después de la barrita final \c '|'.
    - Siempre modifica el valor de \c "L<>".
    - Retorna la última posición de \c "str" que fue examinada.

    \dontinclude test_str2list.cpp
    \skipline    test::str2list::list()
    \until       }}
    \see         test_str2list::str2list_list()
*/
template <class STRING>
const char* str2list( std::list<STRING>& L , const char* str ) {
    L.clear();
    if ( str == 0) { return 0; }
    if (*str == 0) { return str; }
    while ( iswhitespace_str2list(*str) ) { ++str; } // skip leading blanks
    if (*str=='|') { ++str; } // skip first '|'

    std::string val; // next value to store into L
    const bool IN_WHITESPACE = false; const bool IN_STRING = !IN_WHITESPACE;
    bool state = IN_WHITESPACE;
    while ( (*str != 0) && (*str != '|') ) {
        if ( state == IN_WHITESPACE ) {
            if ( ! iswhitespace_str2list(*str) ) {
                state = IN_STRING;
                val = *str;
            }
        }
        else { // assert( state == IN_STRING );
            if ( iswhitespace_str2list(*str) ) {
                L.push_back( val );
                val.clear();
                state = IN_WHITESPACE;
            }
            else {
                val += *str;
            }
        }
        ++str;
    }
    if ( !val.empty() ) { L.push_back(val); }
    return str;
//  NOTA: implementado con plantillas para evitar usar el archivo str2list.cpp
}


/** Usa \c str2list(L,str) para retornar la lista de valores de \c "str".

    \dontinclude test_str2list.cpp
    \skipline    test::str2list::example()
    \until       }}
    \see         test_str2list::str2list_example()
*/
inline std::list<std::string> str2list( const char* str ) {
    std::list<std::string> L;
    str2list(L,str);
    return L;
}

/** Retorna la lista de valores construida en base a \c "str".
    Usa \c str2list() junto con \c ATON_wrap::ATON().
    \tparam N tipo de dato: { <code> int long double </code> }
    \tparam ATON_wrap Empaque \c ATON() alrededor del convertidor:
                 { <code> atoi() atol() atof() </code> }.
*/
template <class N, class ATON_wrap>
std::list<N> str2list_wrap( const char* str ) {
    std::list<std::string> L;
    std::list<N> RES;
    str2list(L,str);

    std::list< std::string >::const_iterator it;
    for ( it= L.begin(); it!=L.end(); ++it ) {
        RES.push_back( ATON_wrap::ATON(it->c_str()) );
    }
    return RES;
}

/// Empaque \c ATON() alrededor de la función \c atoi().
struct str2list_int_wrap {
    /// Retorna \c atoi(str).
    static int ATON( const char * str ) { return atoi(str); }
};

/// Empaque \c ATON() alrededor de la función \c atol().
struct str2list_long_wrap {
    /// Retorna \c atol(str).
    static long ATON( const char * str ) { return atol(str); }
};

/// Empaque \c ATON() alrededor de la función \c atof().
struct str2list_double_wrap {
    /// Retorna \c atof(str).
    static double ATON( const char * str ) { return atof(str); }
};

inline std::list<int> intlist( const char* str ) {
    return str2list_wrap<int, str2list_int_wrap>( str );
} ///< Sinónimo de \c makeList_int().

/** Retorna la lista de enteros \c (int) construida en base a \c "str".
    Usa \c str2list() junto con \c atoi().

    \dontinclude test_str2list.cpp
    \skipline    test::intlist()
    \until       }}
    \see         test_str2list::test_intlist()
*/
inline std::list<int> makeList_int( const char* str ) {
    return str2list_wrap<int, str2list_int_wrap>( str );
}


inline std::list<long> longlist( const char* str ) {
    return str2list_wrap<long, str2list_long_wrap>( str );
} ///< Sinónimo de \c makeList_long().

/** Retorna la lista de enteros \c (long) construida en base a \c "str".
    Usa \c str2list() junto con \c atol().

    \dontinclude test_str2list.cpp
    \skipline    test::makeList_long()
    \until       }}
    \see         test_str2list::test_makeList_long()
*/
inline std::list<long> makeList_long( const char* str ) {
    return str2list_wrap<long, str2list_long_wrap>( str );
}


/** Retorna la lista de flotantes construida en base a \c "str".
    Usa \c str2list() junto con \c atof().

    \dontinclude test_str2list.cpp
    \skipline    test::doublelist()
    \until       }}
    \see         test_str2list::test_doublelist()
*/
inline std::list<double> doublelist( const char* str ) {
    return str2list_wrap<double, str2list_double_wrap>( str );
}

std::list<char> charlist( const char* str ) {
    std::list<std::string> Lstr;
    std::list<char> Lchar;
    str2list(Lstr,str);

    std::list< std::string >::const_iterator it;
    for ( it= Lstr.begin(); it!=Lstr.end(); ++it ) {
        if  ( (*it)[0] == '\'' ) {
            if ( it->size() == 1 ) {
                Lchar.push_back( ' ' ); // blank
                ++it;
            }
            else {
                Lchar.push_back( (*it)[1] );
            }
        }
        else {
            Lchar.push_back( (*it)[0] );
        }
    }

    return Lchar;
} ///< Sinónimo de \c makeList_char().

/** Retorna la lista de caracteres \c (char) construida en base a \c "str".
    Usa \c str2list() pero se brinca las comillas simples \c (').
    - No necesita que cada letra venga entre comillas simples.
    - Si alguno de los valores tiene más de un caracter, usa el primero e ignora los demás.

    \dontinclude test_str2list.cpp
    \skipline    test::charlist()
    \until       }}
    \see         test_str2list::test_makeList_char()
*/
inline std::list<char> makeList_char( const char* str ) {
    return charlist( str );
}

/** Almacena en \c "M<<>>" la matriz de hileras construida en base a \c "str".
    - Cada renglón de la matriz debe estar entre caracteres \c '|'.
    - Para obtener cada renglón se usa \c str2list().
    - La matriz se retorna como una lista de listas de hileras.
    - Ignora sublistas nulas pues \c "M<<>>" nunca contiene sublistas nulas.
    - Siempre modifica el valor de \c "M<<>>".
    - Retorna la longitud de la sublista más grande de \c "M<<>>" (para
      calcular cuántas columnas tiene la matriz).

    \dontinclude test_str2list.cpp
    \skipline    test::str2matrix()
    \until       }}
    \see         test_str2list::test_str2matrix()
*/
template <class STRING>
size_t str2matrix( std::list< std::list<STRING> >& M , const char* str ) {
    M.clear();
    if ( str == 0) { return 0; }
    if (*str == 0) { return 0; }

    size_t mx = 0; // max sublist length
    M.push_back( std::list<STRING>() );
    while ( *str != 0 ) {
        typename std::list<STRING> & L = M.back(); // L: last list in M<<>>
        str = str2list( L , str );
        if ( *str!=0 ) { str++; }
        mx = ( L.size()<mx ? mx : L.size() ); // keep max
        if ( ! M.back().empty() ) {
            M.push_back( std::list<STRING>() );
        }
    }
    if ( M.back().empty() ) { M.pop_back(); }
    return mx;
//  NOTA: implementado con plantillas para evitar usar el archivo str2list.cpp
}

/** Almacena en \c MATRIX[][] la matriz construida en base a \c "str".
    Usa \c str2matrix() junto con \c ATON_wrap::ATON().
    \tparam N tipo de dato: { <code> int long double </code> }
    \tparam ATON_wrap Empaque \c ATON() alrededor del convertidor:
                 { <code> atoi() atol() atof() </code> }
    \tparam Nrows  Cantidad de filas de \c MATRIX[][].
    \tparam Ncols  Cantidad de columnas de \c MATRIX[][].
*/
template <class N, class ATON_wrap, unsigned Nrows, unsigned Ncols>
void matrix2list_wrap( N MATRIX[Nrows][Ncols] , const char* str ) {
    std::list< std::list<std::string> > Mlist;
    size_t NCOLS = str2matrix( Mlist , str ); // Mlist <== str
    size_t NROWS = Mlist.size();

    NROWS = ( NROWS<Nrows ? NROWS : Nrows ); // take min(NROWS,Nrows)
    NCOLS = ( NCOLS<Ncols ? NCOLS : Ncols ); // take min(NCOLS,Ncols)

    typename std::list< std::list<std::string> > :: const_iterator it;
    typename            std::list<std::string>   :: const_iterator jt;

    it = Mlist.begin();
    for ( size_t i=0; i<NROWS; ++i,++it ) {
        jt = it->begin();
        for ( size_t j=0; j<NCOLS; ++j ) {
            if ( jt==it->end() ) { MATRIX[i][j] = 0; }
            else {
                MATRIX[i][j] = ATON_wrap::ATON( jt->c_str() );
                jt++;
            }
        }
    }
}

/** Almacena en \c M[][] la matriz de enteros \c (int) construida en base a \c "str".
    Usa \c str2matrix() junto con \c atoi().

    \dontinclude test_str2list.cpp
    \skipline    test::intmatrix()
    \until       }}
    \see         test_str2list::test_intmatrix()
*/
template <unsigned Nrows, unsigned Ncols>
inline void intmatrix( int M[Nrows][Ncols] , const char* str ) {
    return matrix2list_wrap<int, str2list_int_wrap, Nrows, Ncols>( M , str );
}

/** Almacena en \c M[][] la matriz de enteros \c (long) construida en base a \c "str".
    Usa \c str2matrix() junto con \c atol().
    \dontinclude test_str2list.cpp
    \skipline    test::longmatrix()
    \until       }}
    \see         test_str2list::test_longmatrix()
*/
template <unsigned Nrows, unsigned Ncols>
inline void longmatrix( long M[Nrows][Ncols] , const char* str ) {
    return matrix2list_wrap<long, str2list_long_wrap, Nrows, Ncols>( M , str );
}

/** Almacena en \c M[][] la matriz de flotantes construida en base a \c "str".
    Usa \c str2matrix() junto con \c atof().
    \dontinclude test_str2list.cpp
    \skipline    test::doublematrix()
    \until       }}
    \see         test_str2list::test_doublematrix()
*/
template <unsigned Nrows, unsigned Ncols>
inline void doublematrix( double M[Nrows][Ncols] , const char* str ) {
    return matrix2list_wrap<double,str2list_double_wrap,Nrows,Ncols>( M,str );
}

#include <cstring> // memset()

/** Almacena en \c M[][] la matriz de caracteres construida en base a \c "str".
    Usa \c str2matrix() pero se brinca las comillas simples \c (').
    - No necesita que cada letra venga entre comillas simples.
    - Si alguno de los valores tiene más de un caracter, usa el primero e ignora los demás.

    \dontinclude test_str2list.cpp
    \skipline    test::charmatrix()
    \until       }}
    \see         test_str2list::test_charmatrix()
*/
template <unsigned Nrows, unsigned Ncols>
inline void charmatrix( char M[Nrows][Ncols] , const char* str ) {
    std::list< std::list<std::string> > Mstr;
    str2matrix(  Mstr, str );
    memset( M, ' ', sizeof(M) ); // llena la matriz de blancos

    unsigned i,j; // paralelos (i<->it) && (j<->jt)
    std::list< std::list<std::string> >::const_iterator it;
    std::list<std::string>::const_iterator jt;
    for ( i=0,it=Mstr.begin(); (i<Nrows && it!=Mstr.end()); ++i,++it ) {
        const std::list<std::string>& Mrenglon = (*it);
        for ( j=0,jt=Mrenglon.begin(); (j<Ncols && jt!=Mrenglon.end()); ++j,++jt ) {
            if  ( (*jt)[0] == '\'' ) {
                if ( jt->size() == 1 ) {
                    M[i][j] = ' '; // blank
                    ++jt;
                }
                else {
                    M[i][j] = (*jt)[1];
                }
            }
            else {
                M[i][j] = (*jt)[0];
            }
        }
    }
    return;
}

#endif // str2list_h

// EOF: str2list.h