import requests
from kivymd.app import MDApp
from kivy.lang import Builder
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image as PILImage
from kivy.graphics.texture import Texture
from kivymd.uix.label import MDLabel
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivymd.uix.button import MDRaisedButton
import socket
from kivy.clock import Clock
from kivy.core.window import Window
from datetime import datetime, timedelta
from kivymd.uix.list import OneLineListItem # Correct import for OneLineListItem


#Window.size = (350,600)

API_KEY = '2aece83b3adbe88f6c83c36fb780bfea'
BASE_URL = 'http://api.openweathermap.org/data/2.5/forecast'

# Define the IP address and port for socket communication
server_ip = '192.168.1.6' # Update this with the IP address of your computer
server_port = 12344
# Choose the same port as defined in the server script

kv_string = """
BoxLayout:
  orientation: 'vertical'
  padding: '10dp'
  spacing: '10dp'
  size_hint: None, None
  size: '400dp', '800dp'

  MDBottomNavigation:
    panel_color: 0, 0, 0, 0.3 # Background color of the bottom navigation bar
    text_color_active: 1, 1, 1, 1 # Set active text color to white
    text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
    size_hint: 1, 1

    MDBottomNavigationItem:
      name: 'home'
      text: 'Home'
      icon: 'home'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white

      BoxLayout:
        orientation: 'vertical'
        padding: '10dp'
        spacing: '10dp'

        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          pos_hint: {'center_x': 0.5}

          BoxLayout:
            padding: '30dp'
            orientation: 'vertical'

            BoxLayout:
              orientation: 'vertical'
              size_hint_y: None
              height: self.minimum_height
              width: '20dp'
              pos_hint: {'center_x': 0.5}
              padding: '20dp'
              spacing: '10dp'
              canvas.before:
                Color:
                  rgba: 0, 0, 0, 0.8 # Translucent black color
                Rectangle:
                  pos: self.pos
                  size: self.size

              MDLabel:
                id: soil_moisture_label
                text: "Waiting for data..."
                halign: "center"
                size_hint_y: None
                height: '24dp'
                color: 1, 1, 1, 1

            Widget:
              size_hint_y: None
              height: '14dp'


          Widget:
            size_hint_y: None
            height: '18dp'

          Image:
            id: graph_image
            allow_stretch: True
            nocache: True
            size_hint_y: None
            height: '300dp'

          Widget:
            size_hint_y: None
            height: '124dp'




    MDBottomNavigationItem:
      name: 'Manual control'
      text: 'Pump Control'
      icon: 'water-pump'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
      BoxLayout:
        padding: '5dp'
        spacing: '10dp'
        width: '10dp'
        #height: '100dp'
        #size_hint_y: '100dp'
        orientation: 'vertical'
        pos_hint: {'center_x': 0.5}


        Widget:
          size_hint_y: None
          height: '84dp'

        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.5 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: mode
            text: "MODE"
            halign: "center"
            size_hint_y: None
            height: '14dp'
            color: 1, 1, 1, 1

        Widget:
          size_hint_y: None
          height: '154dp'

        BoxLayout:
          padding: '30dp'
          spacing: '10dp'
          width: '10dp'
          height: '30dp'
          size_hint_y: '40dp'
          orientation: 'vertical'

          MDRaisedButton:
            text: "AUTO "
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.auto_mode()

          Widget:
            size_hint_y: None
            height: '5dp'





          MDRaisedButton:
            text: "MANUAL "
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.manual_control()

        Widget:
          size_hint_y: None
          height: '24dp'

        BoxLayout:
          orientation: 'vertical'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.5 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: manualControl
            text: "PUMP"
            halign: "center"
            size_hint_y: None
            height: '14dp'
            color: 1, 1, 1, 1



        Widget:
          size_hint_y: None
          height: '44dp'


        BoxLayout:
          padding: '5dp'
          spacing: '10dp'
          width: '10dp'
          height: '50dp'
          size_hint_y: '50dp'
          orientation: 'horizontal'

          MDRaisedButton:
            text: "START"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.send_message_to_server()


          Widget:
            size_hint_y: None
            height: '10dp'


          MDRaisedButton:
            text: "STOP"
            size_hint_y: None
            height: '100dp'
            pos_hint: {'center_x': 0.5}
            size_hint_x: '10dp'
            font_size: '20sp'
            on_press: app.send_message_to_server_stop()


        Widget:
          size_hint_y: None
          height: '24dp'





        BoxLayout:
          orientation: 'horizontal'
          size_hint_y: None
          height: self.minimum_height
          width: '20dp'
          pos_hint: {'center_x': 0.5}
          padding: '20dp'
          spacing: '10dp'
          canvas.before:
            Color:
              rgba: 0, 0, 0, 0.8 # Translucent black color
            Rectangle:
              pos: self.pos
              size: self.size

          MDLabel:
            id: pump_state_label
            text: "Pump state : Off"
            halign: "center"
            size_hint_y: None
            height: '24dp'
            color: 1, 1, 1, 1

        Widget:
          size_hint_y: None
          height: '84dp'


    MDBottomNavigationItem:
      name: 'data'
      text: 'Weather'
      icon: 'application-cog'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
      
      MDFloatLayout:
        md_bg_color:1,1,1,1
      
        MDLabel:
          id:location
          text: "Delhi,IN"
          pos_hint: {'center_x' : 0.5, 'center_y':0.89}
          halign: "center"
          font_size: "20sp"
          #font_name: "BPoppins"
        
        Image:
          id: weather_image
          source: "assets/sun.png"
          pos_hint: {"center_x":0.5,"center_y":0.77}
      
        MDLabel:
          id:temperature
          text: "[b]40[/b]°"
          markup: True
          pos_hint: {'center_x' : 0.5, 'center_y':0.62}
          halign: "center"
          font_size: "60sp"
          #font_name: "BPoppins"
          
        MDLabel:
          id:weather
          text: "Partly cloudy"
          #markup: True
          pos_hint: {'center_x' : 0.5, 'center_y':0.54}
          halign: "center"
          font_size: "30sp"
          #font_name: "BPoppins"
          
          
        MDFloatLayout:
          pos_hint: {"center_x":.25, "center_y":.4}
          size_hint: .22,.1
          
          Image:
            source: "assets/humidity.png"
            pos_hint: {"center_x":0.1,"center_y":0.5}
          MDLabel:
            id:humidity
            text: "80%"
            pos_hint: {'center_x' : 1, 'center_y':0.7}
            halign: "center"
            font_size: "14sp"
            #font_name: "BPoppins"
          MDLabel:
            text: "Humidity"
            pos_hint: {'center_x' : 1, 'center_y':0.3}
            halign: "center"
            font_size: "14sp"
            #font_name: "BPoppins"
        MDFloatLayout:
          pos_hint: {"center_x":.7, "center_y":.4}
          size_hint: .22,.1
          
          Image:
            source: "assets/wind.png"
            pos_hint: {"center_x":0.1,"center_y":0.5}
          MDLabel:
            id:wind_speed
            text: "40km/h"
            pos_hint: {'center_x' : 1.1, 'center_y':0.7}
            halign: "center"
            font_size: "16sp"
            #font_name: "BPoppins"
          MDLabel:
            text: "Wind speed"
            pos_hint: {'center_x' : 1.1, 'center_y':0.3}
            halign: "center"
            font_size: "14sp"
            #font_name: "BPoppins"    
            
        MDFloatLayout:
          size_hint_y:.3
          canvas:
            Color:
              rgb: rgba(148,117,225,225)
            RoundedRectangle: 
              size: self.size
              pos: self.pos
              radius: [10,10,0,0]
              
          MDFloatLayout:
            pos_hint: {"center_x":.5, "center_y":.71}
            size_hint:.9,.32
            canvas:
              Color:
                rgb: rgba(131,69,225,225)
              RoundedRectangle: 
                size: self.size
                pos: self.pos
                radius: [6]
                
            TextInput:
              id: city_name
              hint_text: "Enter city name"
              size_hint: 1, None
              pos_hint: {"center_x":.5, "center_y":.5}
              height: self.minimum_height
              multiline: False
              font_size: "20sp"
              hint_text_color: 1,1,1,1 
              foreground_color: 1,1,1,1
              background_color: 1,1,1,0
              padding: 15
              cursor_color: 1,1,1,1
              cursor_width:"2sp"   
              
          Button:
            text: "Get weather"
            font_size:"20sp"
            size_hint: .9, .32
            pos_hint: {"center_x":.5, "center_y":.29}
            background_color: 1,1,1,0
            color: rgba(148,117,225,225)
            on_release: app.search_weather()
            canvas.before:
              Color:
                rgb: 1,1,1,1
              RoundedRectangle: 
                size: self.size
                pos: self.pos
                radius: [6]
                
    MDBottomNavigationItem:
      name: 'prediction'
      text: 'Predict'
      icon: 'weather-cloudy-clock'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white
      MDFloatLayout:
        md_bg_color: 1, 1, 1, 1
        padding: '20dp'
        spacing: '10dp'
        # First text input for location
        MDTextField:
          id: weather_input
          hint_text: "Enter Temperature"
          size_hint_x: 0.8
          size_hint_y: None
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.7}
          multiline: False
          # Second text input for date
        MDTextField:
          id: humidity_input
          hint_text: "Enter Humidity"
          size_hint_x: 0.8
          size_hint_y: None
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.55}
          multiline: False
        # Predict button
        MDRaisedButton:
          text: "Predict"
          size_hint_x: 0.8
          size_hint_y: None
          height: '50dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.4}
          on_press: app.predict_weather() # Call the predict weather method
         # Label for the predicted temperature text
        MDLabel:
          text: "Prediction: "
          size_hint_y: None
          size_hint_x: 0.8
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.25}
        # Label for displaying the predicted temperature
        MDLabel:
          id: temp_label
          size_hint_y: None
          size_hint_x: 0.8
          text: "40 degrees"
          size_hint_y: None
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.195}
          
    MDBottomNavigationItem:
      name: 'weather'
      text: 'Forecast'
      icon: 'weather-cloudy'
      text_color_active: 1, 1, 1, 1 # Set active text color to white
      text_color_normal: 1, 1, 1, 1 # Set normal (inactive) text color to white

      MDFloatLayout:
        md_bg_color: 1, 1, 1, 1
        padding: '20dp'
        spacing: '10dp'

        MDTextField:
          id: city_input
          hint_text: 'Enter city name'
          size_hint_x: 0.8
          size_hint_y: None
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.7}
          multiline: False

        MDRaisedButton:
          text: 'Get Forecast'
          size_hint_x: 0.8
          size_hint_y: None
          height: '40dp'
          pos_hint: {'center_x': 0.5, 'center_y': 0.55}
          on_release: app.get_forecast()

        Widget:
          size_hint_y: None
          height: '20dp'
        MDScrollView:
          do_scroll_x: True # Enable horizontal scrolling
          size_hint_x: None
          size_hint_y: None
          height: '300dp' # Set a fixed height for the ScrollView
          width: "2200"
          MDList:
            id: weather_list
            size_hint_x: 0.7
            #orientation : 'horizontal'
            font_size:'7px'
            pos_hint: {'center_x': 0.5, 'center_y': 0.7}


      
"""


class SoilMoistureApp(MDApp):

    api_key="2aece83b3adbe88f6c83c36fb780bfea"

    def build(self):
        return Builder.load_string(kv_string)

    def on_start(self):
        self.graph_image = self.root.ids.graph_image
        self.soil_moisture_label = self.root.ids.soil_moisture_label
        self.pump_state_label = self.root.ids.pump_state_label # Added this line
        self.temp_text = self.root.ids.temp_label

        # Connect to the socket server
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client_socket.connect((server_ip, server_port))

        # Start receiving data from the server
        Clock.schedule_interval(self.receive_data, 1) # Update every 1 second

    def receive_data(self, dt):
        try:
            # Receive all data at once
            data = self.client_socket.recv(1024).decode().strip()

            # Split the data by a delimiter, e.g., a newline character
            data_parts = data.split('\n')

            # Ensure we have all parts (soil moisture, pump state, and temperature)
            if len(data_parts) == 3:
                soil_moisture = data_parts[0].strip()
                pump_state = data_parts[1].strip()
                temperature = data_parts[2].strip()

                # Update labels with the received data
                self.soil_moisture_label.text = f"Soil Moisture: {soil_moisture}"
                self.pump_state_label.text = f"Pump State: {pump_state}"
                self.temp_text.text = f"Predicted soil moisture: {temperature}"

                # Update the graph with the soil moisture data
                self.update_graph(soil_moisture)

        except Exception as e:
            print("Error receiving data:", e)

    def update_graph(self, soil_moisture=None):
        plt.clf()
        if soil_moisture is not None:
            plt.plot([0, 1], [int(soil_moisture), int(soil_moisture)])
        plt.xlabel('Time (s)')
        plt.ylabel('Soil Moisture (%)')
        plt.title('Soil Moisture Graph')
        plt.grid(True)

        buffer = BytesIO()
        plt.savefig(buffer, format='png')
        buffer.seek(0)
        img = PILImage.open(buffer)
        img = img.transpose(PILImage.FLIP_TOP_BOTTOM)

        texture = Texture.create(size=(img.width, img.height))
        texture.blit_buffer(img.tobytes(), colorfmt='rgba', bufferfmt='ubyte')
        self.graph_image.texture = texture

    def send_message_to_server(self):
        try:
            self.client_socket.send(b"start")
        except Exception as e:
            print("Error sending message to server:", e)

    def send_message_to_server_stop(self):
        try:
            self.client_socket.send(b"stop")
        except Exception as e:
            print("Error sending message to server:", e)

    def auto_mode(self):
        try:
            self.client_socket.send(b"auto")
        except Exception as e:
            print("Error sending message to server:", e)

    def manual_control(self):
        try:
            self.client_socket.send(b"manual")
        except Exception as e:
            print("Error sending message to server:", e)

    def get_weather(self, city_name):
        try:
            url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={self.api_key}"
            response = requests.get(url)
            x = response.json()

            # Print the JSON response for debugging
            print(x)

            if x["cod"] != "404":
                temperature = str(round(x["main"]["temp"] - 273.15))
                humidity = str(x["main"]["humidity"])
                weather = x["weather"][0]["main"]
                wind_speed = str(round(x["wind"]["speed"] * 18 / 5)) # converting m/s to km/h
                location = x["name"] + ", " + x["sys"]["country"]

                # Updating UI elements with fetched data
                self.root.ids.temperature.text = f"{temperature}°"
                self.root.ids.weather.text = weather
                self.root.ids.humidity.text = f"{humidity}%"
                self.root.ids.wind_speed.text = f"{wind_speed} km/h"
                self.root.ids.location.text = location
            else:
                print("City not found")
        except requests.ConnectionError:
            print("No internet")

    def search_weather(self):
        city_name = self.root.ids.city_name.text
        if city_name == "":
            city_name="Delhi"

        self.get_weather(city_name)

    def predict_weather(self):
        weather_input = self.root.ids.weather_input.text
        humidity_input = self.root.ids.humidity_input.text

        # Format the data as "weather,humidity"
        data_to_send = f"{weather_input},{humidity_input}"

        # Send the formatted data to the client
        self.client_socket.send(data_to_send.encode('utf-8'))

        # Optionally, you can clear the input fields after sending
        self.root.ids.weather_input.text = ""
        self.root.ids.humidity_input.text = ""

    def get_forecast(self):
        city = self.root.ids.city_input.text
        if not city:
            return

        # Construct the API URL
        url = f"{BASE_URL}?q={city}&appid={API_KEY}&units=metric"

        # Make the API request
        response = requests.get(url)
        data = response.json()

        # Check if the request was successful
        if response.status_code == 200:
            current_date = datetime.now()
            forecast_dates = [current_date + timedelta(days=i) for i in range(3)]

            # Dictionary to store aggregated forecast data for each day
            daily_forecast = {date.strftime('%Y-%m-%d'): {'temp_sum': 0, 'humidity_sum': 0, 'count': 0} for date in
                              forecast_dates}

            # Parse the forecast data
            for entry in data['list']:
                timestamp = datetime.strptime(entry['dt_txt'], '%Y-%m-%d %H:%M:%S')
                date_str = timestamp.strftime('%Y-%m-%d')

                if date_str in daily_forecast:
                    daily_forecast[date_str]['temp_sum'] += entry['main']['temp']
                    daily_forecast[date_str]['humidity_sum'] += entry['main']['humidity']
                    daily_forecast[date_str]['count'] += 1

            # Clear the existing list
            self.root.ids.weather_list.clear_widgets()

            # Generate and show the forecast info in the list
            for date_str, info in daily_forecast.items():
                if info['count'] > 0:
                    avg_temp = info['temp_sum'] / info['count']
                    avg_humidity = info['humidity_sum'] / info['count']
                    forecast_text = f"{date_str}: Avg Temp: {avg_temp:.1f}°C, Avg Humidity: {avg_humidity:.1f}%"
                else:
                    forecast_text = f"{date_str}: No data available"

                # Add the forecast information as list items
                self.root.ids.weather_list.add_widget(
                    OneLineListItem(text=forecast_text)
                )

        else:
            error_message = f"Error: {data.get('message', 'Unable to fetch weather data')}"
            self.root.ids.weather_list.add_widget(
                OneLineListItem(text=error_message)
            )
if __name__ == '__main__':
    SoilMoistureApp().run()