/* $Header$
*/

#include "Nice/Chat/Line.h"

#include <cstdarg>

#ifndef _WIN32
#include <fnmatch.h>
#endif

#include <string>

using namespace std;


#undef DEBUG

#undef LOG_NAME
#define LOG_NAME name()

namespace Nice
{

    ChatLine::ChatLine () : ChatCore()
    {
        T_LOG ( "Constructing" );
        setStdSettings ();
    }

    ChatLine::ChatLine ( const char *device )
        : ChatCore ( device )
    {
        T_LOG ( "Constructing" );
        setStdSettings ();
    }


    ChatLine::ChatLine ( int fd ) : ChatCore ( fd )
    {
        T_LOG ( "Constructing" );
        setStdSettings ();
    }

    ChatLine::~ChatLine ()
    {
        D_LOG ( string ( "Chat line: Destructing " ) + m_device_name );
    }

    void ChatLine::setStdSettings ()
    {
        m_timeout = Nice::Time::seconds ( NICE_CHAT_TIMEOUT_IN_SECONDS );
        setLineEndSend ( "\r" );
        setLineEndRecv ( "\r\n" );
        m_persistent = true;
        m_minLine = 1;
    }

    int ChatLine::getc ( char *ch )
    {
        if ( m_readBuffer.length () > 0 )
        {
            *ch = m_readBuffer[0];
            T_LOG ( ( *ch > 30 ? *ch : '^' ) << " " << ( int ) *ch << "#" << m_readBuffer.length () );
            m_readBuffer.erase ( 0, 1 );
            return ( 1 );
        }

        int ret = wait ( m_timeout );
        ssize_t rc;

        if ( ret > 0 )
        {
            rc = read ( m_fd, m_readBlockBuffer, 1024 );
            if ( rc < 0 )
            {
                const int errnoCopy = errno;
                E_LOG ( errnoCopy );
                E_THROW ( ::Nice::ChatException, strerror ( errnoCopy ) );
            }
            else if (rc == 0)
            {
                E_THROW( ::Nice::ChatException,
                        std::string("Detected end of connection to ") +
                        m_device_name);
            }
            else if ( rc > 0 )
            {
                m_readBlockBuffer[rc] = 0;
                m_readBuffer.append ( m_readBlockBuffer );
                *ch = m_readBuffer[0];
                m_readBuffer.erase ( 0, 1 );
                T_LOG ( ( *ch > 30 ? *ch : '^' ) << " " << ( int ) *ch << " " << rc );
                return ( 1 );
            }
        }

        *ch = 0;
        return 0;
    }

    int ChatLine::sendline ( const std::string & _line )
    {
        ssize_t rc;
        std::string line;

        line = _line + m_lineEndSend;
        rc = write ( m_fd, line.c_str (), line.length () );
        if ( rc < 0 )
            E_THROW ( ::Nice::ChatException, strerror ( errno ) );

#ifndef _WIN32
        fsync ( m_fd );
#endif
        return ( int ) rc;
    }

    void ChatLine::timeoutAktion ( const std::string &msg )
    {
        if ( m_lineBuffer.size() )
        {
            E_LOG ( m_lineBuffer );
        }
//    E_LOG ( msg );
        E_THROW ( ::Nice::Chat::TimeOutException, msg );
    }


    int ChatLine::recvline ( std::string & _line )
    {
        int ret = 0;
        char ch;
        _line.clear ();

        while ( ( ret = getc ( &ch ) ) > 0 )
        {
            if ( ret > 0 )
            {
                if ( strchr ( m_lineEndRecv, ch ) )
                {
                    if ( m_lineBuffer.length() >= m_minLine )
                    {
                        _line = m_lineBuffer;
                        m_lineBuffer.clear ();
                        T_LOG ( "Line #" << m_lineBuffer.length() << " <" << _line << ">" );
                        return ( int ) _line.length ();
                    }
                    m_lineBuffer.clear ();
                }
                else
                {
                    m_lineBuffer += ch;
                }
            }
        }
        if ( ret == 0 )
        {
            ret = CONNECTION_TIMEOUT;
        }

        return ret;
    }

    int ChatLine::dumpdata ()
    {
        int ret = 0;
        char ch;
        while ( ( ret += getc ( &ch ) ) > 0 );
        return ret;
    }


    int ChatLine::tryReadlines ( const int _max )
    {
        std::string line_buffer;
        int rc = 0;
        int rcTotal = 0;
        int count = 0;
        std::ostringstream os;

        os << m_device_name << " ";

        m_srbuffer.clear ();
        while ( count != _max && ( rc = recvline ( line_buffer ) ) > 0 )
        {
            m_srbuffer.push_back ( line_buffer );
            os << "<" << line_buffer << ">";
            count++;
            rcTotal += rc;
        }
        if ( rcTotal > 0 )
        {
            D_LOG ( os.str() );
        }
        else
        {
            D_LOG ( "Nothing received." );
            rcTotal = rc;
        }
        int card, command, module;
        std::stringstream log_msg;
        std::vector<std::string>::const_iterator it;
        for(it = m_srbuffer.begin(); it < m_srbuffer.end(); it++)
        {
            int scanresult = sscanf(it->c_str(), "%d %d %d", &card, &command, &module);
            if(scanresult == 3 && (module == 5 || module == 6) && command != 171 && command != 166)
            {
                log_msg << *it << " ~  ";
                U7_LOG("Chat2 : " << " ~ " << log_msg.str());
            }
            U7_LOG("Chat2Bis : " << " ~ " << *it);
        }

        return rcTotal;
    }

    int ChatLine::chat ( int _num, const char *_cmd )
    {
        std::string line_buffer;
        int rc, i;
        std::ostringstream os;

        if ( _cmd && strlen ( _cmd ) > 0 )
            sendline ( _cmd );

        os << m_device_name << " recvline<" << _cmd << ">(" << _num << ")=";

        m_srbuffer.clear ();
        for ( i = 0; i < _num; i++ )
        {
            rc = recvline ( line_buffer );
            T_LOG ( "rc=" << rc );
            if ( rc == CONNECTION_TIMEOUT )
            {
                os << " - Read timeout";
                W_LOG ( os.str() );
                timeoutAktion ( "Read timeout" );
            }

            if ( rc < 0 )
            {
                os << " - " << strerror ( errno );
                E_LOG ( os.str() );
                E_THROW ( ::Nice::ChatException, strerror ( errno ) );
            }

            m_srbuffer.push_back ( line_buffer );
            os << "<" << line_buffer << ">";
        }
        int card, command, module;
        int scanresult = sscanf(_cmd, "%d %d %d", &card, &command, &module);
        if(scanresult == 3 && (module == 5 || module == 6) && command != 171 && command != 166)
        {
            std::stringstream log_msg;
            std::vector<std::string>::const_iterator it;
            for(it = m_srbuffer.begin(); it < m_srbuffer.end(); it++)
            {
                log_msg << *it << " ~  ";
            }
            U7_LOG("Chat3 : " << _cmd << " ~ " << log_msg.str());
        }

        D_LOG ( os.str() );
        return i;
    }

    int ChatLine::chat ( const char *_rsp, const int _max, const char *_cmd )
    {
        std::string line_buffer;
        int rc, i = 0;
#ifndef _WIN32
        std::ostringstream os;

        os << m_device_name << " <";
        if ( _cmd && strlen ( _cmd ) > 0 )
        {
            sendline ( _cmd );
            os << _cmd;
        }
        os << "> \"" << _rsp << "\" = ";

        if ( _rsp == NULL )
            return 0;
        m_srbuffer.clear ();
        do
        {
            rc = recvline ( line_buffer );
            T_LOG ( "rc=" << rc );
            if ( rc == CONNECTION_TIMEOUT )
            {
                os << " - Read timeout";
                W_LOG ( os.str() );
                timeoutAktion ( "Read timeout" );
            }

            if ( rc < 0 )
            {
                os << " - " << strerror ( errno );
                E_LOG ( os.str() );
                E_THROW ( ::Nice::ChatException, strerror ( errno ) );
            }

            m_srbuffer.push_back ( line_buffer );
            T_LOG ( line_buffer );
            os << "<" << line_buffer << ">";
            i++;
        }
        while ( fnmatch ( _rsp, line_buffer.c_str (), FNM_NOESCAPE ) == FNM_NOMATCH && i != _max );
        int card, command, module;
        int scanresult = sscanf(_cmd, "%d %d %d", &card, &command, &module);
        if(scanresult == 3 && (module == 5 || module == 6) && command != 171 && command != 166 && command != 162 && command != 163 && command != 225 && command != 168)
        {
            std::stringstream log_msg;
            std::vector<std::string>::const_iterator it;
            for(it = m_srbuffer.begin(); it < m_srbuffer.end(); it++)
            {
                log_msg << *it << " ~  ";
            }
            U7_LOG("Chat4 : " << _cmd << " ~ " << log_msg.str());
        }
// -1 as number of lines means that it have to be read a stream until the
// acknowledge arrives. That means that the counted received lines have to
// be not equal with the number of lines!

        T_LOG ( "rc=" << rc );
        D_LOG ( os.str() );
#endif
        return i;
    }

    int ChatLine::chat ( const char *_rsp, const char *_cmd )
    {
        std::string line_buffer;
        int rc, i = 0;
#ifndef _WIN32
        std::ostringstream os;

        os << m_device_name << " <";
        if ( _cmd && strlen ( _cmd ) > 0 )
        {
            sendline ( _cmd );
            os << _cmd;
        }
        os << "> \"" << _rsp << "\" = ";

        if ( _rsp == NULL )
            return 0;
        m_srbuffer.clear ();
        do
        {
            rc = recvline ( line_buffer );
            T_LOG ( "rc=" << rc );
            if ( rc == CONNECTION_TIMEOUT )
            {
                os << " - Read timeout";
                W_LOG ( os.str() );
                timeoutAktion ( "Read timeout" );
            }

            if ( rc < 0 )
            {
                os << " - " << strerror ( errno );
                E_LOG ( os.str() );
                E_THROW ( ::Nice::ChatException, strerror ( errno ) );
            }

            m_srbuffer.push_back ( line_buffer );
            T_LOG ( line_buffer );
            i++;
            os << "<" << line_buffer << ">";
        }
        while ( fnmatch ( _rsp, line_buffer.c_str (), FNM_NOESCAPE ) == FNM_NOMATCH );
        int card, command, module;
        int scanresult = sscanf(_cmd, "%d %d %d", &card, &command, &module);
        if(scanresult == 3 && (module == 5 || module == 6) && command != 171 && command != 166)
        {
            std::stringstream log_msg;
            std::vector<std::string>::const_iterator it;
            for(it = m_srbuffer.begin(); it < m_srbuffer.end(); it++)
            {
                log_msg << *it << " ~  ";
            }
            U7_LOG("Chat5 : " << _cmd << " ~ " << log_msg.str());
        }
        T_LOG ( "rc=" << rc );
        D_LOG ( os.str() );
#endif
        return i;
    }

#define SRLINE_SECPRINTF( x, y, z ) \
  { \
     int size = 80; \
     x = new char[size]; \
     while (true) {  \
         int r = vsnprintf(x, size, y, z); \
         if (r == -1)  {  delete [] x;  size *= 2;  x = new char[size];  }  \
         else if (r >= size)  {  delete [] x;  size += r;  x = new char[size];  }  \
         else  {  break;  } \
     } \
  }

    int ChatLine::mprintf ( int _num, const char *_cmd_fmtstr,
                            ... )
    {
        char *str = 0;
        va_list ap;
        va_start ( ap, _cmd_fmtstr );
        if ( _cmd_fmtstr )  SRLINE_SECPRINTF ( str, _cmd_fmtstr, ap );
        va_end ( ap );
        int rc = chat ( _num, str );
        if ( str )
            delete[]str;
        return rc;
    }

    int ChatLine::nprintf ( int _max, const char *_rsp_fmtstr, const char *_cmd_fmtstr,
                            ... )
    {
        char *cmd_str = 0;
        char *rsp_str = 0;

        va_list ap, aq;
        va_start ( ap, _cmd_fmtstr );
        va_copy ( aq, ap );

        if ( _cmd_fmtstr ) SRLINE_SECPRINTF ( cmd_str, _cmd_fmtstr, ap );
        if ( _rsp_fmtstr ) SRLINE_SECPRINTF ( rsp_str, _rsp_fmtstr, aq );

        va_end ( ap );
        va_end ( aq );

        int rc = chat ( rsp_str, _max, cmd_str );
        if ( cmd_str ) delete[]cmd_str;
        if ( rsp_str ) delete[]rsp_str;
        return rc;
    }

    int ChatLine::printf ( const char *_rsp_fmtstr, const char *_cmd_fmtstr,
                           ... )
    {
        char *cmd_str = 0;
        char *rsp_str = 0;

        va_list ap, aq;
        va_start ( ap, _cmd_fmtstr );
        va_copy ( aq, ap );

        if ( _cmd_fmtstr ) SRLINE_SECPRINTF ( cmd_str, _cmd_fmtstr, ap );
        if ( _rsp_fmtstr ) SRLINE_SECPRINTF ( rsp_str, _rsp_fmtstr, aq );

        va_end ( ap );
        va_end ( aq );

        int rc = chat ( rsp_str, cmd_str );
        if ( cmd_str )
            delete[]cmd_str;
        if ( rsp_str )
            delete[]rsp_str;
        return rc;
    }

    std::string ChatLine::operator[] ( int _num )
    {
        if ( _num >= ( int ) m_srbuffer.size () )
            E_THROW ( ::Nice::ChatException, "Buffer overflow" );
        return m_srbuffer[_num];
    }
}
