//////////////////////////////////////////////////////////////////////
//
// ApogeeNet.cpp : Library of basic networking functions for Apogee APn/Alta.
//
// Copyright (c) 2003-2006 Apogee Instruments, Inc.
//
//////////////////////////////////////////////////////////////////////

#ifdef WIN32
// removes warnings about vectors
#pragma warning(disable: 4786)
#endif

#include "ApogeeNet.h"
#include "ApogeeNetErr.h"
#include "ApgEthernet.h"
#include <string.h>
#include <vector>
#include <string>
#include <sstream>
#include <iomanip>

#define LOG_ERROR 1
 
#ifdef LOG_ERROR
    #include <ctime>
    #include <fstream>
#endif

//-----------------------------------
//  PRIVATE VARIABLE
ApgEthernet * m_Io = NULL;

//-----------------------------------
//  PRIVATE FUNCTIONS

std::string uShort2Str(const unsigned short val, bool hexOut=false);
std::string uLong2Str(const unsigned long val, bool hexOut=false);

std::vector<std::string> MakeTokens(const std::string &str,
const std::string &separator);

std::string mkHexRegStr( const unsigned short Reg );

APN_NET_TYPE HandleException( const std::string & what, APN_NET_TYPE error );

void OpenCameraSession( const ApgEthernet & NetIo, char *HostAddr );
void CloseCameraSession( const ApgEthernet & NetIo, char *HostAddr );

void CheckTheIoObj();

//-----------------------------------
//  PUBLIC FUNCTIONS

// This is an example of an exported function.
APN_NET_TYPE ApnNetConnect( char *HostAddr )
{
    try
    {
        m_Io = new ApgEthernet();

        CheckTheIoObj();

        OpenCameraSession( *m_Io, HostAddr );

        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetClose( char *HostAddr )
{
	
    try
    {
        CheckTheIoObj();

        CloseCameraSession( *m_Io, HostAddr );
      	
        delete m_Io;
        m_Io = NULL;

        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {        
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetReboot( char *HostAddr )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(REBOOT_CMD);

        std::string data;
        m_Io->HttpGet( Url, data );
        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	
}


APN_NET_TYPE ApnNetReadReg( char			*HostAddr, 
							unsigned short	FpgaReg, 
							unsigned short	*FpgaData )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(FPGA_ACCESS);
        Url.append(READ_REG);
        Url.append( uShort2Str(FpgaReg) );

        std::string data;
        m_Io->HttpGet( Url, data );

        if( 0 == data.size() )
        {
            throw std::runtime_error("ApnNetReadReg no data");
        }

        std::vector<std::string> tokens = MakeTokens(data,"=");

        std::istringstream is( tokens.at(1) );
        is >> std::hex >> *FpgaData;

        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	


}


APN_NET_TYPE ApnNetWriteReg( char *HostAddr, 
							 unsigned short	FpgaReg, 
							 unsigned short FpgaData )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(FPGA_ACCESS);
        Url.append(WRITE_REG);
        Url.append(uShort2Str(FpgaReg));
        Url.append("&");
        Url.append(WRITE_DATA);
        Url.append(uShort2Str(FpgaData,true));

        std::string data;
        m_Io->HttpGet( Url, data );

        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetWriteRegMulti( char			 *HostAddr,
								  unsigned short FpgaReg, 
								  unsigned short FpgaData[], 
								  const unsigned short RegCount )
{
    try
    {
        CheckTheIoObj();

        std::string BaseUrl;
        BaseUrl.append(HTTP_PREAMBLE);
        BaseUrl.append(HostAddr);
        BaseUrl.append(FPGA_ACCESS);
        BaseUrl.append(WRITE_REG);
        BaseUrl.append(uShort2Str(FpgaReg));
       
        int count = 0;
        std::string Url( BaseUrl );

        for(int i=0; i < RegCount; ++i)
        {
            Url.append("&");
            Url.append( WRITE_DATA );
            Url.append( mkHexRegStr(FpgaData[i]) );

            if( APN_MAX_WRITES_PER_URL-1 == count )
            {
                //send data
                std::string data;
                m_Io->HttpGet( Url, data );

                //reset
                Url = BaseUrl;
                count = 0;
            }
            else
            {
                ++count;
            }
        }

        //send any remaining data
        if( count )
        {
             std::string data;
             m_Io->HttpGet( Url, data );
        }
      
        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }
}


APN_NET_TYPE ApnNetWriteRegMultiMRMD( char			 *HostAddr,
									  unsigned short FpgaReg[],
									  unsigned short FpgaData[],
									  const unsigned short RegCount )
{
    for(int i=0; i < RegCount; ++i)
    {
        APN_NET_TYPE result = ApnNetWriteReg( HostAddr, 
							 FpgaReg[i], FpgaData[i] );

        if( APN_NET_SUCCESS != result )
        {
            return result;
        }
    }

    return APN_NET_SUCCESS;
}


APN_NET_TYPE ApnNetReadRegMulti( char			*HostAddr,
								 unsigned short FpgaReg[], 
								 unsigned short FpgaData[], 
								 const unsigned short RegCount )
{

    try
    {
        CheckTheIoObj();

        //build the get string with all of the registers
        std::string base;
        base.append(HTTP_PREAMBLE);
        base.append(HostAddr);
        base.append("/FPGA?");
        std::string finalUrl = base;

   
        bool first = true;
        std::string finalResult;
        int count = 0;

        for(int i=0; i < RegCount; ++i )
        {
            if( first )
            {
                std::string vv = "RR=" +  uShort2Str( FpgaReg[i] );
                finalUrl.append(vv);
                first = false;
            }
            else
            {
                std::string vv = "&RR=" +  uShort2Str( FpgaReg[i] );
                finalUrl.append(vv);
            }

            if( APN_MAX_READS_PER_URL-1 == count )
            {
                 //send request for registers
                std::string result;
                m_Io->HttpGet( finalUrl, result );
           
                //save the reply
                finalResult.append( result );

                //reset
                count = 0;
                finalUrl = base;
                first = true;
            }
            else
            {
                ++count;
            }
            
        }

        if( count )
        {
            //send the cmd
            std::string result;
            m_Io->HttpGet( finalUrl, result );
       
            //save the reply
            finalResult.append( result );
        }

        //convert the string into a map
        std::vector<std::string> tokens = MakeTokens(finalResult,"\n");

        std::vector<std::string>::iterator siter;
    
        int num=0;
        for( siter = tokens.begin(); siter != tokens.end(); ++siter, ++num )
        {        
            int regStart = (*siter).find("[");
            int regEnd = (*siter).find("]");
            int len = regEnd - regStart - 1;
            std::string regStr = (*siter).substr(regStart+1,len);   

            unsigned short theReg = 0;
            std::istringstream is1( regStr );
            is1 >> theReg;

            int valStart = (*siter).find("x");
            int valEnd = (*siter).size();
            len = valEnd - valStart - 1;
            std::string valStr = (*siter).substr(valStart+1,len);

            unsigned short theValue = 0;
            std::istringstream is2( valStr );
            is2 >> std::hex >> theValue;

            if( num < RegCount )
            {
               FpgaData[num] = theValue;
            }

            
        }

        return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }


}


APN_NET_TYPE ApnNetStartExp( char			*HostAddr,
							 unsigned long	ImageWidth, 
							 unsigned long	ImageHeight )
{

	// Check to make sure width and height are non-zero
	if ( ( ImageWidth == 0 ) || ( ImageHeight == 0 ) )
	{
		return APN_NET_ERR_IMAGE_PARAMS;		// Failure
	}

	// Check to make sure our total image size is less than or equal to what we can do
	if ( ( ImageWidth * ImageHeight * 2 ) > ( 1024 * 1024 * 28 ) )
	{
		return APN_NET_ERR_IMAGE_PARAMS;		// Failure
	}


    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append("/FPGA?CI=0,0,");
        Url.append(uLong2Str( ImageWidth ));
        Url.append(",");
        Url.append(uLong2Str( ImageHeight ));
        Url.append(",0xFFFFFFFF");

        //send the cmd
        std::string result;
        m_Io->HttpGet( Url, result );
       

        return APN_NET_SUCCESS;	// Success
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }
}


APN_NET_TYPE ApnNetStopExp( char		*HostAddr,
							bool		DigitizeData )
{

    //TODO - figure out if this really is a no op

	return APN_NET_SUCCESS;	// Success
}


APN_NET_TYPE ApnNetSetSpeed( char		*HostAddr,
							 bool		HighSpeed )
{
	// High speed mode is UDP
	// Normal mode is TCP

	if ( HighSpeed )
	{
	}
	else
	{
	}

	return APN_NET_SUCCESS;	// Success
}


APN_NET_TYPE ApnNetGetImageTcp( char			*HostAddr,
								const unsigned long	ImageByteCount,
								unsigned short	*pMem )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append("/UE/image.bin");

        std::vector<unsigned short> data;
        m_Io->HttpGet( Url, data, ImageByteCount );

        const unsigned long actualBytes = data.size() * sizeof(unsigned short);

        if( actualBytes != ImageByteCount )
        {
            return APN_NET_ERR_IMAGE_DATA;
        }

        std::vector<unsigned short>::iterator iter;
        int count = 0;
        
        for( iter = data.begin(); iter != data.end(); ++iter, ++count)
	    {
		    pMem[count] = ntohs( (*iter) );
	    }

	    return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	
}


APN_NET_TYPE ApnNetGetNvramData( char *HostAddr,
                                 const unsigned long ImageByteCount,
								 char *pMem )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append("/UE/nvram.bin");

        //send the cmd
        std::string result;
        m_Io->HttpGet( Url, result );

        if( ImageByteCount != result.size() )
        {
            throw std::runtime_error("ApnNetGetNvramData invalid data size");
        }

        memcpy( pMem, &(*result.begin()), ImageByteCount );

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetGetMacAddress( char *HostAddr,
								  char *pMacAddr )
{
    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(MAC_ADDRESS_READ);

        //send the cmd
        std::string result;
        m_Io->HttpGet( Url, result );
	
	    unsigned short buffer[3];

	    const APN_NET_TYPE ApStatus = ApnNetGetNvramData( HostAddr, sizeof(buffer), (char*) buffer );

        if( ApStatus != APN_NET_SUCCESS )
        {
            return ApStatus;
        }
	    
	    sprintf( pMacAddr, "%04X%04X%04X", ntohs(buffer[0]), ntohs(buffer[1]), ntohs(buffer[2]) );

	    return APN_NET_SUCCESS;
    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }
}


APN_NET_TYPE ApnNetSerialReadIpPort( char			*HostAddr,
									 unsigned short SerialId,
									 unsigned short	*PortNumber )
{

	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}

    try
    {
        CheckTheIoObj();

        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_GET_IP_PORT);

        if ( SerialId == 0 )
        {
             Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }
      
        //send the cmd
        std::string result;
        m_Io->HttpGet( Url, result );

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetStartSockets( ) 
{
#ifdef WIN32
	WSADATA stWSAData;
	int		nRet;

	nRet = WSAStartup( MAKEWORD(2, 0), &stWSAData );
#endif
	return APN_NET_SUCCESS;
} 


APN_NET_TYPE ApnNetStopSockets( )
{
#ifdef WIN32
	WSACleanup();
#endif
	return APN_NET_SUCCESS;
} 


APN_NET_TYPE ApnNetSerialPortOpen( SOCKET		  *SerialSocket,
								   char			  *HostAddr,
								   unsigned short PortNumber )
{
	unsigned long		CamIpAddress;
	struct sockaddr_in  CamPortAddr;


	CamIpAddress = inet_addr( HostAddr );

	*SerialSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
	
	CamPortAddr.sin_family		= AF_INET;
	CamPortAddr.sin_port		= htons( PortNumber );
	CamPortAddr.sin_addr.s_addr	= CamIpAddress;

	if ( connect(*SerialSocket, (struct sockaddr *)&CamPortAddr, sizeof(CamPortAddr)) == SOCKET_ERROR )
	{
		return APN_NET_ERR_SERIAL_CONNECT;
	}

	return APN_NET_SUCCESS;	// Success
}


APN_NET_TYPE ApnNetSerialPortClose( SOCKET *SerialSocket )
{
	closesocket( *SerialSocket );

	return APN_NET_SUCCESS;	// Success
}


APN_NET_TYPE ApnNetSerialReadBaudRate( char			  *HostAddr,
									   const unsigned short SerialId,
									   unsigned long  *BaudRate )
{
	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}


    try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_GET_BAUD_RATE);

	    // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }

        //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        //get the data out of the result
        std::vector<std::string> tokens = MakeTokens(data,",");

        std::istringstream is( tokens.at(2) );
        is >> *BaudRate;


        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }		
}


APN_NET_TYPE ApnNetSerialWriteBaudRate( char		   *HostAddr,
										unsigned short SerialId,
										unsigned long  BaudRate )
{
	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}

	// Verify that the baud rate is a valid number
	if ( (BaudRate != SERIAL_BAUD_RATE_1200) && (BaudRate != SERIAL_BAUD_RATE_2400) &&
		 (BaudRate != SERIAL_BAUD_RATE_4800) && (BaudRate != SERIAL_BAUD_RATE_9600) &&
		 (BaudRate != SERIAL_BAUD_RATE_19200) && (BaudRate != SERIAL_BAUD_RATE_38400) &&
		 (BaudRate != SERIAL_BAUD_RATE_57600) && (BaudRate != SERIAL_BAUD_RATE_115200) )
	{
		return APN_NET_ERR_SERIAL_BAUDRATE;
	}


	try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_SET_BAUD_RATE);

	    // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }


        Url.append(",");
        Url.append(uLong2Str(BaudRate));

        //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	
}


APN_NET_TYPE ApnNetSerialReadFlowControl( char			 *HostAddr,
										  unsigned short SerialId,
								 		  bool			 *FlowControl )
{
	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}

    try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_GET_FLOW_CONTROL);

	    // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }

         //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        //get the data out of the result
        std::vector<std::string> tokens = MakeTokens(data,",");

        std::istringstream is( tokens.at(2) );
        is >> *FlowControl;


        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetSerialWriteFlowControl( char			  *HostAddr,
										   const unsigned short SerialId,
								 		   const bool FlowControl )
{
	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}

	try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_GET_FLOW_CONTROL);

	    // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }

        Url.append(",");

	    // Determine the flow control setting
	    if ( FlowControl )
        {
            Url.append(SERIAL_FLOW_CONTROL_SW);
        }
	    else
        {
            Url.append(SERIAL_FLOW_CONTROL_NONE);
        }

        //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetSerialReadParity( char			*HostAddr,
									 unsigned short SerialId,
									 ApnNetParity   *Parity )
{
	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}


    try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_GET_PARITY);

        // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }

         //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        //get the data out of the result
        std::vector<std::string> tokens = MakeTokens(data,",");

        if( tokens.at(2).compare(SERIAL_PARITY_NONE) == 0 )
		{
			*Parity = ApnNetParity_None;
		}
		else if(tokens.at(2).compare(SERIAL_PARITY_EVEN) == 0 )
		{
			*Parity = ApnNetParity_Even;
		}
		else if( tokens.at(2).compare(SERIAL_PARITY_ODD) == 0 )
		{
			*Parity = ApnNetParity_Odd;
		}
        else
        {
            throw std::runtime_error("Invalid Parity from camera");
        }

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetSerialWriteParity( char			 *HostAddr,
									  unsigned short SerialId,
									  ApnNetParity   Parity )
{
	

	// Make sure the serial port selection is valid
	if ( (SerialId != 0) && (SerialId != 1) )
	{
		return APN_NET_ERR_SERIAL_ID;
	}


    try
    {
        std::string Url;
        Url.append(HTTP_PREAMBLE);
        Url.append(HostAddr);
        Url.append(SERIAL_SET_PARITY);

	    // Create a string based on the particular serial port
        if ( SerialId == 0 )
        {
            Url.append(SERIAL_PORT_A);        
        }
        else
        {
            Url.append(SERIAL_PORT_B);
        }

        Url.append(",");

        // Determine new parity setting
        switch ( Parity )
        {
            case ApnNetParity_None:
                 Url.append(SERIAL_PARITY_NONE );
            break;

            case ApnNetParity_Even:
                 Url.append(SERIAL_PARITY_EVEN );
            break;

            case ApnNetParity_Odd:
                 Url.append(SERIAL_PARITY_ODD );
            break;

            default:
                throw std::runtime_error("Invalid input parity");
            break;
        }

        //send message
        ApgEthernet NetIo;

        OpenCameraSession( NetIo, HostAddr );

        std::string data;
        NetIo.HttpGet( Url, data );
    
        CloseCameraSession( NetIo, HostAddr );

        return APN_NET_SUCCESS;	// Success

    }
    catch(std::exception & err)
    {
        return HandleException( err.what(), APN_NET_ERR_GENERIC_CGI );
    }	

}


APN_NET_TYPE ApnNetSerialRead( SOCKET		  *SerialSocket,
							   char			  *ReadBuffer,
							   unsigned short *BufferCount )
{
	int				RetVal;
	char			Buffer[256];
	unsigned short	BufferSize;


	BufferSize = 255;

	// Make sure the input params are valid
	if ( (ReadBuffer == NULL) || (BufferCount == NULL) )
		return APN_NET_ERR_SERIAL_READ_INPUT;

	// Do the operation
	RetVal = recv( *SerialSocket, Buffer, BufferSize, 0 );

	if ( RetVal == 0 )
	{
		*BufferCount = 0;
		return APN_NET_ERR_SERIAL_NO_CONNECTION;
	}

	*BufferCount = RetVal;

	strncpy( ReadBuffer, Buffer, *BufferCount );

	ReadBuffer[*BufferCount] = '\0';

	return APN_NET_SUCCESS;	// Success
}


APN_NET_TYPE ApnNetSerialWrite( SOCKET		   *SerialSocket,
							    char		   *WriteBuffer,
								unsigned short BufferCount )
{
	int RetVal;


	// Make sure the input params are valid
	if ( (BufferCount == 0) || (WriteBuffer == NULL) )
		return APN_NET_ERR_SERIAL_WRITE_INPUT;

	RetVal = send( *SerialSocket, WriteBuffer, BufferCount, 0 );

	if ( RetVal == 0 )
		return APN_NET_ERR_SERIAL_NO_CONNECTION;
	else if ( RetVal != BufferCount )
		return APN_NET_ERR_SERIAL_WRITE_FAILURE;

	return APN_NET_SUCCESS;	// Success
}


//----------------------------------------------
//  USHORT      2       STR
std::string uShort2Str(const unsigned short val, bool hexOut)
{
    std::stringstream ss;

    if( hexOut )
    {
        ss <<  "0x" << std::hex << val;
    }
    else
    {
        ss << val;
    }

    return ss.str();
}


//----------------------------------------------
//  ULONG      2       STR
std::string uLong2Str(const unsigned long val, bool hexOut)
{
    std::stringstream ss;

    if( hexOut )
    {
        ss <<  "0x" << std::hex << val;
    }
    else
    {
        ss << val;
    }

    return ss.str();
}

//----------------------------------------------
//  MAKE        TOKENS
std::vector<std::string> MakeTokens(const std::string &str,
const std::string &separator)
{
    std::vector<std::string> returnVector;
    std::string::size_type start = 0;
    std::string::size_type end = 0;

    while( (end = str.find (separator, start)) != std::string::npos)
    {
        returnVector.push_back (str.substr (start, end-start));
        start = end + separator.size();
    }

    returnVector.push_back( str.substr(start) );

    return returnVector;
}

//----------------------------------------------
//  MK   HEX   REG      STR
std::string mkHexRegStr( const unsigned short Reg )
{
    if( !Reg )
    {
        std::string result("0x0000");
        return result;
    }
    else
    {
        std::stringstream ss;
        ss <<  std::hex << std::setw(6) << std::setfill('0') << std::internal << std::showbase << Reg;
        return ss.str();
    }
}

//----------------------------------------------
//      HANDLE  EXCEPTION
APN_NET_TYPE HandleException( const std::string & what, const APN_NET_TYPE error )
{
#ifdef LOG_ERROR
    time_t rawtime;
    time ( &rawtime );
    std::string dateTime( ctime(&rawtime) );
    
    //remove the new line
    std::string newline("\n");
    size_t found = dateTime.find_last_not_of(newline);
    if (found != std::string::npos)
    {
        dateTime.erase(found+1);
    }

    std::ofstream theFile;
    theFile.open( "C:/temp/logMe.txt", std::ios_base::app );

    theFile << dateTime.c_str() << ": " << what.c_str() << "\n";
    theFile.close();
    
#endif
    return error;
}


//----------------------------------------------
//  CHECK   THE     IO      OBJ
void CheckTheIoObj()
{
    if( NULL == m_Io )
    {
        throw std::runtime_error( "Ethernet IO object is NULL." );
    }
}


//----------------------------------------------
//  OPEN        CAMERA      SESSION
void OpenCameraSession( const ApgEthernet & NetIo, char *HostAddr )
{

    std::string Url;
    Url.append(HTTP_PREAMBLE);
    Url.append(HostAddr);
    Url.append(SESSION_OPEN);

    std::string data;
    NetIo.HttpGet( Url, data );

}

//----------------------------------------------
//  CLOSE        CAMERA      SESSION
void CloseCameraSession( const ApgEthernet & NetIo, char *HostAddr )
{
    std::string Url;
    Url.append(HTTP_PREAMBLE);
    Url.append(HostAddr);
    Url.append(SESSION_CLOSE);

    std::string data;
    NetIo.HttpGet( Url, data );

    if( data.size() == 0 )
    {
        throw std::runtime_error("Disconnect failed, session id not returned from camera.");
    }
}
 
