/*! 
* 
* Copyright(c) 2010 Apogee Instruments, Inc. 
* \class LinuxEthernetIO 
* \brief this class uses libcurl to perform IO on the Linux OS 
* 
*/ 

#include "LinuxEthernetIO.h" 

//for runtime exception
#include <stdexcept>

//////////////////////////// 
// Write any errors in here  
static char errorBuffer[CURL_ERROR_SIZE];  

//////////////////////////// 
// VECT WRITER
static std::vector<unsigned char> bufferVect;  
static int vectWriter(unsigned char *data, size_t size, size_t nmemb,  
                  std::vector<unsigned char> &bufferVect) 
{
 
    const int numBytes = size * nmemb;

    for(int i = 0; i < numBytes; ++i)
    {
        bufferVect.push_back( data[i] );
    }

    return numBytes;
}
 

//////////////////////////// 
// STR WRITER
// This is the writer call back function used by curl  
static std::string bufferStr; 
static int strWriter(char *data, size_t size, size_t nmemb,  
                  std::string &bufferStr) 
{
 
    const int numBytes = size * nmemb;

    bufferStr.append( data, numBytes );

    return numBytes;
}

//////////////////////////// 
// LOCAL     NAMESPACE
namespace
{
    //TODO - determine if i need to make this configurable
    const long OPERATION_TIMEOUT = (60*1);  //60 seconds * the number of minutes
};

//////////////////////////// 
// CTOR 
LinuxEthernetIO::LinuxEthernetIO() : m_curlHandle( 0 )                     
{ 
    m_curlHandle = curl_easy_init();

    if( !m_curlHandle )
    {
        std::string errStr("");
        std::runtime_error err( errStr );
        throw( err );
    }
} 

//////////////////////////// 
// DTOR 
LinuxEthernetIO::~LinuxEthernetIO() 
{ 
    curl_easy_cleanup( m_curlHandle );
} 



//////////////////////////// 
// HTTP GET 
void LinuxEthernetIO::HttpGet( const std::string & Url,
            std::string & data)
{
    CurlSetupStrWrite ( Url );
    data = ExecuteStr();
}


//////////////////////////// 
// HTTP GET 
void LinuxEthernetIO::HttpGet( const std::string & Url, 
            std::vector<unsigned short> & data,
            const int numBytesExpected)
{
    CurlSetupVectWrite ( Url );
    std::vector<unsigned char> result = ExecuteVect();
    
    if( result.size() != numBytesExpected )
    {
        throw std::runtime_error( "Invalid number of bytes received." );
    }

    //TODO inefficent, have to do an extra memcpy to get
    //the info into a std::vector<unsigned short> format
    //figure out a better way to do this
    data.clear();
    data.resize( result.size() / sizeof(unsigned short) );
    memcpy( &(*data.begin()), &(*result.begin()) , result.size() );
   
}


//////////////////////////// 
// CURL     SETUP  STR  WRITE
void LinuxEthernetIO::CurlSetupStrWrite(const std::string & url)
{
     // Now set up all of the curl options  
    curl_easy_setopt(m_curlHandle, CURLOPT_ERRORBUFFER, errorBuffer);  
    curl_easy_setopt(m_curlHandle, CURLOPT_URL, url.c_str());  
    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, strWriter);  
    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, &bufferStr); 
    curl_easy_setopt(m_curlHandle, CURLOPT_TIMEOUT,OPERATION_TIMEOUT);
    
}

//////////////////////////// 
// CURL     SETUP       VECTOR          WRITE
void LinuxEthernetIO::CurlSetupVectWrite(const std::string & url)
{
     // Now set up all of the curl options  
    curl_easy_setopt(m_curlHandle, CURLOPT_ERRORBUFFER, errorBuffer);  
    curl_easy_setopt(m_curlHandle, CURLOPT_URL, url.c_str());  
    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, vectWriter);  
    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, &bufferVect); 
    curl_easy_setopt(m_curlHandle, CURLOPT_TIMEOUT,OPERATION_TIMEOUT);
}

//////////////////////////// 
// EXECUTE  STR
std::string LinuxEthernetIO::ExecuteStr()
{
    //clear out the string
    bufferStr.clear();

    //perform the transfer
    const CURLcode result = curl_easy_perform(m_curlHandle);

    if( CURLE_OK != result )
    {
        //TODO - figure out how to add the command that was sent to
        //the error msg
        throw std::runtime_error( errorBuffer );
       
    }

    return bufferStr;
}

//////////////////////////// 
// EXECUTE  VECT
std::vector<unsigned char> LinuxEthernetIO::ExecuteVect()
{
    //clear out the vector
    bufferVect.clear();

    //perform the transfer
    const CURLcode result = curl_easy_perform(m_curlHandle);

    if( CURLE_OK != result )
    {
        //TODO - figure out how to add the command that was sent to
        //the error msg
        throw std::runtime_error( errorBuffer );
    }

    return bufferVect;
} 
