from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.button import Button
from kivy.uix.image import Image
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.behaviors import ButtonBehavior
from kivy.graphics.texture import Texture
from kivy.clock import Clock
from kivy.metrics import dp
from kivy.properties import StringProperty
import JsonUtl as jsonutil
import cv2
import multiprocessing


class ButtonImage(ButtonBehavior, Image):
    pass


class MainScreen(Screen):
    ServerListVar = jsonutil.OpenJsontoArray()
    BtnId = 0
    DelBtnId = 0

    def __init__(self, **kw):
        super().__init__(**kw)
        for i in range(0, len(self.ServerListVar)):
            self.CreateButtons(i)

    def Clicked(self, widget):
        Adress = self.ids.StreamAdress
        if not Adress.text == "":
            self.ServerListVar.append(Adress.text.strip())
            jsonutil.SaveArraytoJson(self.ServerListVar)
            self.CreateButtons(len(self.ServerListVar) - 1)
            Adress.text = ""

    def CamSelectButtonPressed(self, x):
        app = App.get_running_app()
        app.data = self.ServerListVar[x]
        app.root.current = "Stream"

    def CamDeleteButtonPressed(self, x):
        del self.ServerListVar[x]
        self.ids.CamSelectStackLayout.clear_widgets()
        jsonutil.SaveArraytoJson(self.ServerListVar)
        self.ServerListVar = jsonutil.OpenJsontoArray()
        self.BtnId = 0
        self.DelBtnId = 0
        for i in range(0, len(self.ServerListVar)):
            self.CreateButtons(i)

    def CreateButtons(self, i):
        box = BoxLayout(size_hint=(None, None), width=dp(200), height=dp(200), orientation="vertical")
        bimage = ButtonImage(source="ConnectionError.png")
        b2 = ButtonImage(size_hint=(None, None), width=dp(200), height=dp(50))
        b2.source = "DeleteButton.png"
        bimage.bind(on_press=lambda instance, button_id=self.BtnId: self.CamSelectButtonPressed(button_id))
        self.BtnId = self.BtnId + 1
        b2.bind(on_press=lambda instance, button_id=self.DelBtnId: self.CamDeleteButtonPressed(button_id))
        self.DelBtnId = self.DelBtnId + 1

        # call capture in a separate process
        process = multiprocessing.Process(target=self.capture_video, args=(self.ServerListVar[i], bimage))
        process.start()
        self.processes.append(process)

        box.add_widget(bimage)
        box.add_widget(b2)
        self.ids.CamSelectStackLayout.add_widget(box)

    def capture_video(self, url, widget):
        capture = cv2.VideoCapture(url, cv2.CAP_FFMPEG, [cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY])
        while True:
            ret, frame = capture.read()
            if ret:
            # update the widget texture on the main process
                Clock.schedule_once(lambda dt: self.update_texture(frame, widget))
                break
            else:
                capture.release()
                break

    def update_texture(self, frame, widget):
        buf = cv2.flip(frame, 0).tobytes()
        texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt="bgr")
        texture.blit_buffer(buf, colorfmt="bgr", bufferfmt="ubyte")
        widget.texture = texture


class StreamView(Screen):
    output = StringProperty()

    def on_enter(self):
        app = App.get_running_app()
        self.output = app.data
        self.capture = cv2.VideoCapture(self.output, cv2.CAP_FFMPEG, [cv2.CAP_PROP_HW_ACCELERATION, cv2.VIDEO_ACCELERATION_ANY])
        b = ButtonImage(size_hint=(0.05, 1), pos_hint={"top": 1}, source="BackButton.png")
        b.bind(on_press=lambda instance, button_id="i": gotomainscreen(button_id))
        self.Box = BoxLayout()
        self.image = Image()

        self.add_widget(self.Box)
        self.Box.add_widget(b)
        self.Box.add_widget(self.image)

        self.update_event = Clock.schedule_interval(self.update, 1.0 / 30.0)
        
        def gotomainscreen(x):
            app = App.get_running_app()
            app.root.current = "Main"

    def update(self, dt):
        ret, frame = self.capture.read()
        if ret:
            buf = cv2.flip(frame, 0).tobytes()
            texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt="bgr")
            texture.blit_buffer(buf, colorfmt="bgr", bufferfmt="ubyte")
            self.image.texture = texture

    def on_leave(self):
        self.capture.release()
        self.update_event.cancel()
        self.Box.clear_widgets()
        cv2.destroyAllWindows()


class ScreenMan(ScreenManager):
    pass


class IPCAMApp(App):
    def build(self):
        sm = ScreenMan()
        self.processes = []
        sm.add_widget(MainScreen(name="Main"))
        sm.add_widget(StreamView(name="Stream"))
        sm.current = "Main"
        return sm

    def on_stop(self):
# terminate all capture processes on exit
     for process in self.root.get_screen("Main").processes:
        process.terminate()


if __name__ == "main":
    IPCAMApp().run()