//
// 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <termios.h>
#include <unistd.h>

#ifdef USE_BLUEZ
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#endif

#ifdef USE_BT_FBSD
#include <bluetooth.h>
#include <sdp.h>
#include <err.h>
#endif

#include "common.h"
#include "utils.h"
#include "conf.h"
#include "lib_wrapper.h"
#include "dispatcher.h"
#include "atsend.h"
#include "pr_rfcomm.h"

extern char tmp[MAXMAXLEN];
extern boolean_t stillRun;

//
// RFCOMM socket handling
//

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

int rfcommSetup(ConnectInfo* conn)
{
    int fd = rfcommFD(conn);
    if (fd < 0) {
        DEBUG2("rfcommSetup() wrong descriptor");
        return -1;
    }

    return setupAtConnection(conn, fd);
}

int rfcommConnect(ConnectInfo* conn)
{
    DEBUG2("[DS]: Serial Client mode. Use device %s : %d", conn->portStr->str, conn->port);
    
    const char *addr = conn->portStr->str;
    int channel = conn->port;
    
    if (conn->connectionData && ((_RfcommConnection*) conn->connectionData)->fileDescriptor >= 0) {
        rfcommClose(conn,1);
    }
    
    if (conn->connectionData) {
        free(conn->connectionData);
    }
    
    conn->connectionData = (_RfcommConnection*) malloc(sizeof(_RfcommConnection));
    _RfcommConnection* cn = (_RfcommConnection*) conn->connectionData;

    cn->fileDescriptor = -1;
    cn->useCallId      = BOOL_YES;
    cn->hasActiveCall  = BOOL_NO;
    
    #ifdef USE_BLUEZ

    struct sockaddr_rc bt_addr = { 0 };

    bt_addr.rc_family  = AF_BLUETOOTH;
    bt_addr.rc_channel = (uint8_t) channel;
    str2ba(addr, &(bt_addr.rc_bdaddr));

    if ((cn->fileDescriptor = socket(AF_BLUETOOTH, SOCK_STREAM|SOCK_CLOEXEC, BTPROTO_RFCOMM)) < 0) {
        logger(L_ERR, "opening BT socket");
        printf("ERROR: opening BT socket\n");
        cn->fileDescriptor = -1;
        return -1;
    }

    connect(cn->fileDescriptor, (struct sockaddr *) &bt_addr, sizeof(bt_addr));

    #endif
    
    if (cn->fileDescriptor < 0) {
        logger(L_ERR,"[DS]: can not open RFCOMM channel");
	    conn->state = PEER_DISCONNECTED;
        return EXIT_NOK;
    }
    
    conn->state = PEER_CONNECTED;
    return EXIT_OK;
}

void rfcommClose(ConnectInfo* conn, int final)
{
    if (final == 0) return;
    
    _RfcommConnection* cn = (_RfcommConnection*) conn->connectionData;
    if (cn) {
	if (cn->fileDescriptor >= 0) {
            shutdown(cn->fileDescriptor, SHUT_RDWR);
            cn->fileDescriptor = -1;
	}
	free(cn);
    }
    conn->connectionData = NULL;
    conn->state = PEER_DISCONNECTED;
}

void rfcommReset(ConnectInfo* conn)
{
    rfcommClose(conn, 0);
}

int rfcommWrite(ConnectInfo* conn, dMessage* msg)
{
    //logger(L_DBG, "rfcommWrite");
    if (!msg) {
        return EXIT_OK;
    }
    const char* command = msg->value;
    int count           = msg->size;
    
    if (!command || count <= 0) {
        return EXIT_OK;
    }
    
    _RfcommConnection* cn = (_RfcommConnection*) conn->connectionData;
    if (!cn) return EXIT_NOK;

    // send command
    if (cn->fileDescriptor >= 0) {

        memset(tmp, 0, MAXMAXLEN);
        strcat(tmp, "rfcommWrite ");

        int logSz = (count > 256 ? 255 : count);

        // it is possible to get binary data here
        memcpy(tmp, command, logSz); // Do not dump long commands
        tmp[logSz] = '\0';
        logger(L_DBG, tmp);

        sprintf(tmp, "rfcommWrite %d bytes", count);
        logger(L_INF, tmp);

        int n = write(cn->fileDescriptor,command,count);
        if (n < 0) {
            logger(L_ERR, "error writing to rfcomm socket");
            return EXIT_NOK;
        }
        return EXIT_OK;
    } else {
        logger(L_ERR, "error writing to rfcomm socket: already closed");
    }
    return EXIT_NOK;
}

boolean_t rfcommCheckActiveCall(ConnectInfo* conn)
{
    _RfcommConnection* cn = (_RfcommConnection*) conn->connectionData;
    
    if (!cn) {
        return BOOL_NO;
    }
    
    if (cn->hasActiveCall) {
    
        if (cn->useCallId) {
	    char callerId[MAXLEN];
	    
	    int fd = cn->fileDescriptor;
	    if (fd < 0) {
	        cn->hasActiveCall = BOOL_NO;
	    } else {
	        int ret = getClip(fd, callerId);
		if (ret != EXIT_EXACT) { // Seems the call was finished
                     cn->hasActiveCall = BOOL_NO;
		}
	    }
	} else {
	    // no way to check, just drop it
	    cn->hasActiveCall = BOOL_NO;
	}
    }
    
    return cn->hasActiveCall;
}

boolean_t rfcommHasActiveCall(ConnectInfo* conn)
{
    _RfcommConnection* cn = (_RfcommConnection*) conn->connectionData;
    return (cn ? cn->hasActiveCall : BOOL_NO);
}
