
using namespace std;

class dcPorts
{
    public:
        dcPorts();
        void startupDCports();
        void manuallySwitchDC(short pinID);
        void resumeAutoMode(short pinID);  // return DC port back to auto mode
        void dcOn(short pinID, short source);  // turn dc on
        void dcOff(short pinID, short source);  // turn dc off
        void sortDcWidget(short sortID);  // NOT USING - sort DC ports from frontend
        void changeDcSortID(short v);
        void compileDcJson();  // compile data to be sent to front end
        void dcResponse(short pinID);  // needed to send response back to front end after manually switching 
        short getStatus(short pinID);  // get DC port status
        short getTotalPorts();  // get total dc ports
        short getPinID(short arrayLoc);  // get DC pinID
    private:
        short dcPinID[40] = {};  // unique pinID
        short dcPin[40] = {};  // gpio pin number
        short dcDriverID[40] = {};  // driverID
        short dcInvert[40] = {};  // track if logic is inverted
        short dcManual[40] = {};  // track if DC port is in manual mode
        short dcManualStatus[40] = {};  // track DC port status during manual mode so we can save to database
        short dcStatus[40] = {};  // DC status
        short dcTotal = 0;  // total DC ports found
        short dcSortID = 0;  // sortID for widget - used so we can fill arrays in same order
};
dcPorts::dcPorts(){}

short dcPorts::getStatus(short pinID) {  // get DC port status
    // std::cout << "getStatus -------- " << pinID << std::endl;
    short arrayID = 0;
    for(short a=0; a < dcTotal; a++) {  //
        if (dcPinID[a] == pinID) {arrayID = a; break;}
    }
    // std::cout << "dcStatus[arrayID] = " << dcStatus[arrayID] << std::endl;
    return dcStatus[arrayID];
}

short dcPorts::getTotalPorts() {  // get total dc ports
    return dcTotal;
}

short dcPorts::getPinID(short arrayLoc) {  // get DC pinID
    return dcPinID[arrayLoc];
}

void dcPorts::changeDcSortID(short v) {dcSortID = v;}  // set dcSortID

void dcPorts::dcResponse(short pinID) {  // needed to send response back to front end after manually switching
    for(short a=0; a < dcTotal; a++) {  // search through all dc ports
        if (dcPinID[a] == pinID) {  // when match is found get status for dc port
            outgoingDataString = "{\"dc\":[";
            outgoingDataString += "{\"id\":\"";
            outgoingDataString += to_string(pinID);
            outgoingDataString += "\",\"v\":\"";            
            outgoingDataString += to_string(dcStatus[a]);
            outgoingDataString += "\"}";
            outgoingDataString += "]}";
        }
    }
}

void dcPorts::compileDcJson() {  // compile data to be sent to front end
    for(short a=0; a < dcTotal; a++) {  // get pinID and status of each dc ports
        outgoingDataString += "{\"id\":\"";
        outgoingDataString += to_string(dcPinID[a]);
        outgoingDataString += "\",\"v\":\"";
        
        outgoingDataString += to_string(dcStatus[a]);
        outgoingDataString += "\"}";
        if (a < dcTotal - 1) outgoingDataString += ",";  // add comma if another sensor is coming
    }
}

void dcPorts::startupDCports() {  // run at startup
	if (startupComplete == 0) {
        std::cout << " " << std::endl;
        std::cout << "startupDCports------------------------------" << std::endl;
    }

    std::fill_n(dcPinID, 40, 0);  // reset array
    std::fill_n(dcPin, 40, 0);  // reset array
    std::fill_n(dcDriverID, 40, 0);  // reset array
    std::fill_n(dcInvert, 40, 0);  // reset array
    std::fill_n(dcManual, 40, 0);  // reset array
    std::fill_n(dcManualStatus, 40, 0);  // reset array
    // std::fill_n(dcStatus, 40, 0);  // reset array

    MYSQL_RES *res;
    MYSQL_ROW row;

    std::string s = "";  // used in query string for sort type
    if (dcSortID == 1) {s = "name";}
    else if (dcSortID == 2) {s = "iconID";}
    else if (dcSortID == 3) {s = "gpio";}
    
    // search database for pins defined as a DC port and enabled
    connectMysql();
    std::string query = "SELECT pinID, driverID, gpio, invert, manual, manualStatus FROM pinsAvailable WHERE pinTypeID = '2' AND assigned = '1' ORDER BY " + s + " ASC";  // construct query string
    mysql_query(conn, query.c_str());  // run query

    res = mysql_use_result(conn);
    dcTotal = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        dcPinID[dcTotal] = atoi(row[0]);
        dcDriverID[dcTotal] = atoi(row[1]);
        dcPin[dcTotal] = atoi(row[2]);
        dcInvert[dcTotal] = atoi(row[3]);
        dcManual[dcTotal] = atoi(row[4]);
        dcManualStatus[dcTotal] = atoi(row[5]);
        
        if (startupComplete == 0) {
            if ((dcManual[dcTotal] == 1) && (dcManualStatus[dcTotal] == 0)) {dcStatus[dcTotal] = 0;}
            else if ((dcManual[dcTotal] == 1) && (dcManualStatus[dcTotal] == 1)) {dcStatus[dcTotal] = 1;}
            else {dcStatus[dcTotal] = 0;}  // NEED TO CHANGE TO SMART START
        }
        
        if (dcDriverID[dcTotal] == 2) {  // if dc port is attached to Pi pin
	        pinMode(dcPin[dcTotal],OUTPUT);
            if (dcStatus[dcTotal] == 1) {  // turn on
                if (dcInvert[dcTotal] == 0) {digitalWrite(dcPin[dcTotal], 1);}  // no inverting
                else {digitalWrite(dcPin[dcTotal], 0);}  // invert dc port
            }
            else {  // turn off
                if (dcInvert[dcTotal] == 0) {digitalWrite(dcPin[dcTotal], 0);}  // no inverting
                else {digitalWrite(dcPin[dcTotal], 1);}  // invert dc port
            }
        }
        else {  // other driver
            // std::cout << "Other Driver ------------" << std::endl;
            if (dcStatus[dcTotal] == 1) {  // turn on
                if (dcInvert[dcTotal] == 0) {pca9685.adjustPinLevel(dcPinID[dcTotal], 4095);}  // no inverting
                else {pca9685.adjustPinLevel(dcPinID[dcTotal], 0);}  // invert dc port
            }
            else {  // turn off
                if (dcInvert[dcTotal] == 0) {pca9685.adjustPinLevel(dcPinID[dcTotal], 0);}  // no inverting
                else {pca9685.adjustPinLevel(dcPinID[dcTotal], 4095);}  // invert dc port
            }
        }

		if (startupComplete == 0) {
            std::cout << "gpio DC port - " << dcPinID[dcTotal] << " - " << dcPin[dcTotal] << " - " << dcDriverID[dcTotal] << " - " << dcStatus[dcTotal] << std::endl;
        }
        dcTotal++;
    }
    mysql_disconnect(); 
    mysql_free_result(res);
    
	if (startupComplete == 0) {
        std::cout << "dcTotal------- " << dcTotal << std::endl;
        std::cout << "----------------------------------------" << std::endl;
    }
}

void dcPorts::manuallySwitchDC(short pinID) {  // switch DC port manually from frontend command    
    // std::cout << "manuallySwitchDC -------- " << pinID << std::endl;

    for(short a=0; a < dcTotal; a++) {  // search through all dc ports
        if (dcPinID[a] == pinID) {  // if pinID matches switch dc port
            if (dcDriverID[a] == 2) {  // if dc port is attached to Pi pin
                if (dcStatus[a] == 0) {  // turn on
                    dcStatus[a] = 1;
                    if (dcInvert[a] == 0) {digitalWrite(dcPin[a], 1);}  // no inverting
                    else {digitalWrite(dcPin[a], 0);}  // invert dc port
                }
                else {  // turn off
                    dcStatus[a] = 0;
                    if (dcInvert[a] == 0) {digitalWrite(dcPin[a], 0);}  // no inverting
                    else {digitalWrite(dcPin[a], 1);}  // invert dc port
                }
            }
            else {  // other driver
                if (dcStatus[a] == 0) {  // turn on
                    dcStatus[a] = 1;
                    if (dcInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 4095);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 0);}  // invert dc port
                }
                else {  // turn off
                    dcStatus[a] = 0;
                    if (dcInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 0);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 4095);}  // invert dc port
                }
            }
            if (dcManual[a] == 0) {systemSettings.logActivity(5,2,dcPinID[a],dcStatus[a],"");}  // log action
            else {systemSettings.logActivity(1,2,dcPinID[a],dcStatus[a],"");}  // log action
            dcManual[a] = 1;

            connectMysql();
            std::string query = "UPDATE pinsAvailable SET manual = '" + to_string(dcManual[a]) + "', manualStatus = '" + to_string(dcStatus[a]) + "' WHERE pinID = '" + to_string(dcPinID[a]) + "'";
            mysql_query(conn, query.c_str()); // run query
            mysql_disconnect(); 
            
            // std::cout << "dcStatus[a]------- " << dcStatus[a] << std::endl;
            break;
        }
    }
}

void dcPorts::resumeAutoMode(short pinID) {  // return DC port back to auto mode
    // std::cout << "resumeAutoMode -------- " << pinID << std::endl;
    for(short a=0; a < dcTotal; a++) {  // search through all DC ports
        if (dcPinID[a] == pinID) {  // find port
            dcManual[a] = 0;
            connectMysql();
            std::string query = "UPDATE pinsAvailable SET manual = '0' WHERE pinID = '" + to_string(dcPinID[a]) + "'";
            mysql_query(conn, query.c_str()); // run query
            mysql_disconnect(); 
            systemSettings.logActivity(6,2,dcPinID[a],dcStatus[a],"");  // log action
            break;
        }
    }
}

void dcPorts::dcOn(short pinID, short source) {  // switch DC port on  
    // std::cout << "dcOn -------- " << pinID << std::endl;
    // source 3 = schedules
    // source 4 = rules
    // source 7 = smart start
    for(short a=0; a < dcTotal; a++) {  // search through all ports
        if (dcPinID[a] == pinID) {  // if pinID matches switch port
            if (dcManual[a] == 0) {
                if (dcDriverID[a] == 2) {  // if dc port is attached to Pi pin
                    if (dcInvert[a] == 0) {digitalWrite(dcPin[a], 1);}  // no inverting
                    else {digitalWrite(dcPin[a], 0);}  // invert dc port
                }
                else {  // other driver
                    if (dcInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 4095);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 0);}  // invert dc port
                }
                dcStatus[a] = 1;
            }
            break;
        }
    }
}

void dcPorts::dcOff(short pinID, short source) {  // switch DC port off 
    // std::cout << "dcOff -------- " << pinID << std::endl;
    // source 3 = schedules
    // source 4 = rules
    // source 7 = smart start
    for(short a=0; a < dcTotal; a++) {  // search through all ports
        if (dcPinID[a] == pinID) {  // if pinID matches switch port
            if (dcManual[a] == 0) {
                if (dcDriverID[a] == 2) {  // if dc port is attached to Pi pin
                    if (dcInvert[a] == 0) {digitalWrite(dcPin[a], 0);}  // no inverting
                    else {digitalWrite(dcPin[a], 1);}  // invert dc port
                }
                else {  // other driver
                    if (dcInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 0);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 4095);}  // invert dc port
                }
                dcStatus[a] = 0;
            }
            break;
        }
    }
}

void dcPorts::sortDcWidget(short sortID) {  // NOT USING - sort DC ports from frontend
    // std::cout << "sortDcWidget -------- " << sortID << std::endl;
    if (sortID != 100) {dcSortID = sortID;}

    std::fill_n(dcPinID, dcTotal, 0);  // reset array
    std::fill_n(dcDriverID, 40, 0);  // reset array
    std::fill_n(dcPin, 40, 0);  // reset array
    std::fill_n(dcStatus, 40, 0);  // reset array

    std::string s = "";  // used in query string for sort type
    if (dcSortID == 1) {s = "name";}
    else if (dcSortID == 2) {s = "iconID";}
    else if (dcSortID == 3) {s = "gpio";}

    MYSQL_RES *res;
    MYSQL_ROW row;

    // read DC ports from database in new sort order
    connectMysql();
    std::string query = "SELECT pinID, driverID, gpio FROM pinsAvailable WHERE pinTypeID = '2' AND assigned = '1' ORDER BY " + s + " ASC";  // construct query string
    mysql_query(conn, query.c_str());  // run query

    res = mysql_use_result(conn);
    dcTotal = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        dcPinID[dcTotal] = atoi(row[0]);
        dcDriverID[dcTotal] = atoi(row[1]);
        dcPin[dcTotal] = atoi(row[2]);
        dcTotal++;        
    }
    mysql_disconnect(); 
    mysql_free_result(res);
    
    for (short a=0; a < dcTotal; a++)  // go through each dc port and write status
    {
        dcStatus[a] = digitalRead(dcPin[a]);  // read current status  // NEED TO CHANGE TO SMART START
        // std::cout << dcPinID[a] << " = " << dcStatus[a] << std::endl;
    }
        
    // std::cout << "dcTotal------- " << dcTotal << std::endl;
}

dcPorts dcPorts;