//
//  OpenTok.swift
//  Runner
//
//  Created by Zohaib Iqbal Kambrani on 18/10/2021.
//

import Foundation
import OpenTok
import UIKit

enum SdkState: String {
    case loggedOut = "LOGGED_OUT"
    case loggedIn = "LOGGED_IN"
    case wait = "WAIT"
    case error = "ERROR"
}

class OpenTok : NSObject{
    
    private var mainViewController:MainFlutterVC!
    private var registrar:FlutterPluginRegistrar?
    var methodChannel: FlutterMethodChannel?

    init(mainViewController:MainFlutterVC, registrar:FlutterPluginRegistrar?){
        self.mainViewController = mainViewController
        self.methodChannel = FlutterMethodChannel(name: "OpenTok-Platform-Bridge", binaryMessenger: mainViewController.binaryMessenger)
        self.registrar = registrar

        let remoteVDOFactory = OpenTokRemoteVideoFactory(messenger: registrar!.messenger())
        registrar?.register(remoteVDOFactory, withId: "remote-video-container")

        let localVDOFactory = OpenTokLocalVideoFactory(messenger: registrar!.messenger())
        registrar?.register(localVDOFactory, withId: "local-video-container")
    }

    var otSession: OTSession?

    var subscriber: OTSubscriber?
    lazy var publisher: OTPublisher = {
        let settings = OTPublisherSettings()
        settings.name = UIDevice.current.name
        return OTPublisher(delegate: self, settings: settings)!
    }()

    func initSession(call:FlutterMethodCall, result: @escaping FlutterResult){
        if let arguments = call.arguments as? [String: String],
           let apiKey = arguments["apiKey"],
           let sessionId = arguments["sessionId"],
           let token = arguments["token"]{

            var error: OTError?
            defer {
                // todo
            }

            notifyFlutter(state: SdkState.wait)
            otSession = OTSession(apiKey: apiKey, sessionId: sessionId, delegate: self)!
            otSession?.connect(withToken: token, error: &error)

            result("")
        }else{

        }

    }


    func swapCamera(call:FlutterMethodCall, result: @escaping FlutterResult) {
        if publisher.cameraPosition == .front {
            publisher.cameraPosition = .back
        } else {
            publisher.cameraPosition = .front
        }
        result("")
    }

    func toggleAudio(call:FlutterMethodCall, result: @escaping FlutterResult) {
         publisher.publishAudio = !publisher.publishAudio
        result("")
    }
    
    func toggleVideo(call:FlutterMethodCall, result: @escaping FlutterResult) {
         publisher.publishVideo = !publisher.publishVideo
        result("")
    }
    
    func hangupCall(call:FlutterMethodCall, result: @escaping FlutterResult) {
        var error:OTError?
        otSession?.disconnect(&error)
        result(error == nil)
    }


    func notifyFlutter(state: SdkState) {
        methodChannel?.invokeMethod("updateState", arguments: state.rawValue)
    }

}

extension OpenTok: OTSessionDelegate {
    func sessionDidConnect(_ sessionDelegate: OTSession) {
        print("The client connected to the session.")
        notifyFlutter(state: SdkState.loggedIn)

        var error: OTError?
        defer {
            // todo
        }

        self.otSession?.publish(self.publisher, error: &error)

        if let pubView = self.publisher.view {
            pubView.frame = CGRect(x: 0, y: 0, width: 200, height: 300)

            if OpenTokLocalVideoFactory.view == nil {
                OpenTokLocalVideoFactory.viewToAddPub = pubView
            } else {
                OpenTokLocalVideoFactory.view?.addPublisherView(pubView)
            }
        }
    }

    func sessionDidDisconnect(_ session: OTSession) {
        print("The client disconnected from the session.")
        notifyFlutter(state: SdkState.loggedOut)
    }

    func session(_ session: OTSession, didFailWithError error: OTError) {
        print("The client failed to connect to the session: \(error).")
    }

    func session(_ session: OTSession, streamCreated stream: OTStream) {
        print("A stream was created in the session.")
        var error: OTError?
        defer {
            // todo
        }
        subscriber = OTSubscriber(stream: stream, delegate: self)

        session.subscribe(subscriber!, error: &error)
    }

    func session(_ session: OTSession, streamDestroyed stream: OTStream) {
        print("A stream was destroyed in the session.")
    }
}

extension OpenTok: OTPublisherDelegate {
    func publisher(_ publisher: OTPublisherKit, streamCreated stream: OTStream) {
    }

    func publisher(_ publisher: OTPublisherKit, streamDestroyed stream: OTStream) {
    }

    func publisher(_ publisher: OTPublisherKit, didFailWithError error: OTError) {
        print("Publisher failed: \(error.localizedDescription)")
    }
}

extension OpenTok: OTSubscriberDelegate {
    func subscriberDidConnect(toStream subscriberKit: OTSubscriberKit) {
        print("Subscriber connected")

        if let subView = self.subscriber?.view {
            subView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)

            if OpenTokRemoteVideoFactory.view == nil {
                OpenTokRemoteVideoFactory.viewToAddSub = subView
            } else {
                OpenTokRemoteVideoFactory.view?.addSubscriberView(subView)
            }
        }
    }

    func subscriber(_ subscriber: OTSubscriberKit, didFailWithError error: OTError) {
        print("Subscriber failed: \(error.localizedDescription)")
    }
    
}
