using namespace std;

class outlets
{
    public:
        outlets();
        void startupACoutlets();  // run at startup
        void outletOn(short pinID, short source);  // turn outlet on
        void outletOff(short pinID, short source);  // turn outlet off
        void manuallySwitchOutlet(short pinID);  // manually turn outlet on or off from frontend
        void resumeAutoMode(short pinID);  // return outlet back to auto mode
        void sortOutletWidget(short sortID);  // NOT USING - sort AC outlets according to frontend
        void switchGPIO(int ledPin);  // JUST FOR TESTING
        void changeOutletSortID(short v);  // updates sort order ID from system settings when program starts
        void compileOutletJson();  // compile data to be sent to front end
        void outletResponse(short pinID);  // needed to send response back to front end after manually switching an outlet
        short getStatus(short pinID);  // get AC outlet status
        short getTotalOutlets();  // get total outlets
        short getPinID(short arrayLoc);  // get outlet pinID
    private:
        // short* outletPinID;  // unique pinID  -- track pinID from pinsAvailable table
        // short* outletPin;  // gpio pin number
        // short* outletDriverID;  // driverID
        // short* outletInvert;  // track if logic is inverted
        // short* outletManual;  // track if outlet is in manual mode
        // short* outletManualStatus;  // track outlet status during manual mode so we can save to database
        // short* outletStatus;  // track outlet status

        short outletPinID[64] = {};  // unique pinID  -- track pinID from pinsAvailable table
        short outletPin[64] = {};  // gpio pin number
        short outletDriverID[64] = {};  // driverID
        short outletInvert[64] = {};  // track if logic is inverted
        short outletManual[64] = {};  // track if outlet is in manual mode
        short outletManualStatus[64] = {};  // track outlet status during manual mode so we can save to database
        short outletStatus[64] = {};  // track outlet status
        short outletTotal = 0;  // track total outlets found
        short outletSortID = 0;  // sortID for widget - used so we can fill arrays in same order
};
outlets::outlets(){}

short outlets::getStatus(short pinID) {  // get AC outlet status
    short arrayID = 0;
    for(short a=0; a < outletTotal; a++) {  //
        if (outletPinID[a] == pinID) {arrayID = a; break;}
    }
    return outletStatus[arrayID];
}

short outlets::getTotalOutlets() {  // get total outlets
    return outletTotal;
}

short outlets::getPinID(short arrayLoc) {  // get outlet pinID
    return outletPinID[arrayLoc];
}

void outlets::outletResponse(short pinID) {  // needed to send response back to front end after manually switching an outlet
    for(short a=0; a < outletTotal; a++) {  // search through all outlets
        if (outletPinID[a] == pinID) {  // when match is found get status for outlet
            outgoingDataString = "{\"ac\":[";
            outgoingDataString += "{\"id\":\"";
            outgoingDataString += to_string(pinID);
            outgoingDataString += "\",\"v\":\"";            
            outgoingDataString += to_string(outletStatus[a]);
            outgoingDataString += "\"}";
            outgoingDataString += "]}";
        }
    }
}

void outlets::compileOutletJson() {  // compile data to be sent to front end
    for(short a=0; a < outletTotal; a++) {  // get pinID and status of each outlet
        outgoingDataString += "{\"id\":\"";
        outgoingDataString += to_string(outletPinID[a]);
        outgoingDataString += "\",\"v\":\"";
        
        outgoingDataString += to_string(outletStatus[a]);
        outgoingDataString += "\"}";
        if (a < outletTotal - 1) outgoingDataString += ",";  // add comma if another sensor is coming
    }
}

void outlets::changeOutletSortID(short v) {outletSortID = v;}  // updates sort order ID from system settings when program starts

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

    // delete[] outletPinID;
    // delete[] outletPin;
    // delete[] outletDriverID;
    // delete[] outletInvert;
    // delete[] outletManual;
    // delete[] outletManualStatus;
    // delete[] outletStatus;

    MYSQL_RES *res;
    MYSQL_ROW row;

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

    // search database for pins defined as an outlet and enabled
    connectMysql();
    std::string query = "SELECT COUNT(*) FROM pinsAvailable WHERE pinTypeID = '1' AND assigned = '1';";
    mysql_query(conn, query.c_str());  // run query
    res = mysql_use_result(conn);
    outletTotal = 0;
    while ((row = mysql_fetch_row(res)) != NULL) {outletTotal = atoi(row[0]);}
    mysql_free_result(res);
    
    // outletPinID = new short[outletTotal];
    // outletPin = new short[outletTotal];
    // outletDriverID = new short[outletTotal];
    // outletInvert = new short[outletTotal];
    // outletManual = new short[outletTotal];
    // outletManualStatus = new short[outletTotal];
    // outletStatus = new short[outletTotal];
    
    query = "SELECT pinID, driverID, gpio, invert, manual, manualStatus FROM pinsAvailable WHERE pinTypeID = '1' AND assigned = '1' ORDER BY " + s + " ASC";  // construct query string
    mysql_query(conn, query.c_str());  // run query

    res = mysql_use_result(conn);
    outletTotal = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        outletPinID[outletTotal] = atoi(row[0]);
        outletDriverID[outletTotal] = atoi(row[1]);
        outletPin[outletTotal] = atoi(row[2]);
        outletInvert[outletTotal] = atoi(row[3]);
        outletManual[outletTotal] = atoi(row[4]);
        outletManualStatus[outletTotal] = atoi(row[5]);
        
        if (startupComplete == 0) {
            if ((outletManual[outletTotal] == 1) && (outletManualStatus[outletTotal] == 0)) {outletStatus[outletTotal] = 0;}
            else if ((outletManual[outletTotal] == 1) && (outletManualStatus[outletTotal] == 1)) {outletStatus[outletTotal] = 1;}
            else {outletStatus[outletTotal] = 0;}  // NEED TO CHANGE TO SMART START
        }

        if (outletDriverID[outletTotal] == 2) {  // if outlet is attached to Pi pin
	        pinMode(outletPin[outletTotal],OUTPUT);
            if (outletStatus[outletTotal] == 1) {  // turn on
                if (outletInvert[outletTotal] == 0) {digitalWrite(outletPin[outletTotal], 1);}  // no inverting
                else {digitalWrite(outletPin[outletTotal], 0);}  // invert outlet
            }
            else {  // turn off
                if (outletInvert[outletTotal] == 0) {digitalWrite(outletPin[outletTotal], 0);}  // no inverting
                else {digitalWrite(outletPin[outletTotal], 1);}  // invert outlet
            }
        }
        else {  // other driver
            // std::cout << "Other Driver ------------" << std::endl;
            if (outletStatus[outletTotal] == 1) {  // turn on
                if (outletInvert[outletTotal] == 0) {pca9685.adjustPinLevel(outletPinID[outletTotal], 4095);}  // no inverting
                else {pca9685.adjustPinLevel(outletPinID[outletTotal], 0);}  // invert outlet
            }
            else {  // turn off
                if (outletInvert[outletTotal] == 0) {pca9685.adjustPinLevel(outletPinID[outletTotal], 0);}  // no inverting
                else {pca9685.adjustPinLevel(outletPinID[outletTotal], 4095);}  // invert outlet
            }
        }
		if (startupComplete == 0) {
            std::cout << "gpio AC outlet - " << outletPinID[outletTotal] << " - " << outletPin[outletTotal] << " - " << outletDriverID[outletTotal] << " - " << outletStatus[outletTotal] << std::endl;
        }
        outletTotal++;
    }
    mysql_disconnect(); 
    mysql_free_result(res);
    
    if (startupComplete == 0) {
        std::cout << "outletTotal------- " << outletTotal << std::endl;
        std::cout << "----------------------------------------" << std::endl;
    }
}

void outlets::manuallySwitchOutlet(short pinID) {  // manually turn outlet on or off from frontend    
    // std::cout << "manuallySwitchOutlet -------- " << pinID << std::endl;

    for(short a=0; a < outletTotal; a++) {  // search through all outlets
        if (outletPinID[a] == pinID) {  // if pinID matches switch outlet
            if (outletDriverID[a] == 2) {  // if outlet is attached to Pi pin
                if (outletStatus[a] == 0) {  // turn on
                    outletStatus[a] = 1;
                    if (outletInvert[a] == 0) {digitalWrite(outletPin[a], 1);}  // no inverting
                    else {digitalWrite(outletPin[a], 0);}  // invert outlet
                }
                else {  // turn off
                    outletStatus[a] = 0;
                    if (outletInvert[a] == 0) {digitalWrite(outletPin[a], 0);}  // no inverting
                    else {digitalWrite(outletPin[a], 1);}  // invert outlet
                }
            }
            else {  // other driver
                if (outletStatus[a] == 0) {  // turn on
                    outletStatus[a] = 1;
                    if (outletInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 4095);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 0);}  // invert outlet
                }
                else {  // turn off
                    outletStatus[a] = 0;
                    if (outletInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 0);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 4095);}  // invert outlet
                }
            }

            if (outletManual[a] == 0) {systemSettings.logActivity(5,1,outletPinID[a],outletStatus[a],"");}  // log action
            else {systemSettings.logActivity(1,1,outletPinID[a],outletStatus[a],"");}  // log action
            outletManual[a] = 1;

            connectMysql();
            std::string query = "UPDATE pinsAvailable SET manual = '" + to_string(outletManual[a]) + "', manualStatus = '" + to_string(outletStatus[a]) + "' WHERE pinID = '" + to_string(outletPinID[a]) + "'";
            mysql_query(conn, query.c_str()); // run query
            mysql_disconnect(); 

            // std::cout << "outletStatus[a]------- " << outletStatus[a] << std::endl;
            break;
        }
    }
}

void outlets::resumeAutoMode(short pinID) {  // return outlet back to auto mode
    // std::cout << "resumeAutoMode -------- " << pinID << std::endl;
    for(short a=0; a < outletTotal; a++) {  // search through all outlets
        if (outletPinID[a] == pinID) {  // if pinID matches switch outlet
            outletManual[a] = 0;
            connectMysql();
            std::string query = "UPDATE pinsAvailable SET manual = '0' WHERE pinID = '" + to_string(outletPinID[a]) + "'";
            mysql_query(conn, query.c_str()); // run query
            mysql_disconnect();
            systemSettings.logActivity(6,1,outletPinID[a],outletStatus[a],"");  // log action
            break;
        }
    }
}

void outlets::outletOn(short pinID, short source) {  // switch AC outlet on  
    // std::cout << "outletOn -------- " << pinID << std::endl;
    // source 3 = schedules
    // source 4 = rules
    // source 7 = smart start
    for(short a=0; a < outletTotal; a++) {  // search through all outlets
        if (outletPinID[a] == pinID) {  // if pinID matches switch outlet
            if (outletManual[a] == 0) 
            {
                if (outletDriverID[a] == 2) {  // if outlet is attached to Pi pin
                    if (outletInvert[a] == 0) {digitalWrite(outletPin[a], 1);}  // no inverting
                    else {digitalWrite(outletPin[a], 0);}  // invert outlet
                }
                else {  // other driver
                    if (outletInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 4095);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 0);}  // invert outlet
                }
                outletStatus[a] = 1;
                // std::cout << "outletOn -------- " << pinID << std::endl;
            }
            break;
        }
    }
}

void outlets::outletOff(short pinID, short source) {  // switch AC outlet off 
    // std::cout << "outletOff -------- " << pinID << std::endl;
    // source 3 = schedules
    // source 4 = rules
    // source 7 = smart start
    for(short a=0; a < outletTotal; a++) {  // search through all outlets
        if (outletPinID[a] == pinID) {  // if pinID matches switch outlet
            if (outletManual[a] == 0) {
                if (outletDriverID[a] == 2) {  // if outlet is attached to Pi pin
                    if (outletInvert[a] == 0) {digitalWrite(outletPin[a], 0);}  // no inverting
                    else {digitalWrite(outletPin[a], 1);}  // invert outlet
                }
                else {  // other driver
                    if (outletInvert[a] == 0) {pca9685.adjustPinLevel(pinID, 0);}  // no inverting
                    else {pca9685.adjustPinLevel(pinID, 4095);}  // invert outlet
                }
                outletStatus[a] = 0;
                // std::cout << "outletOff -------- " << pinID << std::endl;
            }
            break;
        }
    }
}

void outlets::sortOutletWidget(short sortID) {  // NOT USING - sort AC outlets according to frontend
    // std::cout << "sortOutletWidget -------- " << sortID << std::endl;
    if (sortID != 100) {outletSortID = sortID;}  // we ignore if sort is during startup

    std::fill_n(outletPinID, 64, 0);  // reset array
    std::fill_n(outletDriverID, 64, 0);  // reset array
    std::fill_n(outletPin, 64, 0);  // reset array
    std::fill_n(outletStatus, 64, 0);  // reset array

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

    MYSQL_RES *res;
    MYSQL_ROW row;

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

    res = mysql_use_result(conn);
    outletTotal = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        outletPinID[outletTotal] = atoi(row[0]);
        outletDriverID[outletTotal] = atoi(row[1]);
        outletPin[outletTotal] = atoi(row[2]);
        outletTotal++;
    }
    mysql_disconnect();
    mysql_free_result(res);
    
    for (short a=0; a < outletTotal; a++)  // go through each outlet and write status
    {
        outletStatus[a] = digitalRead(outletPin[a]);  // read current status  // NEED TO CHANGE TO SMART START
        // std::cout << outletPinID[a] << " = " << outletStatus[a] << std::endl;
    }

    // std::cout << "outletTotal------- " << outletTotal << std::endl;
}

void outlets::switchGPIO(int ledPin) {  // USED FOR TESTING

    // std::cout << "switchGPIO -------- " << ledPin << std::endl;
        // std::cout << currentMillisTime << std::endl;
        digitalWrite(ledPin, !digitalRead(ledPin)); // write opposite of current status
        for (short a=0; a < outletTotal; a++)  // go through each outlet and write status
        {
            if (outletPin[a] == ledPin) {outletStatus[a] = digitalRead(ledPin); break;}  // read current status
        }
        // saveOutletStatus();
	// digitalWrite(ledPin,HIGH);
	// delay(1000);
	// digitalWrite(ledPin,LOW);
	// delay(1000);
	// std::cout << "Blink the LED" << endl;
    // connectMysql();
    // runQuery();
    
    // line below gets currentMillisTime since 1970
    // const auto p1 = std::chrono::system_clock::now();

    // std::cout << "seconds since epoch: "
    //           << std::chrono::duration_cast<std::chrono::seconds>(
    //                p1.time_since_epoch()).count() << '\n';

}

outlets outlets;