
using namespace std;

class rules
{
    public:
        rules();
        void startupRules();  // run at startup
        void monitorRules();  // monitor custom rules
        void addRule(std::string data);  // add new custom rule
        void checkRule(short arrayID);  // check rule conditions 
        void runRule(short arrayID);  // run custom rule
        void setEquipmentTimers(short arrayID);  // set equipment timers
        void calcTimers();  // calculate timers for custom rules
        void deleteRule(short ID);  // delete custom rule
        void enableRule(short ID);  // enable custom rule
		void frontendResponse();  // needed to send response back to front end when schedule is added or edited
    	// void addRule(short type, short offOn, char* schName, int start, bool mon, bool tue, bool wed, bool thu, bool fri, bool sat, bool sun, short repeatDays, short pinID, bool email, bool alert);
    private:
        // short* ruleID;
        // std::string* ruleName;  // track rule name for emails
        // short* typeID;  // track ruleTypeID, schedule, sensor or manual
        // short* email;  // track if email is enabled
        // short* alert;  // track if dashboard alert is enabled
        // short* enabled;  // track if rule is enabled
        // short* ruleRan;  // track if rule ran and when it finishes
        // short* resetRule;  // flag to reset the rule
        // short* sensorRuleID;  // track ruleID
        // std::string* sensorAddress;  // track sensorAddress for rule sensors
        // short* sensorPinID;  // track pinID for rule sensors
        // short* sensorPinTypeID;  // track what kind of sensor it is
        // short* sensorDataType;  // track what data from sensor is used in rule
        // double* sensorValue;  // track sensor trigger value
        // short* sensorLessGreater;  // track if less than or greater than
        // short* sensorStability;  // track how long the sensor should remain in trigger zone before triggering, in seconds
		// unsigned long long* sensorStabilityMillis;
        // short* sensorLogic;  // track if sensor works with another, None, AND, OR
        // short* conditionMeet;  // track if sensor condition is meet
        // short* equipRuleID;  // track ruleID
        // short* equipPinID;  // track pinID for rule equipment
        // short* equipPinTypeID;  // track what kind of equipment it is
        // short* equipOffOn;  // track if it turns equipment on/off
        // short* equipSwitchDelay;  // track how long until device is switched on or off
        // short* equipSwitched;  // track if equipment was switched
		// unsigned long long* equipSwitchDelayMillis;
        // short* equipNextDoseDelay;  // track how long to delay dosing regular schedule
        // float* equipDoseML;  // track how much to dose

        std::string* newRule;
        short ruleID[50] = {};  // track ruleID
        std::string ruleName[50] = {};  // track rule name for emails
        short typeID[50] = {};  // track ruleTypeID, schedule, sensor or manual
        short email[50] = {};  // track if email is enabled
        short alert[50] = {};  // track if dashboard alert is enabled
        short enabled[50] = {};  // track if rule is enabled
        short ruleRan[50] = {};  // track if rule ran and when it finishes
        short resetRule[50] = {0};  // flag to reset the rule
        short sensorRuleID[100] = {};  // track ruleID
        std::string sensorAddress[100] = {};  // track sensorAddress for rule sensors
        short sensorPinID[100] = {};  // track pinID for rule sensors
        short sensorPinTypeID[100] = {};  // track what kind of sensor it is
        short sensorDataType[100] = {};  // track what data from sensor is used in rule
        double sensorValue[100] = {};  // track sensor trigger value
        short sensorLessGreater[100] = {};  // track if less than or greater than
        short sensorStability[100] = {};  // track how long the sensor should remain in trigger zone before triggering, in seconds
		// unsigned long long* sensorStabilityMillis = new unsigned long long[100];
        unsigned long long sensorStabilityMillis[100] = {};  // track how long the sensor should remain in trigger zone before triggering, in seconds
        short sensorLogic[100] = {};  // track if sensor works with another, None, AND, OR
        short conditionMeet[100] = {0};  // track if sensor condition is meet
        short equipRuleID[600] = {};  // track ruleID
        short equipPinID[600] = {};  // track pinID for rule equipment
        short equipPinTypeID[600] = {};  // track what kind of equipment it is
        short equipOffOn[600] = {};  // track if it turns equipment on/off
        short equipSwitchDelay[600] = {};  // track how long until device is switched on or off
        short equipSwitched[600] = {0};  // track if equipment was switched
        unsigned long long equipSwitchDelayMillis[600] = {};  // track how long until device is switched on or off
		// unsigned long long* equipSwitchDelayMillis = new unsigned long long[600];
        short equipNextDoseDelay[600] = {};  // track how long to delay dosing regular schedule
        float equipDoseML[600] = {};  // track how much to dose
        short totalRules = 0;  // track how many rules there are
        short totalSensors = 0;  // track how many sensors for all rules
        short totalEquipment = 0;  // track how many pieces of equipment for all rules
        short totalIncoming = 0;  // total new rule data incoming
        short countIncoming = 0;  // count pieces of new custom rule as they come in
        short countIncomingEquip = 0;  // total pieces of equipment for new custom rule incoming
        short equipComing = 0;  // flag so we know equipment is coming now
        short newRuleID = 0;  // track new rule ID for saving related records
        std::string nameIncoming = "";  // temporary store new rule name until saving
};
rules::rules(){}

void rules::addRule(std::string data)  // add new custom rule
{
    countIncoming++;
    // std::cout << "data = " << data << " count = " << countIncoming << std::endl;
    std::string query = "";
    if (totalIncoming == 0) {  // rule starts, get chunks of data count coming
        totalIncoming = std::stoi(data);  // total incoming chunks of data
        countIncoming = 0;  // reset count
        // std::cout << "totalIncoming          = " << totalIncoming << std::endl;
        // std::cout << "totalRules             = " << totalRules << std::endl;
        // std::cout << "totalSensors           = " << totalSensors << std::endl;
        // std::cout << "totalEquipment         = " << totalEquipment << std::endl;
    }
    else if (countIncoming <= totalIncoming)
    {
        // countIncoming++;
        if (countIncoming == 1) {sensorLogic[totalSensors] = 255;}  // set a default value for logic
        
        if ((equipComing == 0) && (sensorLogic[totalSensors] == 255))  // rule data and first sensor
        {
            switch (countIncoming)
            {
                case 1: nameIncoming = ""; nameIncoming = data; break;
                case 2: typeID[totalRules] = std::stoi(data); break;
                case 3: alert[totalRules] = std::stoi(data); break;
                case 4: 
                    email[totalRules] = std::stoi(data);
                    // std::cout << "typeID[totalRules]       = " << typeID[totalRules] << std::endl;
                    // std::cout << "nameIncoming             = " << nameIncoming << std::endl;
                    // std::cout << "email[totalRules]        = " << email[totalRules] << std::endl;
                    // std::cout << "alert[totalRules]        = " << alert[totalRules] << std::endl;
                    connectMysql();
                    query = "INSERT INTO rules(ruleTypeID, ruleName, email, alert) VALUES(" + to_string(typeID[totalRules]) + ",'" + nameIncoming + "'," + to_string(email[totalRules]) + "," + to_string(alert[totalRules]) + ")";
                    mysql_query(conn, query.c_str());  // run query
                    // mysql_disconnect(); 

                    MYSQL_RES *res;
                    MYSQL_ROW row;

                    query = "SELECT MAX(ruleID) FROM `rules` WHERE 1";

                    // connectMysql();
                    mysql_query(conn, query.c_str());  // run query
                    res = mysql_use_result(conn);
                    while ((row = mysql_fetch_row(res)) != NULL)
                    {
                        newRuleID = atoi(row[0]);
                    }
                    mysql_free_result(res);
                    mysql_disconnect(); 


                break;

                // sensor 1
                case 5: sensorValue[totalSensors] = std::stof(data); break;
                case 6: sensorDataType[totalSensors] = std::stoi(data); break;
                case 7: sensorLessGreater[totalSensors] = std::stoi(data); break;
                case 8: sensorStability[totalSensors] = std::stoi(data); break;
                case 9: sensorPinTypeID[totalSensors] = std::stoi(data); break;
                case 10: 
                    if ((sensorPinTypeID[totalSensors] == 3) || (sensorPinTypeID[totalSensors] == 4) || (sensorPinTypeID[totalSensors] == 5))  // pH, dht2, ds18b20
                    {
                        sensorAddress[totalSensors] = data;
                    }
                    else {sensorPinID[totalSensors] = std::stoi(data);}
                break;
                case 11: 
                    sensorLogic[totalSensors] = std::stoi(data);
                    if (sensorLogic[totalSensors] == 0) {equipComing = 1; countIncomingEquip = 0;}  // no more sensors, equipment coming next
                
                    // std::cout << "newRuleID                        = " << newRuleID << std::endl;
                    // std::cout << "sensorPinID[totalSensors]        = " << sensorPinID[totalSensors] << std::endl;
                    // std::cout << "sensorAddress[totalSensors]      = " << sensorAddress[totalSensors] << std::endl;
                    // std::cout << "sensorDataType[totalSensors]     = " << sensorDataType[totalSensors] << std::endl;
                    // std::cout << "sensorValue[totalSensors]        = " << sensorValue[totalSensors] << std::endl;
                    // std::cout << "lessGreater[totalSensors]        = " << sensorLessGreater[totalSensors] << std::endl;
                    // std::cout << "stabilitySeconds[totalSensors]   = " << sensorStability[totalSensors] << std::endl;
                    // std::cout << "logic[totalSensors]              = " << sensorLogic[totalSensors] << std::endl;
                    connectMysql();
                    if (sensorPinTypeID[totalSensors] == 3)  // pH
                    {
                        std::cout << "PH PROBE BEING USED ----------------- " << std::endl;
                        query = "INSERT INTO rulesSensors(ruleID, sensorAddress, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + ",'" + sensorAddress[totalSensors] + "'," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    else if ((sensorPinTypeID[totalSensors] == 4) || (sensorPinTypeID[totalSensors] == 5))  // dht2, ds18b20
                    {
                        query = "INSERT INTO rulesSensors(ruleID, sensorAddress, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + ",'" + sensorAddress[totalSensors] + "'," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    else  // other sensor types
                    {
                        query = "INSERT INTO rulesSensors(ruleID, sensorPinID, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + "," + to_string(sensorPinID[totalSensors]) + "," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    mysql_disconnect();

                    totalSensors++;
                    // std::cout << "Sensor 1 Finished ----------------- " << std::endl;
                break;
            }
        }

        else if ((equipComing == 0) && (sensorLogic[totalSensors-1] != 255))  // second sensor coming
        {
            switch (countIncoming)
            {
                // sensor 2
                case 12: sensorValue[totalSensors] = std::stof(data); break;
                case 13: sensorDataType[totalSensors] = std::stoi(data); break;
                case 14: sensorLessGreater[totalSensors] = std::stoi(data); break;
                case 15: sensorStability[totalSensors] = std::stoi(data); break;
                case 16: sensorPinTypeID[totalSensors] = std::stoi(data); break;
                case 17: 
                    if ((sensorPinTypeID[totalSensors] == 3) || (sensorPinTypeID[totalSensors] == 4) || (sensorPinTypeID[totalSensors] == 5))
                    {
                        sensorAddress[totalSensors] = data;
                    }
                    else {sensorPinID[totalSensors] = std::stoi(data);}
                break;
                case 18: 
                    // std::cout << "Sensor 2 Finished ----------------- " << std::endl;
                    sensorLogic[totalSensors] = std::stoi(data);

                    // std::cout << "newRuleID                        = " << newRuleID << std::endl;
                    // std::cout << "sensorPinID[totalSensors]        = " << sensorPinID[totalSensors] << std::endl;
                    // std::cout << "sensorDataType[totalSensors]     = " << sensorDataType[totalSensors] << std::endl;
                    // std::cout << "sensorValue[totalSensors]        = " << sensorValue[totalSensors] << std::endl;
                    // std::cout << "lessGreater[totalSensors]        = " << sensorLessGreater[totalSensors] << std::endl;
                    // std::cout << "stabilitySeconds[totalSensors]   = " << sensorStability[totalSensors] << std::endl;
                    // std::cout << "logic[totalSensors]              = " << sensorLogic[totalSensors] << std::endl;
                    connectMysql();
                    if (sensorPinTypeID[totalSensors] == 3)  // pH
                    {
                        std::cout << "PH PROBE BEING USED ----------------- " << std::endl;
                        query = "INSERT INTO rulesSensors(ruleID, sensorAddress, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + ",'p" + sensorAddress[totalSensors] + "'," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    else if ((sensorPinTypeID[totalSensors] == 4) || (sensorPinTypeID[totalSensors] == 5))  // dht2, ds18b20
                    {
                        query = "INSERT INTO rulesSensors(ruleID, sensorAddress, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + ",'" + sensorAddress[totalSensors] + "'," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    else  // other sensor types
                    {
                        query = "INSERT INTO rulesSensors(ruleID, sensorPinID, sensorDataType, sensorValue, lessGreater, stabilitySeconds, logic) VALUES(" + to_string(newRuleID) + "," + to_string(sensorPinID[totalSensors]) + "," + to_string(sensorDataType[totalSensors]) + "," + to_string(sensorValue[totalSensors]) +  "," + to_string(sensorLessGreater[totalSensors]) +  "," + to_string(sensorStability[totalSensors]) +  "," + to_string(sensorLogic[totalSensors]) + ")";
                        mysql_query(conn, query.c_str());  // run query
                    }
                    mysql_disconnect();

                    totalSensors++;
                    equipComing = 1;
                    countIncomingEquip = 0; break;
            }
        }

        else if (equipComing == 1)
        {
            countIncomingEquip++;
            switch (countIncomingEquip)
            {
                case 1: 
                    equipOffOn[totalEquipment] = std::stoi(data);
                    // if (data == "3") {equipOffOn[totalEquipment] = 1;}
                    // else if (data == "4") {equipOffOn[totalEquipment] = 2;}
                break;
                case 2: equipSwitchDelay[totalEquipment] = std::stoi(data); break;
                case 3: equipPinID[totalEquipment] = std::stoi(data); break;
                case 4: equipNextDoseDelay[totalEquipment] = std::stoi(data); break;
                case 5: 
                    equipDoseML[totalEquipment] = std::stof(data);
                    connectMysql();
                    query = "INSERT INTO rulesEquipment(ruleID, equipmentPinID, equipmentOffOn, switchDelay, nextDoseDelay, doseML) VALUES(" + to_string(newRuleID) + "," + to_string(equipPinID[totalEquipment]) + "," + to_string(equipOffOn[totalEquipment]) + "," + to_string(equipSwitchDelay[totalEquipment]) + "," + to_string(equipNextDoseDelay[totalEquipment]) + "," + to_string(equipDoseML[totalEquipment]) + ")";
                    mysql_query(conn, query.c_str());  // run query
                    mysql_disconnect(); 

                    totalEquipment++;
                    countIncomingEquip = 0;
                    // std::cout << "equipment " + to_string(totalEquipment) + " END ----------------- " << std::endl;
                break;
            }

            // countIncoming++;
        }
    }
    if (countIncoming == totalIncoming-1) 
    {
        totalIncoming = 0;
        countIncomingEquip = 0;
        countIncoming = 0;
        equipComing = 0;
        totalRules++;
        startupRules();
        // std::cout << "" << std::endl;
        // std::cout << "SAVE RULE ----------------- " << std::endl;
        // std::cout << "newRuleID              = " << newRuleID << std::endl;
        // std::cout << "totalRules             = " << totalRules << std::endl;
        // std::cout << "totalSensors           = " << totalSensors << std::endl;
        // std::cout << "totalEquipment         = " << totalEquipment << std::endl;
        // std::cout << "" << std::endl;
    }
}

void rules::calcTimers()  // calculate timers for custom rules
{
    // for (short a=0; a < 50; a++)  // reset timer length
    // {
    //     for (short b=0; b < 4; b++) {_timerLen[a][b] = 0;}
    // }
    // for (short a=0; a < _totalRules; a++)  // calculate length of timers in millis
    // {
    //     if (_dataRules[_ruleID[a]][3] >= 1) {_timerLen[_ruleID[a]][0] = _dataRules[_ruleID[a]][3] * 1000;}  // sensor stabilize timer - seconds
    //     if (_dataRules[_ruleID[a]][5] >= 1) {_timerLen[_ruleID[a]][1] = ((_dataRules[_ruleID[a]][5] * 60)*60)*1000;}  // stop dosing pumps from running X hours timer
    //     if (_dataRules[_ruleID[a]][11] >= 1) {_timerLen[_ruleID[a]][2] = (_dataRules[_ruleID[a]][11] * 60)*1000;}  // run X minutes after feed starts
    //     if (_dataRules[_ruleID[a]][12] >= 1) {_timerLen[_ruleID[a]][3] = (_dataRules[_ruleID[a]][12] * 60)*1000;}  // run X minutes after maintenance starts
    // }
}

void rules::runRule(short arrayID)  // run custom rule
{
    // std::cout << "runRule ----------------------------------------------- " << std::endl;
    short arrayLocations[2] = {0,0};  // track main array locations when reading sensor state, used to get sensor data when processing rule
    short totalCount = 0;  // used to count equipment found and fill local arrays
    short switchedCount = 0;  // used to count through for loop for equipment found and fill local arrays

    if (resetRule[arrayID] == 1)  // reset equipment switched if required
    {
        for(short a=0; a < totalEquipment; a++)  // go through all equipment
        {
            if (equipRuleID[a] == ruleID[arrayID]) {equipSwitched[a] = 0;}  // find equipment for this rule and mark not switched
        }
        resetRule[arrayID] = 0;  // reset flag
        // std::cout << "sendEmail -------------------- " << std::endl;
        if (email[arrayID] == 1) {emails.sendEmail("Custom Rule " + ruleName[arrayID] + " Ran", "Message from Robo-Tank Aquarium Controller");}

    }

    for(short a=0; a < totalEquipment; a++)  // go through all equipment and count how many are for this rule
    {
        if (equipRuleID[a] == ruleID[arrayID]) {totalCount++;}
    }

    for(short a=0; a < totalEquipment; a++)  // go through all equipment for switching
    {
        if (equipRuleID[a] == ruleID[arrayID])  // find all equipment for this rule
        {
            if (currentMillisTime >= equipSwitchDelayMillis[a])  // if switching delay has passed
            {
                // std::cout << "ruleRan[arrayID] ------ " << ruleRan[arrayID] << std::endl;
                if ((equipPinTypeID[a] == 1) && (equipSwitched[a] == 0)) // AC outlet - if it hasn't switched do so
                {
                    if (equipOffOn[a] == 0) {outlets.outletOff(equipPinID[a],4); systemSettings.logActivity(4,1,equipPinID[a],0,ruleName[arrayID]);}
                    else {outlets.outletOn(equipPinID[a],4); systemSettings.logActivity(4,1,equipPinID[a],1,ruleName[arrayID]);}
                    equipSwitched[a] = 1;  // set flag that equipment was switched so it only happens once
                }
                else if ((equipPinTypeID[a] == 2) && (equipSwitched[a] == 0))  // DC port - if it hasn't switched do so
                {
                    if (equipOffOn[a] == 0) {dcPorts.dcOff(equipPinID[a],4); systemSettings.logActivity(4,2,equipPinID[a],0,ruleName[arrayID]);}
                    else if (equipOffOn[a] == 1) {dcPorts.dcOn(equipPinID[a],4); systemSettings.logActivity(4,2,equipPinID[a],1,ruleName[arrayID]);}
                    equipSwitched[a] = 1;  // set flag that equipment was switched so it only happens once
                }
                else if ((equipPinTypeID[a] == 12) && (equipSwitched[a] == 0))  // dosing pump - if it hasn't switched do so
                {
                    dosing.ruleStartStirrer(equipPinID[a],equipNextDoseDelay[a],equipDoseML[a]);
                    equipSwitched[a] = 1;  // set flag that equipment was switched so it only happens once
                    systemSettings.logActivity(4,3,equipPinID[a],1,ruleName[arrayID]);  // log action
                }

                switchedCount++;  // count number of equipment for this rule that has been switched
                if (switchedCount == totalCount) {  // rule finished
                    ruleRan[arrayID] = 2;
                    resetRule[arrayID] = 0;
                    for(short a=0; a < totalEquipment; a++)  // 
                    {
                        if (equipRuleID[a] == ruleID[arrayID]) 
                        {
                            equipSwitched[a] = 0;
                        }
                    }
                }
            }
            else {resetRule[arrayID] = 2;}
        }
    }
}

void rules::setEquipmentTimers(short arrayID)  // set equipment timers for a custom rule
{
    if (ruleRan[arrayID] == 0)  // check that rule hasn't started yet
    {
        for(short a=0; a < totalEquipment; a++)  // go through all equipment 
        {
            if (equipRuleID[a] == ruleID[arrayID])  // find equipment for this rule
            {
                equipSwitchDelayMillis[a] = (equipSwitchDelay[a] * 1000) + currentMillisTime;  // set equipment switch delay timer
                // std::cout << "equipSwitchDelayMillis = " << equipSwitchDelayMillis[a] << std::endl;
            }
        }
        ruleRan[arrayID] = 1;  // mark timers for this rule set
    }
}

void rules::checkRule(short arrayID)  // check rule conditions 
{
    double value[2] = {0,0};  // raw sensor value
    short arrayLocations[2] = {0,0};  // track main array locations to use after getting sensor value
    short sensorCount = 0;  // track how many sensors this rule has

    short count = 0;  // used to count sensors found and fill local arrays
    for(short a=0; a < totalSensors; a++)  // go through all sensors and find any for this rule
    {
        if (sensorRuleID[a] == ruleID[arrayID]) 
        {
            if ((sensorPinTypeID[a] == 3) && (probes.getCalRunning(sensorAddress[arrayID]) == 1)) {return;}  // skip checking rules for pH if calibrating

            if (sensorPinTypeID[a] == 3)  // pH
            {
                if (probes.getCalRunning(sensorAddress[a]) == 1) {return;}  // pH is calibrating skip
                else {value[count] = probes.getValue(sensorAddress[a]);}
            }
            else if (sensorPinTypeID[a] == 4) {  // DHT22
                if (sensorDataType[a] == 0) {value[count] = ds18b20.getTempDHT(sensorPinID[a]);}  // temperature
                else {value[count] = ds18b20.getHumidity(sensorPinID[a]);}  // humidity
            }
            else if (sensorPinTypeID[a] == 5) {value[count] = ds18b20.getTemp(sensorAddress[a]);}  // ds18b20
            else if ((sensorPinTypeID[a] == 6) || (sensorPinTypeID[a] == 7) || (sensorPinTypeID[a] == 8) || (sensorPinTypeID[a] == 9) || (sensorPinTypeID[a] == 14)) {  // water sensors
                value[count] = sensors.getStatus(sensorPinID[a]);
                // std::cout << "value[count] ------ " << value[count] << std::endl;
            }

            arrayLocations[count] = a;  // capture array location of sensor
            
            if (sensorLogic[a] == 0) {break;}  // exit search, no more sensors for this rule
            count++;
        }
    }
    // std::cout << "ruleID[arrayID] ------- " << ruleID[arrayID] << std::endl;

    // std::cout << "value   ------------ ";
    // for(short b=0; b < 2; b++) {
    //     std::cout << value[b] << ", ";
    // }
    // std::cout << " - " << conditionMeet[0] << "" << std::endl;
    
    for(short b=0; b < 2; b++) {  // check if DS18B20 for custom rule is missing, if so exit function so we don't trigger custom rule
        if (value[b] == 999999) {return;}
    }

    // std::cout << "arrayLocations   --- ";
    // for(short b=0; b < 2; b++) {
    //     std::cout << arrayLocations[b] << ", ";
    // }
    // std::cout << "" << std::endl;

    // std::cout << "ruleRan[arrayID] ------ " << ruleRan[arrayID] << std::endl;
    // std::cout << "sensorLogic ----------- " << sensorLogic[arrayLocations[0]] << std::endl;
    // std::cout << "sensorLessGreater------ " << sensorLessGreater[arrayLocations[0]] << std::endl;
    // std::cout << "sensorStability-------- " << sensorStability[arrayLocations[0]] << std::endl;
    // std::cout << "sensorValue ----------- " << sensorValue[arrayLocations[0]] << std::endl;
    // std::cout << "sensorLogic ----------- " << sensorLogic[arrayLocations[0]] << std::endl;
    // std::cout << "sensorLogic ----------- " << sensorLogic[arrayLocations[0]] << std::endl;


    if (sensorLogic[arrayLocations[0]] > 0) {sensorCount = 2;}
    else {sensorCount = 1;}







    for(short a=0; a < sensorCount; a++)  // 
    {
        if (sensorLessGreater[arrayLocations[a]] == 0)  // less than
        {
            if ((conditionMeet[arrayLocations[a]] <= 1) && (value[a] < sensorValue[arrayLocations[a]])) 
            {
                if ((conditionMeet[arrayLocations[a]] == 0) && (sensorStability[arrayLocations[a]] > 0)) {  // set stabilizer timer
                    sensorStabilityMillis[arrayLocations[a]] = (sensorStability[arrayLocations[a]] * 1000) + currentMillisTime;
                    conditionMeet[arrayLocations[a]] = 1;
                }

                if ((conditionMeet[arrayLocations[a]] == 1) && (currentMillisTime > sensorStabilityMillis[arrayLocations[a]])) {conditionMeet[arrayLocations[a]] = 2;}  // run rule after stablize timer passes
                else if (conditionMeet[arrayLocations[a]] == 0) {conditionMeet[arrayLocations[a]] = 2;}  // run rule without stabilizer timer
            }
            else if (value[a] >= sensorValue[arrayLocations[a]]) {conditionMeet[arrayLocations[a]] = 0; resetRule[arrayID] = 1;}  // reset rule
        }
        else if (sensorLessGreater[arrayLocations[a]] == 1)  // greater than
        {
            if ((conditionMeet[arrayLocations[a]] <= 1) && (value[a] > sensorValue[arrayLocations[a]])) 
            {
                if ((conditionMeet[arrayLocations[a]] == 0) && (sensorStability[arrayLocations[a]] > 0)) {  // set stabilizer timer
                    sensorStabilityMillis[arrayLocations[a]] = (sensorStability[arrayLocations[a]] * 1000) + currentMillisTime;
                    conditionMeet[arrayLocations[a]] = 1;
                }

                if ((conditionMeet[arrayLocations[a]] == 1) && (currentMillisTime > sensorStabilityMillis[arrayLocations[a]])) {conditionMeet[arrayLocations[a]] = 2;}  // run rule after stablize timer passes
                else if (conditionMeet[arrayLocations[a]] == 0) {conditionMeet[arrayLocations[a]] = 2;}  // run rule without stabilizer timer
            }
            else if (value[a] <= sensorValue[arrayLocations[a]]) {conditionMeet[arrayLocations[a]] = 0; resetRule[arrayID] = 1;}  // reset rule
        }
    }
    
    // std::cout << "conditionMeet[0] ------- " << conditionMeet[0] << std::endl;

    if (sensorLogic[arrayLocations[0]] == 0) {  // only one sensor
        if ((ruleRan[arrayID] <= 1) && (conditionMeet[arrayLocations[0]] == 2)) 
        {
            setEquipmentTimers(arrayID);
            runRule(arrayID);
        }
        else if (conditionMeet[arrayLocations[0]] == 0) {ruleRan[arrayID] = 0;}
    }
    else if (sensorLogic[arrayLocations[0]] == 1) {  // AND logic
        if ((ruleRan[arrayID] == 0) && (conditionMeet[arrayLocations[0]] == 2) && (conditionMeet[arrayLocations[1]] == 2)) 
        {
            setEquipmentTimers(arrayID);
            runRule(arrayID);
        }
        else if (resetRule[arrayID] == 2)
        {
            runRule(arrayID);
        }
        else if ((conditionMeet[arrayLocations[0]] == 0) || (conditionMeet[arrayLocations[1]] == 0)) {ruleRan[arrayID] = 0;}
    }
    else if (sensorLogic[arrayLocations[0]] == 2) {  // OR logic
        if ((ruleRan[arrayID] == 0) && ((conditionMeet[arrayLocations[0]] == 2) || (conditionMeet[arrayLocations[1]] == 2))) 
        {
            setEquipmentTimers(arrayID);
            runRule(arrayID);
        }
        else if (resetRule[arrayID] == 2)
        {
            runRule(arrayID);
        }
        if ((conditionMeet[arrayLocations[0]] == 0) && (conditionMeet[arrayLocations[1]] != 2)) {ruleRan[arrayID] = 0;}
        if ((conditionMeet[arrayLocations[1]] == 0) && (conditionMeet[arrayLocations[0]] != 2)) {ruleRan[arrayID] = 0;}

        // if ((conditionMeet[arrayLocations[0]] == 0) || (conditionMeet[arrayLocations[1]] == 0)) {ruleRan[arrayID] = 0;}
    }
    // std::cout << "ruleRan[arrayID] ------ " << ruleRan[arrayID] << std::endl;
    // std::cout << "" << std::endl;
}

void rules::monitorRules()  // monitor custom rules
{
    for (short a=0; a < totalRules; a++)  // go through all custom rules
    {
        if (enabled[a] == 1)  // rule is enabled
        {
            checkRule(a);
        }
    }
    // delay(100);
}

void rules::startupRules() {  // run at startup
	if (startupComplete == 0) {
        std::cout << " " << std::endl;
        std::cout << "startupRules------------------------------" << std::endl;
    }
    MYSQL_RES *res;
    MYSQL_ROW row;
    
    std::fill_n(ruleID, 50, 0);  // reset array
    std::fill_n(ruleName, 50, 0);  // reset array
    std::fill_n(typeID, 50, 0);  // reset array
    std::fill_n(email, 50, 0);  // reset array
    std::fill_n(alert, 50, 0);  // reset array
    std::fill_n(enabled, 50, 0);  // reset array
    std::fill_n(sensorRuleID, 100, 0);  // reset array
    std::fill_n(sensorAddress, 100, 0);  // reset array
    std::fill_n(sensorPinID, 100, 0);  // reset array
    std::fill_n(sensorPinTypeID, 100, 0);  // reset array
    std::fill_n(sensorDataType, 100, 0);  // reset array
    std::fill_n(sensorValue, 100, 0);  // reset array
    std::fill_n(sensorLessGreater, 100, 0);  // reset array
    std::fill_n(sensorStability, 100, 0);  // reset array
    std::fill_n(sensorLogic, 100, 0);  // reset array
    std::fill_n(equipRuleID, 600, 0);  // reset array
    std::fill_n(equipPinID, 600, 0);  // reset array
    std::fill_n(equipPinTypeID, 600, 0);  // reset array
    std::fill_n(equipOffOn, 600, 0);  // reset array
    std::fill_n(equipSwitchDelay, 600, 0);  // reset array
    std::fill_n(equipNextDoseDelay, 600, 0);  // reset array
    std::fill_n(sensorStabilityMillis, 600, 0);  // reset array
    std::fill_n(equipSwitchDelayMillis, 600, 0);  // reset array
    std::fill_n(equipDoseML, 600, 0);  // reset array


    connectMysql();
    std::string query = "SELECT ruleID, ruleName, ruleTypeID, email, alert, enabled\
        FROM rules\
        ORDER BY ruleID ASC";
    mysql_query(conn, query.c_str());  // run query
    res = mysql_use_result(conn);
    totalRules = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        ruleID[totalRules] = atoi(row[0]);
        ruleName[totalRules] = row[1];
        typeID[totalRules] = atoi(row[2]);
        email[totalRules] = atoi(row[3]);
        alert[totalRules] = atoi(row[4]);
        enabled[totalRules] = atoi(row[5]);
        totalRules++;
    }
    mysql_free_result(res);
    // mysql_disconnect(); 

    query = "SELECT rulesSensors.ruleID, rulesSensors.sensorPinID, rulesSensors.sensorAddress, rulesSensors.sensorDataType, rulesSensors.sensorValue, rulesSensors.lessGreater, rulesSensors.stabilitySeconds, rulesSensors.logic,\
        pinsAvailable.pinTypeID\
        FROM rulesSensors\
        LEFT JOIN pinsAvailable\
        ON rulesSensors.sensorPinID = pinsAvailable.pinID\
        ORDER BY rulesSensors.ruleID, rulesSensors.rulesSensorID ASC";

    mysql_query(conn, query.c_str());  // run query
    res = mysql_use_result(conn);

    
    totalSensors = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        sensorRuleID[totalSensors] = atoi(row[0]);
        if (row[1] != NULL) {sensorPinID[totalSensors] = atoi(row[1]);}
		else {sensorPinID[totalSensors] = 0;}
        sensorDataType[totalSensors] = atoi(row[3]);
        sensorValue[totalSensors] = atof(row[4]);
        sensorLessGreater[totalSensors] = atoi(row[5]);
        sensorStability[totalSensors] = atoi(row[6]);
        sensorLogic[totalSensors] = atoi(row[7]);
        if (row[8] != NULL) {sensorPinTypeID[totalSensors] = atoi(row[8]);}
		else {sensorPinTypeID[totalSensors] = 0;}
        if (row[2] != NULL) {
            sensorAddress[totalSensors] = row[2];
            if (sensorPinTypeID[totalSensors] == 0) {  // if no pinTypeID was assigned we will assign it
                if (isStringNumbers(sensorAddress[totalSensors]) == 1) {sensorPinTypeID[totalSensors] = 3;}  // if address is a number sensor is a probe
                else {sensorPinTypeID[totalSensors] = 5;}  // if address has letters it a ds18b20
            }
        }
		else {sensorAddress[totalSensors] = "0";}
        totalSensors++;
    }
    mysql_free_result(res);

    query = "SELECT rulesEquipment.ruleID, rulesEquipment.equipmentPinID, rulesEquipment.equipmentOffOn, rulesEquipment.switchDelay, rulesEquipment.nextDoseDelay, rulesEquipment.doseML,\
        pinsAvailable.pinTypeID\
        FROM rulesEquipment\
        LEFT JOIN pinsAvailable\
        ON rulesEquipment.equipmentPinID = pinsAvailable.pinID\
        ORDER BY rulesEquipment.ruleID, rulesEquipment.equipmentPinID ASC";

    mysql_query(conn, query.c_str());  // run query
    res = mysql_use_result(conn);
    
    totalEquipment = 0;
    while ((row = mysql_fetch_row(res)) != NULL)
    {
        equipRuleID[totalEquipment] = atoi(row[0]);
        equipPinID[totalEquipment] = atoi(row[1]);
        equipOffOn[totalEquipment] = atoi(row[2]);
        equipSwitchDelay[totalEquipment] = atoi(row[3]);
        equipNextDoseDelay[totalEquipment] = atoi(row[4]);
        equipDoseML[totalEquipment] = atoi(row[5]);
        equipPinTypeID[totalEquipment] = atoi(row[6]);
        totalEquipment++;
    }
    mysql_free_result(res);
    mysql_disconnect(); 

	if (startupComplete == 0) {
        std::cout << "totalRules ---------- " << totalRules << std::endl;
        std::cout << "totalSensors -------- " << totalSensors << std::endl;
        std::cout << "totalEquipment ------ " << totalEquipment << std::endl;

        std::cout << "ruleID   ------------ ";
        for(short a=0; a < totalRules; a++) {std::cout << ruleID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "ruleName   ---------- ";
        for(short a=0; a < totalRules; a++) {std::cout << ruleName[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "typeID   ------------ ";
        for(short a=0; a < totalRules; a++) {std::cout << typeID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "email --------------- ";
        for(short a=0; a < totalRules; a++) {std::cout << email[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "alert --------------- ";
        for(short a=0; a < totalRules; a++) {std::cout << alert[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "enabled ------------- ";
        for(short a=0; a < totalRules; a++) {std::cout << enabled[a] << ", ";}
        std::cout << "" << std::endl;
        
        std::cout << "sensorRuleID -------- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorRuleID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorPinID --------- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorPinID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorAddress ------- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorAddress[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorDataType ------ ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorDataType[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorValue --------- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorValue[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorLessGreater --- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorLessGreater[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorStability ----- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorStability[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorLogic --------- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorLogic[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "sensorPinTypeID ----- ";
        for(short a=0; a < totalSensors; a++) {std::cout << sensorPinTypeID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipRuleID --------- ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipRuleID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipPinID ---------- ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipPinID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipPinTypeID ------ ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipPinTypeID[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipOffOn ---------- ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipOffOn[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipSwitchDelayMil - ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipSwitchDelay[a] << ", ";}
        std::cout << "" << std::endl;

        std::cout << "equipNextDoseDelay -- ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipNextDoseDelay[a] << ", ";}
        std::cout << "" << std::endl;
        std::cout << "----------------------------------------" << std::endl;

        std::cout << "equipDoseML ------ ";
        for(short a=0; a < totalEquipment; a++) {std::cout << equipDoseML[a] << ", ";}
        std::cout << "" << std::endl;
        std::cout << "----------------------------------------" << std::endl;
    }
}

void rules::deleteRule(short ID) {  // delete custom rule
    // std::cout << "deleteRule ------------------------ " << ID << std::endl;
    connectMysql();
    std::string query = "DELETE FROM rules WHERE ruleID = '" + to_string(ID) + "'";
    mysql_query(conn, query.c_str());  // run query
    mysql_disconnect();

    startupRules();
}

void rules::enableRule(short ID) {  // enable custom rule
    // std::cout << "enableRule ------------------------ " << ID << std::endl;
    short arrayLoc = 0;
    for(short a=0; a < totalRules; a++) {  // search through all dosing pumps to find match
        if (ruleID[a] == ID) {arrayLoc = a; break;}  // get array location
    }
        
    if (enabled[arrayLoc] == 1) {enabled[arrayLoc] = 0;}
    else {enabled[arrayLoc] = 1;}

    connectMysql();
    std::string query = "UPDATE rules SET enabled = '" + to_string(enabled[arrayLoc]) + "' WHERE ruleID = '" + to_string(ruleID[arrayLoc]) + "'";
    mysql_query(conn, query.c_str()); // run query
    mysql_disconnect(); 
}

void rules::frontendResponse() {  // needed to send response back to front end 
	outgoingDataString = "{\"r\":[";
	outgoingDataString += "{\"id\":\"";
	outgoingDataString += "0";
	outgoingDataString += "\"}";
	outgoingDataString += "]}";
}

rules rules;