// © Copyright The University of New South Wales 2018
//
// 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, see http://www.gnu.org/licenses/.
//


#include "maxlib.h"

void maxclient_setconfig(int *config);
void maxclient_listen(board_t *board);

// restart listening to board after pausing (unchanged configuration)
void maxclient_restart();

// stop communication with the board via the serial port and disconnect
void maxclient_stop(board_t *board);

// pause (ignore) board updates.
void maxclient_pause();

void maxclient_toggleListener(board_t *board);
void maxclient_listenerOn(void);
void maxclient_listenerOff(void);
void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes);


bool listenOn = false;

// continuously listen to board and update info_pins values
void maxclient_listen(board_t *board) {
    listenOn = true;
    while(listenOn) {
        firmataclient_onidle(board);
    }
}

// connect to board via a serial port, init board to requested configuration
// and start listening to updates from the board
board_t* maxclient_newboard(char *name, int baud, char *err) {
    
    if( !name || !err) {
        err = "invalid arguments";
        return NULL;
    }
    
    // allocate memory for new board
    board_t *board = (board_t*)malloc(sizeof(board_t));
    if(board == NULL) {
        strcpy(err, "firmataclient_initport: couldn't allocate new memory");
        return NULL;
    }
    
    // open connection to the board, and request REPORT_FIRMATA
    int result = firmataclient_initport(board, name, baud);
    if (result) {
        sprintf(err, "firmataclient_initport: couldn't open port: %s", board->err_msg);
        return board;
    }
    
    return board;
}

// connect to board via a serial port, init board to requested configuration
// and start listening to updates from the board
int maxclient_connect(board_t *board, int len_config, int *pins, int *modes, int func) {
    
    if (board == NULL) return -1;
    
    // request firmware report
    firmataclient_requestfirmware(board);
    
    // wait for board to be ready for communication
    firmataclient_waitForBoardReady(board);
    
    // if it's a READ (boardin) connection, then
    // launch infine listener thread
    if(func == IN_BOARD) {
        // start new thread for (continuous) listening to port updates
        // should be *before* requesting FIRMATA_REPORT
        int err = firmataclient_launchtlisteninghread(board);
        if(err) {
            sprintf(board->err_msg, "firmataclient_launchtlisteninghread: %s", board->err_msg);
            return err;
        }
    }
    
    // now that the board is ready for communication
    // set the correct pin configuration as requested (if supported),
    // or default is MODE_OFF (255)
    maxclient_configurePins(board, len_config, pins, modes);
    
    // now the max object is ready for work.
    return 0;
}

int maxclient_disconnect(board_t *board) {
    
    if (board == NULL) return -1;
    
    // for a READ (boardin) connection, kill listener thread
    if(firmataclient_destroylisteningthread(board) == -1) {
        sprintf(board->err_msg, "maxclient_disconnect: %s", board->err_msg);
        return -1;
    }
    else
        return 0;
}

// update board with requested configuration
void maxclient_configurePins(board_t *board, int len_config, int *pins, int *modes) {
    
    // note: assumes that the board is ready for communication.
    
    // set the correct pin configuration as requested (if supported),
    // or default is MODE_OFF (255)
    if(len_config > 128) len_config = 128;
    for(int i=0; i<len_config; ++i) {
        firmataclient_changemode(board, pins[i], modes[i]);
    }
}

// pause the listener from receiving incoming board messages
void maxclient_pause(void) {
    listenOn = false;
}

// restart listening to board after pausing (unchanged configuration) ???
void maxclient_restart(void) {
    listenOn = true;
}

// stop communication with the board via the serial port and disconnect
void maxclient_stop(board_t *board) {

    listenOn = false;

    // if no further objects are connected to board,
    // then close the port
    firmataclient_closeport(board);
}

// caps the values to range [0-180] degree angles.
void maxclient_moveServo(board_t *board, int pin, int newVal) {
    // check pin index
    if (pin < 0 || pin > TOTAL_IN_PINS) return;
    if(newVal > 180) newVal = 180;
    if(newVal < 0) newVal = 0;
    // update the board
    firmataclient_sendnewvalue(board, pin, newVal);
}

void maxclient_movePwm(board_t *board, int pin, int newVal) {
    // check pin index
    if (pin < 0 || pin > TOTAL_IN_PINS) return;
    if(newVal > 255) newVal = 255;
    if(newVal < 0) newVal = 0;
    // update the board
    firmataclient_sendnewvalue(board, pin, newVal);
}

void maxclient_toggleDigital(board_t *board, int pin, int newVal) {
    // check pin index
    if (pin < 0 || pin > TOTAL_IN_PINS) return;
    // update the board
    firmataclient_senddigitalvalue(board, pin, newVal);
}

void maxclient_changeMode(board_t *board, int pin, int mode) {
    
    // todo: check the mode is supported..
    
    // update the board
    firmataclient_changemode(board, pin, mode);
}

void maxclient_toggleListener(board_t *board) {
    // listenOn = !listenOn;
    if(listenOn) {
        //pause
        listenOn = false;
    }
    else {
        listenOn = true;
        maxclient_listen(board);
    }
}

void maxclient_listenerOn(void) {
        listenOn = true;
}

void maxclient_listenerOff(void) {
        listenOn = false;
}

void maxclient_getInputValues(board_t *board, long *indexArray, long *valuesArray, int pinsCount) {
    firmataclient_getinputvalues(board, indexArray, valuesArray, pinsCount);
}

int maxclient_shutdown(board_t *board) {
    return firmataclient_closeport(board);
}

int maxclient_verifyconfig(board_t *board, int len, int *pins, int *modes) {
    
    if(board == NULL || pins== NULL || modes == NULL) return -1;
    
    // compare pins modes between the current (connected) board, and the board that wants to open a connection to the same port (reader vs. writer)
    for (int i=0; i<len; ++i) {
        int board_mode = board->pin_info[pins[i]].mode;
        if(board_mode != 255 && board_mode != modes[i]) {
            sprintf(board->err_msg, "pin %d conflict between connected boards", pins[i]);
            return -1;
        }
    }
    
    // no conflicts found.
    return 0;
}


void maxclient_freemgr(board_mgr_t *mgr) {
    if (!mgr) return;
    mgr->board = NULL;
    mgr->board_in = NULL;
    mgr->board_out = NULL;
    free(mgr);
}




















