//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "common.h"
#include "lib_wrapper.h"
#include "executor.h"
#include "utils.h"
#include "peer.h"

//
// Frontend support (ganyremote/kanyremote)
//


extern char tmp[MAXMAXLEN];
static char frontEndBuf[MAXLEN];

typedef struct _FrontendConnection_ {
    int           fileDescriptor;
} _FrontendConnection;

int feFD(ConnectInfo* conn)
{
    _FrontendConnection* cn = (_FrontendConnection*) conn->connectionData;
    return (cn ? cn->fileDescriptor : -1);
}

static int frontendOpenInternal(_FrontendConnection* conn, int portno)
{
    int flags;
    sprintf(tmp, "[DS]: connect to frontend >%d<",portno );
    logger(L_DBG, tmp);

    struct sockaddr_in serv_addr;
    struct hostent     *server;

    conn->fileDescriptor = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0);
    if (conn->fileDescriptor < 0) {
        logger(L_ERR, "[DS]: opening socket for frontend");
        return EXIT_NOK;
    }

    if (-1 == (flags = fcntl(conn->fileDescriptor, F_GETFL, 0))) {
        flags = 0;
    }
    fcntl(conn->fileDescriptor, F_SETFL, flags | O_NONBLOCK);

    server = gethostbyname("localhost");
    if (server == NULL) {
        return EXIT_NOK;
    }

    memset((void *) &serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(portno);

    int ret = connect(conn->fileDescriptor,(const struct sockaddr *) &serv_addr, sizeof(serv_addr));
    if (ret != 0 && errno != EINPROGRESS) {
        sprintf(tmp, "[DS]: connect socket for frontend (%d) errno = %d", ret, errno);
        logger(L_ERR, tmp);
        return EXIT_NOK;
    }

    return EXIT_OK;
}

int feOpen(ConnectInfo* conn)
{
    DEBUG2("[DS]: Open front end connection");
    if (conn->connectionData && ((_FrontendConnection*) conn->connectionData)->fileDescriptor >= 0) {
        close(((_FrontendConnection*) conn->connectionData)->fileDescriptor);
    }
    
    if (conn->connectionData) {
        free(conn->connectionData);
    }
    
    conn->connectionData = (_FrontendConnection*) malloc(sizeof(_FrontendConnection));
    _FrontendConnection* cn = (_FrontendConnection*) conn->connectionData;

    cn->fileDescriptor = -1;

    DEBUG2("[DS]: Open front end connection. Use port %d", conn->port);
    if (frontendOpenInternal(conn->connectionData, conn->port) != EXIT_OK) {
        logger(L_ERR,"[DS]: can not open front end port");
        conn->state = PEER_DISCONNECTED;
	    return EXIT_NOK;
    }
    
    conn->state = PEER_CONNECTED;
    return EXIT_OK;
}

void feClose(ConnectInfo* conn, int final)
{
    _FrontendConnection* cn = (_FrontendConnection*) conn->connectionData;
    if (cn) {
        if (cn->fileDescriptor >= 0) {
            close(cn->fileDescriptor);
        }
        cn->fileDescriptor = -1;
    }
    conn->state = PEER_DISCONNECTED;
}

void feReset(ConnectInfo* conn)
{
    feClose(conn, 0);
}

void feWrite(ConnectInfo* conn, const char* buf)
{
    _FrontendConnection* cn = (_FrontendConnection*) conn->connectionData;
    if (cn && cn->fileDescriptor >= 0) {
        int n = write(cn->fileDescriptor, buf, strlen(buf));
        if (n < 0) {
            logger(L_ERR, "[DS]: Error writing to frontend");
        }
    }
}

static int readFromFrontEnd(int fd, char *buf, int size)
{
    //logger(L_INF, "[DS]: readFromFrontEnd");
    if (buf == NULL) {
        return EXIT_OK;
    }
    bzero(buf,size);
    int n = read(fd,buf,size-1);
    if (n < 0) {
        //logger(L_ERR, "[DS]: Error reading from socket");
        return EXIT_NOK;
    } else {
        *(buf+n) = '\0';
        if (n > 0) {
            sprintf(tmp, "[DS]: Got from frontend (%d) >%s<", n, buf);
            logger(L_DBG, tmp);

            int i = 0;
            int k = 0;
            for (i=0; i<n; i++) {
                if (*(buf+i) != '\0') {
                    if( i > k) {
                        *(buf+k) = *(buf+i);
                    }
                    k++;
                }
            }
            *(buf+k) = '\0';
            sprintf(tmp, "[DS]: Got from frontend (%d) >%s<", n, buf);
            logger(L_DBG, tmp);
        }
    }

    return EXIT_OK;
}

int feRead(int fd)
{
    // Verify commands from front-end
    int ret = readFromFrontEnd(fd, frontEndBuf, MAXLEN);
    if (ret == EXIT_OK) {
        if (frontEndBuf[0] != '\0') {
            DEBUG2("[DS]: feRead() >%s<", frontEndBuf);

            eMessage* em = (eMessage*) malloc(sizeof(eMessage));
            em->peer  = 0;
            em->type  = EM_STRING;
            em->value = strdup(frontEndBuf);

            sendToExecutor(em);
            
            return 1;
        }
    }
    return 0;
}

