//
// 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 <unistd.h>

#include "lib_wrapper.h"
#include "common.h"
#include "utils.h"
#include "conf.h"
#include "atsend.h"
#include "pr_serial.h"

extern char tmp[MAXMAXLEN];

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

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

    return setupAtConnection(conn, fd);
}

int serialOpenInternal(_SerialConnection* cn, char* port)
{

    // get a file descriptor
    if ((cn->fileDescriptor = open(port, O_RDWR|O_NOCTTY|O_NDELAY/*|O_CLOEXEC fails to compile ? */)) < 0) {
        sprintf(tmp, "can not open %s", port);
        logger(L_ERR, tmp);
        printf("ERROR: can not open %s\n", port);
        cn->fileDescriptor = -1;
        return -1;
    }
    fcntl(cn->fileDescriptor,F_SETFD,FD_CLOEXEC);

    // check to see that the file is a terminal
    if (!isatty(cn->fileDescriptor)) {
        printf("ERROR: %s does not refer to a terminal\n", port);
        close(cn->fileDescriptor);
        cn->fileDescriptor = -1;
        return -1;
    }

    // get port attributes, store in oldterm
    if (tcgetattr(cn->fileDescriptor, &(cn->oldterm)) < 0) {
        printf("ERROR: Can not get port attributes (%s)\n", port);
        close(cn->fileDescriptor);
        cn->fileDescriptor = -1;
        return -1;
    }

    // get port flags, save in oldflags
    if ((cn->oldflags = fcntl(cn->fileDescriptor, F_GETFL)) < 0) {
        printf("ERROR: Can't get port flags (%s)\n", port);
        close(cn->fileDescriptor);
        cn->fileDescriptor = -1;
        return -1;
    }

    cn->portterm  = cn->oldterm;
    cn->portflags = cn->oldflags;

    cn->portterm.c_cflag = getBaudrate() | CRTSCTS | CS8 | CLOCAL | CREAD | O_NDELAY;
    if (!cn->rtscts) {
        cn->portterm.c_cflag &= ~CRTSCTS;
    }
    cn->portterm.c_iflag	= IGNPAR;
    cn->portterm.c_oflag	= 0;
    cn->portterm.c_lflag	= 0;
    cn->portterm.c_cc[VTIME]	= 0;
    cn->portterm.c_cc[VMIN]	= 0;

    tcflush(cn->fileDescriptor, TCIOFLUSH);

    if (tcsetattr(cn->fileDescriptor,TCSANOW,&(cn->portterm)) < 0) {
        printf("ERROR: Can't set port attributes (%s)\n", port);
        close(cn->fileDescriptor);
        cn->fileDescriptor = -1;
        return -1;
    }

    // set non-blocking/
    if (fcntl(cn->fileDescriptor, F_SETFL, (cn->portflags |= O_NONBLOCK)) < 0) {
        printf("ERROR: Can't set port flags (%s)\n", port);
        close(cn->fileDescriptor);
        cn->fileDescriptor = -1;
        return -1;
    }
    return 0;
}

int serialOpen(ConnectInfo* conn)
{
    if (conn->connectionData && ((_SerialConnection*) conn->connectionData)->fileDescriptor >= 0) {
        close(((_SerialConnection*) conn->connectionData)->fileDescriptor);
    }
    
    if (conn->connectionData) {
        free(conn->connectionData);
    }
    
    conn->connectionData = (_SerialConnection*) malloc(sizeof(_SerialConnection));
    _SerialConnection* cn = (_SerialConnection*) conn->connectionData;

    cn->fileDescriptor = -1;
    cn->useCallId      = BOOL_YES;
    cn->hasActiveCall  = BOOL_NO;
    cn->portflags      = 0;
    cn->oldflags       = 0;
    cn->rtscts         = 0;
    

    DEBUG2("[DS]: Serial Client mode. Use device %s", conn->portStr->str);
    if (serialOpenInternal(cn, conn->portStr->str) < 0) {
        logger(L_ERR,"[DS]: can not open serial port");
        conn->state = PEER_DISCONNECTED;
	    return EXIT_NOK;
    }
    
    conn->state = PEER_CONNECTED;
    return EXIT_OK;
}

//
// Then port will be closed from a forked child use 0
//
void serialClose(ConnectInfo* conn, int final)
{
    _SerialConnection* cn = (_SerialConnection*) conn->connectionData;
    if (!cn) {
        conn->state = PEER_DISCONNECTED;
        return;
    }
    int retval = 0;
    
    if (final) {
        logger(L_INF, "closeSerialPort");
    }
 
    if (cn->fileDescriptor < 0) { // already closed
        if (final) {
            logger(L_INF, "Already closed ?");
        }
    } else {
	    if (final) {
                // restore old settings
                if (tcsetattr(cn->fileDescriptor, TCSADRAIN, &(cn->oldterm)) < 0) {
        	    retval = -1;
                }

                if (fcntl(cn->fileDescriptor, F_SETFL, cn->oldflags) < 0) {
        	    retval = -1;
                }
	    }
	    retval = close(cn->fileDescriptor);
    }
    cn->fileDescriptor = -1;
    conn->state = PEER_DISCONNECTED;
    
    if (retval < 0 && final) {
        logger(L_ERR,"[DS]: Error on closing port\n");
    }
}

void serialReset(ConnectInfo* conn)
{
    serialClose(conn, 0);
}

boolean_t serialCheckActiveCall(ConnectInfo* conn)
{
    _SerialConnection* cn = (_SerialConnection*) conn->connectionData;
    
    if (!cn) {
        return BOOL_NO;
    }
    
    if (cn->hasActiveCall) {
    
        if (cn->useCallId) {
	    int fd = cn->fileDescriptor;
	    if (fd < 0) {
	        cn->hasActiveCall = BOOL_NO;
	    } else {
		char callerId[MAXLEN];
		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 serialHasActiveCall(ConnectInfo* conn)
{
    _SerialConnection* cn = (_SerialConnection*) conn->connectionData;
    return (cn ? cn->hasActiveCall : BOOL_NO);
}
