#include "EStringList.h" // <-- see this file for license, etc.
#include <fstream>
#include "EStdStringTokenizer.h"

#include "rng.h"

using namespace std;

        ostream &
        operator<<(ostream &os, const EStringList &obj)
        {
                os << obj.toString();
                return os;
        }
        string &
        operator<<(string &os, const EStringList &obj)
        {
                os += obj.toString();
                return os;
        }

        EStringList &
        operator +=(EStringList &obj, const string &os)
        {
                obj.getList().push_back( os );
                return obj;
        }


        EStringList::EStringList()
        {
                return ;
        }

        EStringList::EStringList( const StringList &list ) : m_list( list )
        {
        }

        // EStringList::EStringList( const EStringList &elist ) : m_list( elist..getList() )
        // {
        // }

        // static 
        string 
        EStringList::join( const EStringList &strlist, const string &separator )
        {
                const_iterator it = strlist.begin();
                string result;
                while( it != strlist.end() )
                {
                        result += (*it);
                        ++it;
                        //if( result.empty() ) continue; // SOOOO arguable!
                        result += separator;
                }
                //    result.erase( result.find_last_of( separator ) );  // knock of trailing separator
                // does not work for multi-char separator because of the way find_last_of() works. :(
                result.erase( result.rfind( separator ) );

                //CERR << "join(): = ["<<result<<"]"<<endl;
                return result;
        }
        string 
        EStringList::join( const StringList &strlist, const string &separator )
        {
                EStringList e;
                e.setList( strlist );
                return EStringList::join( e, separator );
        }

        // split
        EStringList
        EStringList::split( const string & sequence, const string &separator )
        {
                EStringList result;
                if( sequence.empty() ) return result;

                //CERR <<"split( "<<sequence<<", "<<separator<<" )"<<endl;
                string lazykludge = sequence + separator;

                string::size_type pos = 0;
                string::size_type pos2 = lazykludge.find( separator );
                if( pos2 == string::npos ) {
                        result += lazykludge;
                        return result;
                }

                string slice;
                while( pos2 != string::npos )
                {
                        slice = lazykludge.substr( pos, pos2 - pos );
                        //CERR << "pos="<<pos<<", pos2="<<pos2<<", slice="<<slice<<endl;
                        //         if( ! slice.empty() )
                        //         {
                        result.getList().push_back( slice );
                        //         }
                        pos = pos2 + 1;
                        pos2 = lazykludge.find( separator, pos );
                        while( pos2 == pos && pos2 != string::npos ) /* foo::::bar */
                        {
                                pos++;
                                pos2 = lazykludge.find( separator, pos );
                        }
                }
                return result;
        }

        EStringList // static
        EStringList::tokenize( const std::string &seq, const std::string &separators )
        {
                EStringList result;
                EStdStringTokenizer toker;
                toker.tokenize( seq.c_str(), separators.c_str() );
                string token;
                while( toker.hasMoreTokens() )
                {
                        token = toker.nextToken();
                        //std::cout << "EStringList::tokenize(): token ["<<token<<"]"<<std::endl;
                        result += string( token );
                }
                return result;
        }


        EStringList::StringList &
        EStringList::getList() /* const - see the header */
        {
                return this->m_list;
        }

        void
        EStringList::setList( const StringList &list ) { this->m_list = list; }

        int
        EStringList::count() const
        {
                unsigned int c = 0;
                EStringList::const_iterator it = this->begin();
                EStringList::const_iterator end = this->end();
                while( it != end )
                {
                        ++c;
                        ++it;
                }
                return c;
        }

        string
        EStringList::toString( const string & separator, bool quoteSpecialTokens ) const
        {
                EStringList::const_iterator it = this->begin();
                string str;
                int c = 0;
                string tmpstr;
                static const string quoteworthy = " \t$\n"; // $ might expand to space-sep'd tokens.
                // shite: {,} break shell parsing: Foo.{cpp,h} != "Foo.{cpp,h}"
                while( it != this->end() )
                {
                        ++c;
                        if( quoteSpecialTokens )
                        {
                                tmpstr = (*it);
                                if( tmpstr.find_first_of( quoteworthy ) != string::npos )
                                {
                                        str += "\"" + tmpstr + "\"";
                                }
                                else str += tmpstr;
                        }
                        else
                        {
                                str += (*it);
                        }
                        str += separator;
                        ++it;
                }
                if( c ) str.erase( str.rfind( separator ) ); // knock off trailing separator.
                return str;
        }

        // static 
        EStringList *
        EStringList::loadFromFile( const std::string &fn )
        {
                std::ifstream file(fn.c_str());
                if (!file) return NULL;
                EStringList *list = new EStringList();
                if( ! list->load( fn ) )
                {
                        delete( list );
                        return NULL;
                }
                return list;
        }

        bool
        EStringList::load( const std::string &fn, bool skipComments, bool skipEmptyLines )
        {
                std::ifstream file(fn.c_str());
                if (!file) return false;
                string line;
                while (std::getline(file, line))
                {
                        if( skipEmptyLines && line.empty() ) continue;
                        if( skipComments && !line.empty() && ( line[0] == '#' || line[0] == ';' ) ) continue;
                        //CERR << "line="<<line << endl;
                        *this += line;
                }
                return true;
        }


        std::string
        EStringList::randomEntry( bool removeIt )
        {
                int count = this->count();
                if( 0 == count ) return std::string();
                // int rand = (int) random( 0, count+1 );
                static RNG rng;
                int rand = rng.select( 0, count );
                iterator it = this->begin();
                for( int i = 0; i < rand; i++ ) ++it;
                //CERR << "randomEntry() returning ["<<(*it)<<"]"<<endl;
                if( ! removeIt ) return (*it);
                std::string foo = (*it);
                this->m_list.erase( it );
                return foo;
        }

        string
        EStringList::shift(  unsigned int places )
        {
                if( places < 0 ) return string();
                unsigned int count = this->count();
                if( places > count ) return string();
                string arg;
                for( unsigned int i = 0; i < places; i++ )
                {
                        if( i == places-1 ) arg = *(m_list.begin());
                        this->m_list.erase( m_list.begin() );
                }
                return arg;
        }
