import pandas as pd
import socket
import threading
import serial
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error

global sms_ts
sms_ts = 0

# Initialize serial connection
ser = serial.Serial('/dev/ttyUSB0', 9600)

# Load data
melbourne_file_path = '/home/samvit/Desktop/Harshit-The-Great/large_soil_moisture_dataset.csv'
melbourne_data = pd.read_csv(melbourne_file_path)

# Drop missing values
melbourne_data = melbourne_data.dropna(axis=0)

# Select target variable
y = melbourne_data.Sensor_Value

# Select features
melbourne_features = ['Temperature', 'Humidity']
X = melbourne_data[melbourne_features]

# One-hot encode categorical data
X = pd.get_dummies(X)

# Define model
melbourne_model = DecisionTreeRegressor(random_state=1)

# Split data into training and validation data
train_X, val_X, train_y, val_y = train_test_split(X, y, random_state=0)

# Fit model on training data
melbourne_model.fit(train_X, train_y)

# Predict on validation data
val_predictions = melbourne_model.predict(val_X)
print("Mean Absolute Error on validation data:", mean_absolute_error(val_y, val_predictions))

# Function to predict soil moisture based on temperature and humidity
def predict_sms(Temperature, Humidity):
    # Create a dictionary with the input values
    user_data = {'Temperature': [Temperature], 'Humidity': [Humidity]}
    
    # Convert to DataFrame
    user_df = pd.DataFrame(user_data)
    
    # One-hot encode the user's input to match the training data format
    user_df = pd.get_dummies(user_df)
    
    # Ensure the user input has the same columns as the training data
    user_df = user_df.reindex(columns=X.columns, fill_value=0)
    
    # Predict soil moisture
    user_prediction = melbourne_model.predict(user_df)
    return user_prediction[0]

def read_soil_moisture():
    # Initialize socket connection
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('raspberrypi', 12349))  # Update with the correct server IP and port
    server_socket.listen(1)
    print("Socket listening on {}:{}".format('192.168.1.6', 12344))

    # Function to continuously send data to the KivyMD app
    def send_data_to_client(client_socket):
        global sms_ts
        while True:
            # Read data from serial port
            data = ser.readline().decode().strip()

            # Check if received data contains both soil moisture and pump state
            if "," in data:
                soil_moisture, pump_state = data.split(",")
            else:
                soil_moisture = data
                pump_state = "Off"

            # Print soil moisture data
            print("Soil Moisture: ", soil_moisture)
            print("Pump Control: ", pump_state)
            print("Predicted soil moisture value: ", sms_ts)

            # Send soil moisture to KivyMD app
            client_socket.sendall(f"{soil_moisture}\n{pump_state}\n{sms_ts}\n".encode())

    while True:
        try:
            # Accept connection from KivyMD app
            client_socket, client_address = server_socket.accept()
            print("Connection accepted from:", client_address)

            # Start a new thread to continuously send data to the KivyMD app
            data_thread = threading.Thread(target=send_data_to_client, args=(client_socket,))
            data_thread.start()

            # Listen for data from the KivyMD app
            while True:
                received_data = client_socket.recv(1024).decode().strip()

                # Check for temperature and humidity data
                if ',' in received_data:
                    Temperature, humidity = received_data.split(',')
                    Temperature = float(Temperature)  # Convert Temperature to float
                    humidity = float(humidity)  # Convert humidity to float
                    print(f"Received Temperature: {Temperature}, Humidity: {humidity}")

                    # Call the prediction function
                    predicted_sms = predict_sms(Temperature, humidity)
                    global sms_ts
                    sms_ts = predicted_sms
                    print(f"The predicted soil moisture for the given conditions is: {predicted_sms:.2f}")

                if received_data == 'auto':
                    print("Setting mode to:", received_data)
                    ser.write(b'1')
                if received_data == 'manual':
                    print("Setting mode to:", received_data)
                    ser.write(b'2')
                if received_data == 'start':
                    print("Pump control:", received_data)
                    ser.write(b'3')
                if received_data == 'stop':
                    print("Pump control:", received_data)
                    ser.write(b'4')

        except (socket.error, KeyboardInterrupt) as e:
            print("Socket error or manual interruption: ", e)
            break

    # Close serial and socket connections
    ser.close()
    server_socket.close()

if __name__ == "__main__":
    # Create and start a thread for reading soil moisture
    soil_moisture_thread = threading.Thread(target=read_soil_moisture)
    soil_moisture_thread.start()

    # Join the thread to the main thread
    soil_moisture_thread.join()