//
// 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
//

//
// AT+CKPD commands handling
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>

#include "parse.h"
#include "utils.h"
#include "btio.h"
#include "conf.h"
#include "dispatcher.h"
#include "peer.h"
#include "pr_rfcomm.h"
#include "pr_serial.h"

#define PAUSE_TIME 1

extern char tmp[MAXMAXLEN];

static int  useQuestionInCLCC = 0;
char callerId[MAXLEN];

//
// max should be >= 100
//
int writeAtCommand(int fd,
                   const char* command,
                   char* answer,
                   int   max,
                   int   timeout,
                   char* expect)
{
    int count=0;
    int readcount;
    char tmp2[100];
    int timeoutcounter=0;
    int found=0;

    if (fd < 0) {
        return 0;
    }

    logger(L_DBG,command);
    DEBUG2("writeAtCommand command >%s<", command);
    if (expect != NULL) {
        DEBUG2("writeAtCommand skip till >%s<", expect);
    }

    // send command
    if (command && command[0]) {
        write(fd,command,strlen(command));
        tcdrain(fd);
    }

    if (max == 0) {
        return 0;
    }

    answer[0]=0;
    do {
        // try to read some bytes.
        usleep(100000);
        
	//write(1,".",1);
        timeoutcounter++;

        // read data
        readcount=read(fd,tmp2,sizeof(tmp2)-1);
        if (readcount<0) {
            readcount=0;
        }
        tmp2[readcount]=0;

        // add read bytes to the output buffer
        if (readcount) {
	    //DEBUG2("writeAtCommand got reply >%s<", tmp2);
	
            strcat(answer,tmp2);
            count+=readcount;

            // if we have more time to read, check if we got already the expected string
            if ((timeoutcounter<timeout) && (found==0)) {

                // check if it's the expected answer
                if (strstr(answer,"OK\r") || strstr(answer,"ERR")) {
		    if (expect == NULL) {
                        found=1;
		    } else {
		        if (strstr(answer,"ERR")) {  // got error, so nothing to expect
			    expect = NULL;
			}
		    }
                }

                if (expect && expect[0]) {
                    if (strstr(answer,expect)) {
                        sprintf(tmp, "Got expected %s (iteration %d)", answer, timeoutcounter);
                        logger(L_DBG, tmp);
                        found=1;
                    }
                }

                // if found then set timoutcounter to read only 0.1s after that and not more
                if (found) {
                    //timeoutcounter=timeout-1;
                    break;
                }
            }
        }
    }
    // repeat until timout
    while (timeoutcounter<timeout && count < max);

    if (getLog()) {
        char *a2 = answer;

        while (a2[0] == '\r' || a2[0] == '\n')  {
            a2++;
        }
        logger(L_DBG,a2);
    }

    return count;
}

//
// Phones can send commad back as reply, so it needs to skip it
// 
static void sendSyncCommand(int fd, const char *bytes, char *answer, int size, char *expect)
{
    DEBUG2("sendSyncCommand >%s<\n", bytes);

    // initial timeout was 5
    writeAtCommand(fd, bytes, answer, size, 40, expect);
}

int sendCMER(int fd, int onOff)
{
    char answer[100];
    char cmd[64];

    char * cmer = getAT_CMER(onOff);
    if (cmer == NULL) {
        return EXIT_NOK;
    }

    strcpy(cmd,cmer);
    free(cmer);

    strcat(cmd,"\r");
    //sendSyncCommand(cmd, answer, sizeof(answer), NULL);
    writeAtCommand (fd, cmd, answer, sizeof(answer), 10, NULL);

    if(!IS_OK(answer)) {
        DEBUG2("ERROR: AT_CMER SET >%s< -> %s\n", cmd, answer);
        return EXIT_NOK;
    }
    return EXIT_OK;
}

// Send AT+CKPD command
static int sendCKPD(int fd, char *key)
{
    char answer[1024];
    char ckpd[MAXCKPDLEN];

    if (key == NULL || key[0] == '\0') {
        logger(L_DBG,"Empty symbol in sendCKPD()");
        return EXIT_NOK;
    }
    DEBUG2("sendCKPD >%s<", key);

    sprintf(ckpd,"%s\"%s\"\r", DEF_AT_CKPD, key);

    int model = getModel();

    if (model == MODEL_SE || model == MODEL_SIEMENS) {  // SE-K700, Siemens-S55
        // If we send AT+CKPD=X
        // SE phone will replay
        // OK
        // +CKEV: X,1
        // +CKEV: X,0
        // And we need to filter out that CKEVs
        char expect[16];
        strcpy(expect, DEF_CKEV);
        strcat(expect, " ");
        if (model == MODEL_SIEMENS) {
            strcat(expect, "\"");
        }
        strcat(expect, key);
        if (model == MODEL_SIEMENS) {
            strcat(expect, "\"");
        }
        strcat(expect, ",0");  // Wait button up event

        sendSyncCommand(fd, ckpd, answer, sizeof(answer), expect);
        //DEBUG2("Expect >%s<", expect);
        //DEBUG2("Got all >%s<", answer);

        // Filter out button up
        char *ptr = strstr(answer, expect);
        if(ptr == NULL) {
            logger(L_DBG,"cant find button up event");
        } else {
            int z;
            int l = strlen(expect);
            for (z=0; z<l; z++) {
                *ptr = '\r';
                ptr++;
            }
        }

        // Filter out button down
        strcpy(expect, DEF_CKEV);
        strcat(expect, " ");
        if (model == MODEL_SIEMENS) {
            strcat(expect, "\"");
        }
        strcat(expect, key);
        if (model == MODEL_SIEMENS) {
            strcat(expect, "\"");
        }
        strcat(expect, ",1");
        ptr = strstr(answer, expect);
        if(ptr == NULL) {
            logger(L_DBG,"can not find button down event");
        } else {
            int z;
            int l = strlen(expect);
            for (z=0; z<l; z++) {
                *ptr = '\r';
                ptr++;
            }
        }
        DEBUG2("Filtered >%s<", answer);

    } else {
        sendSyncCommand(fd, ckpd, answer, sizeof(answer), NULL);
    }

    parseCommand(PEER_ANY, answer);

    return EXIT_OK;
}

//
// Should be used in AT mode only
//
int sendSeq(int fd, const char *seq)
{
    if (getUseScreen() == 0 || seq == NULL) {
        return EXIT_OK;
    }

    DEBUG2("sendSeq() >%s<", seq);

    char* copy = strdup(seq);

    char *bufPtr = NULL;
    char *ckpd = strtok_r(copy," ",&bufPtr);

    while (ckpd != NULL) {
        //DEBUG2("NEXT sendSeq()->sendCKPD()>%s<", ckpd);

        if (strcmp(ckpd, PAUSE_STR)==0) {
            sleep(PAUSE_TIME);	// Just wait a bit
        } else {
            sendCKPD(fd, ckpd);
        }
        ckpd = strtok_r(NULL," ",&bufPtr);
    }
    free(copy);

    return EXIT_OK;
}

static void parse_CLCC(char *clcc, char* dest)
{
    // There are could be more than one call at time ... but here we will get then one-by-one
    // +CLCC: 1,,4,0,0,"<phone number here>",129,
    char *start ,*ptr;
    int commaCount = 0;

    ptr = clcc;
    while(ptr && commaCount < 5) {
        if (*ptr == ',') {
            commaCount++;
        }
        ptr++;
    }

    if (ptr == NULL) { // Call was finished
        strcpy(dest, "FINISHED");
	return;
    }
    if (ptr && *ptr == '"') {
        ptr++;
    }
    start = ptr;
    while(ptr && *ptr != '"' &&  *ptr != ',') {
        ptr++;
    }
    if (ptr) {
        *ptr = '\0';
    }

    if(start) {
        DEBUG2("parse_CLCC >%s<", start);
    } else {
        DEBUG2("parse_CLCC >NULL callerID<");
        strcpy(dest, "NO CALLER ID");
	return;
    }
    
    strcpy(dest, start);
    return;
}

int getClip(int fd, char* CallerID)
{
    char clcc[MTEXTLEN];
    char answer[1024];
    char *ptr;
    int   tryAnyway = 0;

    answer[0] = '\0';

    logger(L_DBG, "getClip()");

    // Some phones uses AT+CLCC?, some just AT+CLCC (Sagem, Siemens-S55, SE-K750)

    if (useQuestionInCLCC == 0) {
        //logger(L_DBG, "getClip() send AT+CLCC");

        strcpy(clcc,DEF_AT_CLCC);
        strcat(clcc,"\r");

        sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL);

        if ((ptr = strstr(answer,DEF_CLCC)) != NULL) {
            parse_CLCC(ptr,CallerID);
            return EXIT_EXACT;
        } else if (strstr(answer,"OK") != NULL) {
            return EXIT_OK;
        } else if (strstr(answer,"ERR") != NULL) {
            logger(L_WARN, "Got ERROR on AT+CLCC. Will try AT+CLCC?");
            useQuestionInCLCC = 1;      // Ask phone again; but will never ask AT+CLCC more
        } else {
            tryAnyway = 1;              // Ask phone again; to be sure
        }
    }

    if (useQuestionInCLCC == 1 || tryAnyway == 1) {

        tryAnyway = 0;

        //logger(L_DBG, "getClip() send AT+CLCC?");

        strcpy(clcc,DEF_AT_CLCC);
        strcat(clcc,"?");
        strcat(clcc,"\r");

        sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL);

        if ((ptr = strstr(answer,DEF_CLCC)) != NULL) {
            parse_CLCC(ptr,CallerID);
            return EXIT_EXACT;
        } else if (strstr(answer,"OK") != NULL) {
            return EXIT_OK;
        } else if (strstr(answer,"ERR") != NULL) {
            logger(L_ERR, "Got ERROR on AT+CLCC?");
            return EXIT_NOK;
        }
    }

    DEBUG2("sendClip() unappropriate answer: >%s<", answer);

    return EXIT_NOK;
}

// CLIENT_RFCOMM / CLIENT_AT
int setupAtConnection(ConnectInfo* peer, int fd)
{   
    char answer[1024];
    char cmd[32];
    
    answer[0] = '\0';
    sendSyncCommand(fd, "ATZ\r", answer, sizeof(answer), NULL);
    //sendSyncCommand("AT\r", answer, sizeof(answer), NULL);
    // Do not care - it can fail after reconnect
    //if(!IS_OK(answer)) {
    //    ERROR2("AT -> %s\n", answer)
    //    return -1;
    //}

    sendSyncCommand(fd, "ATE0\r", answer, sizeof(answer), NULL);
    if(!IS_OK(answer)) {
        ERROR2("ATE0 -> %s\n", answer);
    }
    
    char* ptr = getCharset();
    if (ptr) {
        sprintf(cmd,"%s\"%s\"\r", DEF_AT_CSCS, ptr);
        sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
        if(!IS_OK(answer)) {
            ERROR2("Can't set charset to %s\n", ptr);
        }
        free(ptr);
    }

    // Determine model
    sprintf(cmd,"%s\r", DEF_AT_CGMI);
    sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);

    //Set model in conf. data
    setModel(answer);

    if (getModel() == MODEL_MOTOROLA) {	// Motorola RIZR Z3 needs to set MODE=2 to allow AT+CLIP command
        // do not care about responce
        sendSyncCommand(fd, "AT+MODE=2\r", answer, sizeof(answer), NULL);
    }

    sprintf(cmd,"%s\r", DEF_AT_CLIP);
    sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
    if(!IS_OK(answer)) {
        ERROR2("Can't set CLIP ON\n");
    }


    int ret = sendCMER(fd, CMER_ON);
    if(ret != EXIT_OK) {
        ERROR2("fails in set event reporting on");
        return -1;
    }

    // Siemens S55 needs additional AT+CMEC=2 to make AT+CKPD works
    // not sure about other Siemens phones
    if (getModel() == MODEL_SIEMENS) {
        sprintf(cmd,"%s\r", DEF_AT_CMEC);
        sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL);
        if(!IS_OK(answer)) {
            ERROR2("ON AT+CMEC\n");
        }
    }
    
    // Will set global callerId [MAXLEN];
    ret = getClip(fd, callerId);
    if(ret == EXIT_NOK) {       // Got ERROR on AT+CLCC; probably phone does not supports this command
        printf("ERROR: fails in getClip\n");
        
	if (peer->mode == CLIENT_RFCOMM) {
	    _RfcommConnection* cn = (_RfcommConnection*) peer->connectionData;
            if (cn) {
                cn->useCallId = BOOL_NO;
            }
        } else if (peer->mode == CLIENT_AT) {
	    _SerialConnection* cn = (_SerialConnection*) peer->connectionData;
            if (cn) {
                cn->useCallId = BOOL_NO;
            }
	}
    }
    
    return 1;
}

//
// rfcomm peer reader
// in case of incoming call try to get caller ID
//
int atRead(ConnectInfo* peer, char* buffer, int max)
{
    if (! (peer->mode == CLIENT_RFCOMM || peer->mode == CLIENT_AT)) {
        ERROR2("[DS]: atRead() peer type mismatch");
	return 0;
    }
    
    if (!peer->connectionData) return EXIT_NOK;
    
    _RfcommConnection* cnr = NULL;
    _SerialConnection* cns = NULL;
    int fd = -1;
    boolean_t useCallID = BOOL_NO;
    boolean_t hasCall   = BOOL_NO;
    
    if (peer->mode == CLIENT_RFCOMM) {
        cnr = (_RfcommConnection*) peer->connectionData;
	fd  = cnr->fileDescriptor;
	useCallID = cnr->useCallId;
	hasCall   = cnr->hasActiveCall;
    } else if (peer->mode == CLIENT_AT) {
        cns = (_SerialConnection*) peer->connectionData;
	fd  = cns->fileDescriptor;
	useCallID = cns->useCallId;
	hasCall   = cns->hasActiveCall;
    }
    
    if (fd < 0) {
        ERROR2("[DS]: atRead() not connected");
	return 0;
    }
    
    int nbytes = read(fd, buffer, max);
    if (nbytes < 0)  { // Read error
        ERROR2("[DS]: atRead() error %d",errno);
        errnoDebug("[DS]: atRead() read ",errno);  // testing debug
    } else if (nbytes == 0) {
        DEBUG2("[DS]: atRead() EOF");
    } else {
        
	buffer[nbytes] = '\0';
	
        if (strstr(buffer, DEF_RING) != NULL && !hasCall) {	  // Incoming call
            logger(L_INF,"[DS]: Incoming call");  // This event sent periodically until used answered a call
	    
	    if (cnr) {
	       cnr->hasActiveCall = BOOL_YES;
	    } else if (cns) {
	       cns->hasActiveCall = BOOL_YES;
	    }
	    
	    if (useCallID) {
	    
	        char callerId[MAXLEN];
	        int ret = getClip(fd, callerId);
		
		if (ret == EXIT_EXACT) { // Active call exists, got ID
		    strcat(buffer, "Msg:InCall(,");
		    strcat(buffer, callerId);
		    strcat(buffer, ")\r");
		    nbytes = strlen(buffer);
		}
	    } else {
	        strcat(buffer, "Msg:InCall(,)\r");
		nbytes =  strlen(buffer);
	    }
	}
    }
    
    //DEBUG2("[DS]: atRead() got >%s< %d", buffer, nbytes);
    return nbytes;
}


