#include "AcquireNeutronSourceInformationBase.hh"
//////////////////////////////////////////////////////////////////////
AcquireNeutronSourceInformationBase::
AcquireNeutronSourceInformationBase(){

    //std::cout << std::endl;
    //std::cout << "InformationBase()" << std::endl;
    HostProtocol = "http";
}
//////////////////////////////////////////////////////////////////////
AcquireNeutronSourceInformationBase::
AcquireNeutronSourceInformationBase( std::string host_name ){
    SetHostName( host_name );
    HostProtocol = "http";

    //std::cout << std::endl;
    //std::cout << "InformationBase(host_name)" << std::endl;
    //std::cout << "host_name : " << host_name << std::endl;
}
//////////////////////////////////////////////////////////////////////
AcquireNeutronSourceInformationBase::
~AcquireNeutronSourceInformationBase(){
}
//////////////////////////////////////////////////////////////////////
void AcquireNeutronSourceInformationBase::
SetHostName( const std::string host_name ){
    if ( host_name.empty() ){
    }
    else {
        std::string::size_type ind_https = host_name.find("https://");
        if (ind_https==0){
            SetHostProtocol( "https" );
            HostName = host_name.substr(8);
        }
        else{
            std::string::size_type ind_http = host_name.find("http://");
            if (ind_http==0){
                SetHostProtocol( "http" );
                HostName = host_name.substr(7);
            }
            else{
                HostName = host_name;
            }
        }
    }
    std::cout << "HostName : " << HostName << std::endl;
}
//////////////////////////////////////////////////////////////////////
void AcquireNeutronSourceInformationBase::
SetHostProtocol( const std::string host_protocol ){
    if ( host_protocol.empty() ){
    }
    else{
        if ((host_protocol=="http")||(host_protocol=="HTTP")){
            HostProtocol = "http";
        }
        else if ((host_protocol=="https")||(host_protocol=="HTTPS")){
            HostProtocol = "https";
        }
    }
    std::cout << "HostProtocol : " << HostProtocol << std::endl;
}
//////////////////////////////////////////////////////////////////////
std::string AcquireNeutronSourceInformationBase::
PutHostName(){
    return HostName;
}
//////////////////////////////////////////////////////////////////////
void AcquireNeutronSourceInformationBase::
SetFilePath( const std::string file_path ){
    if ( file_path.empty() ){
    }
    else {
        FilePath = file_path;
    }
    //std::cout << "FilePath : " << FilePath << std::endl;
}
//////////////////////////////////////////////////////////////////////
std::string AcquireNeutronSourceInformationBase::
PutFilePath(){
    return FilePath;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
InitializeSockaddrIn () {
    //u_short port = 80;

    memset( &sin, 0, sizeof(sin) );  /* initialized address struct */
    sin.sin_family = AF_INET;
    //sin.sin_port   = htons(port);
    //sin.sin_addr   = 0;

    /* Is HostName an IP address form? */
    if( !inet_aton( HostName.c_str(), &sin.sin_addr ) ) {
        /* Otherwise, an IP address is searched from HostName */
        struct hostent *host = gethostbyname( HostName.c_str() );
        if (!host) {
            return -1;
        }
        sin.sin_addr.s_addr = *(unsigned long*)host->h_addr_list[0];
    }

    /* Get the port number. */
    struct servent *serv;
    //serv = getservbyname( "http", "tcp" );
    serv = getservbyname( HostProtocol.c_str(), "tcp" );
    if (!serv) {
        return -1;
    }
    sin.sin_port = serv->s_port;

    return EXIT_SUCCESS;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
CreateSocket () {
    SocketDescriptor = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    return SocketDescriptor;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
ServerConnect () {
     Int4 ret = connect( SocketDescriptor, (struct sockaddr*)&sin, sizeof(sin) );
     return ret;
}
//////////////////////////////////////////////////////////////////////
void AcquireNeutronSourceInformationBase::
SocketClose () {
     close( SocketDescriptor );
     return;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
SendRequest (std::string& request) {
    if (HostProtocol=="https")
        return SendRequestHTTPS(request);
    else
        return SendRequestHTTP(request);
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
SendRequestHTTP (std::string& request) {
    int length = request.length();
    int ret = 0;

    //std::cout << "length  : " << length << std::endl;
    //std::cout << request << std::endl;
    if( send( SocketDescriptor, request.c_str(), length, 0 ) != length ) {
        ret = -1;
    }

    return ret;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
SendRequestHTTPS (std::string& request) {
    int length = request.length();
    int ret = 0;

    SSL_load_error_strings();
    SSL_library_init();
    ctx = SSL_CTX_new(SSLv23_client_method());
    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, SocketDescriptor);
    SSL_connect(ssl);
    SSL_write(ssl, request.c_str(), length);

    return ret;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
RecvReply (std::string& recv_data) {
    fd_set readfds;
    Char recv_buf[BUFSIZE];  /* Data receiving buffer */
    //std::vector<Char> recv_buf(BUFSIZE);  /* Data receiving buffer */
    Int4 length;

    Int4 recv_length = 0;
    recv_data = "";
    while( 1 ) {
        /* Initialization of descriptor set */
        FD_ZERO( &readfds );
        FD_SET( SocketDescriptor, &readfds );
        memset( recv_buf, 0, sizeof(recv_buf) );   /* clear recive buffer */
        //recv_buf.assign( BUFSIZE, 0 );

        /* It waits for the arrival of the data from the server */
        if( select( SocketDescriptor+1, &readfds, NULL, NULL, NULL ) < 0 ) {
            std::cout << "It failed in select!" << std::endl;
            return -1;
        }

        /* Is there data received to the descriptor? */
        if( FD_ISSET( SocketDescriptor, &readfds ) ) {
            /* Receiving the data */
            if (HostProtocol=="https"){
                length = SSL_read(ssl, recv_buf, sizeof(recv_buf));
            }
            else
                length = recv( SocketDescriptor, recv_buf, sizeof(recv_buf), 0 );
            //length = recv( SocketDescriptor, &recv_buf, recv_buf.size()-1, 0 );
            //std::cout << "     length : " << length << std::endl;
            if ( length < 0 ) {
                return -1;
            }
            else if ( length == 0 ) {   /* Receiving normal end */
                break;
                //return recv_length;
            }
            else {
                recv_length += length;
                recv_data.append(recv_buf,length);
            }
        }
    } /* while loop end */
    //std::cout << "recv_length      : " << recv_length << std::endl;
    //std::cout << "recv_data.length : " << recv_data.length() << std::endl;
    //std::cout << recv_data.substr(0,282) << std::endl;
    //std::cout << recv_data.substr(recv_length-36,34) << std::endl;
    //std::printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
    //       (recv_data.substr(recv_length-34,34)).c_str());  /* last 34 characters */
    if (HostProtocol=="https"){
        SSL_shutdown(ssl);
        SSL_free(ssl);
        SSL_CTX_free(ctx);
        ERR_free_strings();
    }
    return recv_length;
}
//////////////////////////////////////////////////////////////////////
Int4 AcquireNeutronSourceInformationBase::
SplitBodyData (std::string& recv_data, std::string& body_data) {

    //const std::string http_key( "HTTP" );
    std::string HostProtocolLC( HostProtocol );
    transform( HostProtocolLC.begin(), HostProtocolLC.end(), HostProtocolLC.begin(), ::toupper );
    const std::string http_key( HostProtocolLC );
    const std::string content_key( "Content-Length:" );
    const std::string separate_key( "\r\n\r\n" );
    const std::string end_key( "Connection closed by foreign host." );  /* Last 34(+2?) characters */
    std::string work_string;
    std::string::size_type start_pos, end_pos;
    Int4 content_length = 0;
    Int4 ret = 0;

    start_pos = recv_data.find(http_key);
    /* without HTTP header */
    if ( start_pos == std::string::npos ){
        start_pos = 0;
    }
    /* with HTTP header */
    else {
        /* check HTTP error */
        std::istringstream strin( recv_data.substr(start_pos) );
        strin >> work_string >> ret;
        std::cout << "HTTP return code : " << ret << std::endl;
        if ( ret != 200 ) {   /* HTTP status code is ok */
            ret = -1;
        }
        /* check content length */
        else {
            start_pos = recv_data.find( content_key, start_pos+http_key.length() );
            if ( start_pos == std::string::npos ) {
                start_pos = http_key.length() + 13;   /* /1.0 200 OK\r\n = 13 */
            }
            /* get content length */
            else {
                strin.clear();   /* clear EOF */
                strin.str( recv_data.substr(start_pos) );
                strin >> work_string >> content_length;
                std::cout << "content_length   : " << content_length << std::endl;
                start_pos = start_pos + content_key.length();
            }
            /* find start position at body */
            start_pos = recv_data.find(separate_key, start_pos);
            if ( start_pos == std::string::npos ) {
                ret = -1;
            }
        }
        /* with HTTP header, bat not find body start position or http error */
        if ( ret < 0 ) {
            return ret;
        }
    }

    Int4 body_start_pos, body_length;
    if ( start_pos == 0 ){
        body_start_pos = 0;
    }
    else {
        body_start_pos = start_pos + separate_key.length();
    }
    //std::cout << "body_start       : " << body_start_pos << std::endl;

    /* find end position at body */
    //end_pos = recv_data.find(end_key, start_pos+separate_key.length());
    end_pos = recv_data.rfind(end_key);
    if ( end_pos == std::string::npos ){
        end_pos = recv_data.length();
    }
    else {
    }
    body_length = end_pos - body_start_pos;
    //std::cout << "end_pos          : " << end_pos << std::endl;

    /* set body data */
    body_data = recv_data.substr(body_start_pos,body_length);
    ret = body_length;

    //std::cout << "body_length      : " << body_length << std::endl;

    return ret;
}
