/*
 * scbus utils to switch card settings.
 *
 * Copyright (C) 2011 Otvos Attila oattila@onebithq.com
 *
 * This 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 2 of the License, or
 * (at your option) any later version.
 *
 * This 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include "rs485class.h"

#include "rs485.h"
#include "util.h"
#include <errno.h>
#include <stdio.h>

RS485Thread::RS485Thread() {
}

RS485Thread::RS485Thread(int baud, QString device) {
    olddeviceid=0;
    newdeviceid=0;
    if(!(rs485=init_rs485(baud,device.toStdString().c_str()))) {
        lastError=QString(strerror(errno));
    } else {
        if(!inited_rs485(rs485)) {
            lastError=QString(geterror_rs485(rs485));
            done_rs485(rs485);
            rs485=NULL;
        }
    }
    raw=0;
}

RS485Thread::~RS485Thread() {

    if(rs485)
        done_rs485(rs485);
}

void RS485Thread::stop() {
    runing=0;
    terminate();
    if(rs485)
        done_rs485(rs485);
    rs485=NULL;
}

void RS485Thread::run() {
    char buffer[8192];
    unsigned char param[32];
    unsigned char qdevice;
    char cmd[32];
    char* p;
    int n,c,b;
    RS485Status st;
    RS485configQueue qitem;

    qtrycnt=0;
    dtrycnt=0;
    c=0;
    runing=1;
    while(runing && rs485) {
        if(isdata_rs485(rs485)) {
            memset(buffer,0,sizeof(buffer));
            while((n=readline_rs485(rs485, buffer, sizeof(buffer)))) {
                if((n=process_line(buffer, param, sizeof(param)))) {
                    if(param[0]=='S') {
                        if(newdeviceid==param[1]) {
                            emit changeDeviceID(olddeviceid);
                            newdeviceid=0;
                            olddeviceid=0;
                            trycnt=0;
                        }
                        if(getDeviceQueue.count() && !trycnt) {
                            qdevice=getDeviceQueue.at(0);
                            if(dtrycnt && qdevice==param[1]) {
                                getDeviceQueue.removeAt(0);
                                dtrycnt=0;
                            }
                        }
                        st = status[param[1]];
                        st.input=param[2];
                        st.output=param[3];
                        status[param[1]]=st;
                        emit changeStatus(param[1]);
                    }
                    if(param[0]=='O') {
                        st = status[param[1]];
                        if(param[2]>=0 && param[2]<6) {
                            st.config[param[2]].act=true;
                            st.config[param[2]].device=param[3];
                            st.config[param[2]].input=param[4];
                            st.config[param[2]].type=param[5];
                            st.config[param[2]].par=param[6];
                            if(configQueue.count() && !trycnt) {
                                qitem=configQueue.at(0);
                                if(qtrycnt && qitem.device==param[1] && qitem.output==param[2] && qitem.config.device==param[3] && qitem.config.input==param[4] && qitem.config.type==param[5] && qitem.config.par==param[6]) {
                                    configQueue.removeAt(0);
                                    qtrycnt=0;
                                }
                            }
                        }
                        status[param[1]]=st;
                        emit changeConfig(param[1],param[2]);
                    }
                }
                if(n || raw) {
                    if((p=strchr(buffer,'\n'))) *p=0;
                    readedData << QString(buffer);
                    emit isData();
                }
                memset(buffer,0,sizeof(buffer));
            }
        }
        if(!c) {
            if(olddeviceid && newdeviceid && trycnt) {
                snprintf(cmd,sizeof(cmd),"\n*D%02X%02X\n",olddeviceid,newdeviceid);
                write_rs485(rs485, cmd, strlen(cmd));
                trycnt--;
            } else if(getDeviceQueue.count()) {
                if(!dtrycnt)
                    dtrycnt=5;
                qdevice=getDeviceQueue.at(0);
                snprintf(cmd,sizeof(cmd),"\n*G%02X\n", qdevice);
                write_rs485(rs485, cmd, strlen(cmd));
                dtrycnt--;
                if(!dtrycnt)
                    getDeviceQueue.removeAt(0);
            } else if(configQueue.count()) {
                if(!qtrycnt)
                    qtrycnt=5;
                qitem=configQueue.at(0);
                snprintf(cmd,sizeof(cmd),"\n*L%02X%02X%02X%02X%02X%02X\n", qitem.device, qitem.output, qitem.config.device, qitem.config.input, qitem.config.type, qitem.config.par);
                write_rs485(rs485, cmd, strlen(cmd));
                qtrycnt--;
                if(!qtrycnt)
                    configQueue.removeAt(0);
            } else {
                for(QMap<int, RS485Status>::iterator it = status.begin();it!=status.end();it++) {
                    b=0;
                    for(int c=0;c<6;c++) {
                        if(!(status[it.key()].config[c].act)) {
                            int k = it.key();
                            snprintf(cmd,sizeof(cmd),"\n*Q%02X%02X\n",k,c);
                            write_rs485(rs485, cmd, strlen(cmd));
                            b=1;
                            break;
                        }
                    }
                    if(b)
                        break;
                }
            }
        }
        c++;
        if(c>200)
            c=0;
        usleep(1000);
    }
}

QStringList RS485Thread::getData(void) {
    QStringList ret;

    ret = readedData;
    readedData.clear();
    return ret;
}

RS485Status RS485Thread::getStatus(int deviceid) {
    return status[deviceid];
}

void RS485Thread::setDeviceID(int olddeviceid, int newdeviceid) {
    this->olddeviceid=olddeviceid;
    this->newdeviceid=newdeviceid;
    trycnt=5;
}

void RS485Thread::getDeviceID(int deviceid) {
    int n;

    for(n=0;n<getDeviceQueue.count();n++) {
        if(deviceid==getDeviceQueue.at(n)) {
            getDeviceQueue.removeAt(n);
            break;
        }
    }
    getDeviceQueue.append(deviceid);
}

void RS485Thread::setConfig(int deviceid, int output, RS485Config config) {
    RS485configQueue citem;
    RS485configQueue qitem;
    int n;

    citem.device=deviceid;
    citem.output=output;
    citem.config=config;
    for(n=0;n<configQueue.count();n++) {
        qitem=configQueue.at(n);
        if(qitem.device==citem.device && qitem.output==citem.output) {
            configQueue.removeAt(n);
            break;
        }
    }
    configQueue.append(citem);
}

void RS485Thread::enableRaw(bool e) {
    if(e)
        raw=1;
    else
        raw=0;
}
