//
//  ChatViewController.swift
//  Chat Application 1.1
//
//  Created by Avaya on 26/09/2016.
//  Copyright © 2016 Avaya. All rights reserved.
//

import UIKit
import Starscream
import SwiftyJSON
import ObjectMapper
import JSQMessagesViewController



class ChatViewController: JSQMessagesViewController, WebSocketDelegate{
    
    
    func didReceive(event: WebSocketEvent, client: WebSocket) {

        print("didReceive  event: \(String(describing: event)) ")
        switch event {
        case .connected(let headers):
            isConnected = true
            print("websocket is connected: \(headers)")
            websocketDidConnect(socket: client)
        case .disconnected(let reason, let code):
            isConnected = false
            print("websocket is disconnected: \(reason) with code: \(code)")
            websocketDidDisconnect(client)
        case .text(let string):
            print("Received text: \(string)")
            websocketDidReceiveMessage(socket: client, text: string)
        case .binary(let data):
            print("Received data: \(data.count)")
        case .ping(_):
            break
        case .pong(_):
            break
        case .viabilityChanged(_):
            break
        case .reconnectSuggested(_):
            break
        case .cancelled:
            isConnected = false
        case .error(let error):
            isConnected = false
            print("didReceive  event: \(String(describing: event)) error: \(String(describing: error))")
        }
    }
    
    
    
    var chatRequest: String?
    var url: String?
    let joined = NewParticipant()
    var  istypingtimer: Timer?
    var  istypingtimerMy: Timer?
    var  isTypingEventShaduled = false
    var  disconnecttimer: Timer?
    var onHoldTimer: Timer?
    var agentName: String?
    var counter: Int = 0
    var isConnected = false
    var kbFrameSize:CGFloat = 0

    @IBOutlet weak var close_btn: UIBarButtonItem!
    
    @IBOutlet weak var background: UIImageView!
    
    let chatUtils = RequestChatUtils()
    //JSQMessages setup
    var messages = [JSQMessage]()
    let agentBubble = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImage(with: UIColor(red: 242/255, green: 0/255, blue: 12/255, alpha: 1.0))
    let pagePushBubble = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImage(with: UIColor(red: 67/255, green: 224/255, blue: 0/255, alpha: 1.0) )
    let customerBubble = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImage(with: UIColor(red: 0/255, green: 150/255, blue: 150/255, alpha: 1.0) )
    let onHoldBubble = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImage(with: UIColor(red: 0/255, green: 60/255, blue: 214/255, alpha: 1.0) )
    var socket : WebSocket?
    
    @IBAction func desconnectBtn(_ sender: UIBarButtonItem) {
        if isConnected {
            closeChat()
        } else {
            socket?.disconnect()
            closeChatAfterDelay()
        }

    }

    func registerForKeyboardNotifications() {
        NotificationCenter.default.addObserver(self, selector: #selector(kbWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(kbWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    func removeKeyboardNotifications() {
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
    }
    
    @objc func kbWillShow(_ notification: Notification) {
        let userInfo = notification.userInfo
        let temp  = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
        kbFrameSize = temp.height
    }
    
    @objc func kbWillHide() {
        kbFrameSize = 0
    }
    
    func closeChat() {
        let closeMessage = CloseConversationRequest()
        
        let wrapper = CloseWrapper()
        wrapper.body = closeMessage
        
        let jsonString: String?  = wrapper.toJSONString()
        sendMessage(message: jsonString!)
    }
    
    override func textViewDidChange(_ textView: UITextView) {
        super.textViewDidChange(textView)
        let temptext = textView.text
        if (temptext != "" || textView.text != nil) {
           // print("USER IS_TYPING")
            

            if !isTypingEventShaduled {
            istypingtimerMy = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(ChatViewController.sendIsTyping), userInfo: true, repeats: false)
                isTypingEventShaduled = true
            }
        }
    }

    
    @objc func sendIsTyping(timer: Timer){
      print("USER IS_TYPING")
        let isTypingMessage = IsTyping();
        isTypingMessage.isTyping = true
        let wrapper = IsTypingWrapper()
        wrapper.body = isTypingMessage
        
        let jsonString: String?  = wrapper.toJSONString()
        print("Sending \(String(describing: jsonString))")
        istypingtimerMy?.invalidate()
        istypingtimerMy  = nil
        isTypingEventShaduled = false
        sendMessage(message: jsonString!)
    }

    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        self.close_btn.isEnabled = true
        
        print("describing URL: \(String(url ?? ""))")
        
        var request: URLRequest

        let  hardcodedURL = url! + "/services/customer/chat"
        print("hardcodedURL: \(String(hardcodedURL))")
        let prepURL = URL.init(string: hardcodedURL)
            
        print("prepURL: \(String(describing: prepURL)) ")
        request = URLRequest.init(url: prepURL!)
        
        print("request: \(String(describing: request)) ")
        let pinner = FoundationSecurity(allowSelfSigned: true)
        socket = WebSocket.init(request: request, certPinner: pinner)
        
        self.collectionView.backgroundColor = .clear
        self.view.insertSubview(background, at: 0)
        //self.navigationController!.navigationBar.translucent = false
        registerForKeyboardNotifications()
        self.setup()
    }
    
    //required to allow the setting of a background image-view
    override func viewDidLayoutSubviews() {
        if let navigationBarRect = self.navigationController?.navigationBar.frame {
            let y = navigationBarRect.size.height + navigationBarRect.origin.y
            self.collectionView?.contentInset = UIEdgeInsets( top: y, left: 0, bottom: 0, right: 0)
        }
    }
    
    
    
    
    func websocketDidConnect(socket: WebSocket) {
        print("socket connected")
        sendMessage(message: chatRequest!)
    }
    
    func websocketDidDisconnect(_ socket: WebSocket) {
        self.navigationItem.setHidesBackButton(false, animated: false)
    }
    
    func websocketDidReceiveMessage(socket: WebSocket, text: String) {
        
        //determine message-type
        let received = JSON.init(parseJSON: text)
        let body = received["body"]
        let method = received["body","method"]

        switch method {
        case "newAgentFileTransfer":
            print("newAgentFileTransfer")
            
            let uuid = body["uuid"].string
            let workRequestId = body["workRequestId"].string
            let name = body["name"].string
            let agentName = body["agentName"].string
            let index = url!.lastIndex(of: "/")!
            var prefix = ""
            let serverUri = String((url?.suffix(from: index))!)
            var transferUrl = ""
            if url!.contains("wss") {
                prefix = "https:/" + serverUri
            } else {
                prefix = "http:/" + serverUri
            }
            transferUrl = prefix + "/services/customer/rest/attachment/" + uuid! + "?workRequestId=" + workRequestId!
            let newMessage = ChatMessage()
            newMessage.message = "Agent transferred a file - " + name! +  ". Please visit the link below to download it."
            newMessage.name = agentName
            newMessage.senderType = "agent"
            displayAgentMessage(newMessage: newMessage)
            newMessage.message = transferUrl
            newMessage.name = agentName
            displayAgentMessage(newMessage: newMessage)
            
            
        case "requestChat":
            print("requestChat:")
            
            let chatNotification = Mapper<RequestChatWrapper>().map(JSONString: text)
            let messageList =  getOnHoldMessages(chatNotification: chatNotification!)
            let urlList =   getOnHoldURLs(chatNotification: chatNotification!)
            
            displayOnHoldMessages(messages: messageList, urlList)
            
        case "newParticipant":
            print("newParticipant:")
            
            self.reloadMessagesView()
            onHoldTimer?.invalidate()
            self.inputToolbar.contentView.alpha = 1.0
            let joined = extractNewparticiant(received: received)
            agentName = joined.displayName
            showCustomAlert(message: "Agent: \(agentName!) has joined the Chat")
            
        case "newMessage":
            print("newMessage:")
            
            //if the new message is not an ACK
            if( received["type"] != "acknowledgement") {
                let newMessage = extractAgentMessage(received: received)
                displayAgentMessage(newMessage: newMessage)
            }
            isTypingTimerFire()
            
        case "newPushPageMessage":
            print("newPushPageMessage:")
            
            let newMessage = extractpagePushMessage(received: received)
            displayAgentMessage(newMessage: newMessage)
            
        case "isTyping":
            print("isTyping:")
            
            displayIsTyping()
            
        case"participantLeave":
            print("participantLeave: \(text)")
            
            let participantLeave = Mapper<LeaveWrapper>().map(JSONString: text)
            //hide the input bar
            
            var messageTemp = "Agent: \(agentName!) has left the Chat"
            
            
            let endChatFlag = participantLeave!.body?.endChatFlag
            if(endChatFlag == true){
                self.inputToolbar.contentView.alpha = 0.0
                disconnectWebsocket()
                websocketDidDisconnect(socket)
                messageTemp = messageTemp + ". Chat will close after 5 sec"
                disconnecttimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(ChatViewController.closeChatAfterDelay), userInfo: true, repeats: false)
            }
            showCustomAlert(message: messageTemp)
            
            
        case "closeConversation":
            print("closeConversation:")
            let result = body["result"]
            self.inputToolbar.contentView.rightBarButtonItem = nil;
            self.inputToolbar.contentView.alpha = 0.0
            
            if result == "true"{
                disconnectWebsocket()
            }
            let message = "The Chat has ended"
            showCustomAlert(message: message)
            websocketDidDisconnect(socket)
         
        case "routeCancel":
            print("RouteCancel")
        
            self.errorAlert()
            
        default:
            print("Message-type: \(method) is undefined")
        }
        
    }
    
    @objc func closeChatAfterDelay() {
        navigationController?.popViewController(animated: true)
    }
    
    //fires when back button is pressed
    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        
        closeChat()
    }
    
    
    func errorAlert(){

        let alert: UIAlertController = UIAlertController(title: "Error", message: "Sorry, Something has gone wrong. The Chat has been aborted", preferredStyle: .alert)
   
            
        alert.addAction(UIAlertAction(title: "Close", style: .destructive, handler: { (action) -> Void in
          self.disconnectWebsocket()
            
        }))
    
        self.present(alert, animated: true, completion: nil)
    }

    func getOnHoldMessages(chatNotification : RequestChatWrapper) -> [String]{
        
        let comfortGroups = chatNotification.body?.webOnHoldComfortGroups
        var list = [String]()
        
        for group: WebOnHoldComfortGroup in comfortGroups!{
            for onHoldM in group.messages!{
                list.append(onHoldM.message!)
            }
        }
        return list
    }
    
    func getOnHoldURLs(chatNotification : RequestChatWrapper) -> [String]{
        
        let webOnHoldUrls = chatNotification.body?.webOnHoldUrls
        var list = [String]()
        for webOnHoldURL: WebOnHoldUrl in webOnHoldUrls! {
            
            let urls =  webOnHoldURL.urls!
            
            for url in urls{
                print(url.url ?? "")
                list.append(url.url!)
            }
        }
        return list
    }
    
    func displayIsTyping(){
        self.showTypingIndicator = true
        self.scrollToBottom(animated: true)
        
        istypingtimer?.invalidate()
        istypingtimer  = nil
        
        istypingtimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(ChatViewController.isTypingTimerFire), userInfo: nil, repeats: false)
    }
    
    
    @objc func isTypingTimerFire(){
        self.showTypingIndicator = false
        istypingtimer?.invalidate()
        istypingtimer = nil
    }
    
    func displayOnHoldMessages(messages: [String],_ url :[String]){
        
        //combined list of urls and onHold messages
        let mixedMessages = chatUtils.mixLists(list1: messages, url)
        
        print("Mixed List: \(mixedMessages.count)")
        if mixedMessages.count > 0 {
            onHoldTimer?.invalidate()
            onHoldTimer = nil
            
            onHoldTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(ChatViewController.fireOnHoldMessage), userInfo: mixedMessages, repeats: true)
        }
    }
    
    @objc func fireOnHoldMessage(timer: Timer){
        
        
        let messages = timer.userInfo as! [String]
        
        if counter >= messages.count{
            counter = 0
        }
        let sender = "onHold"
        let name = "Automated Message"
        let messageContent = messages[counter]
        let message = JSQMessage(senderId: sender, displayName: name, text: messageContent)!
        self.messages.append(message)
        self.reloadMessagesView()
        
        counter += 1
    }
    
    func displayAgentMessage(newMessage: ChatMessage){
        
        let sender = newMessage.senderType
        let name = newMessage.name
        let messageContent = newMessage.message
        let message = JSQMessage(senderId: sender, displayName: name, text: messageContent)!
        self.messages.append(message)
        self.reloadMessagesView()
        self.scrollToBottom(animated: true)
        
        
        
    }
    
    
    func extractAgentMessage(received: JSON) -> ChatMessage{
        
        let newMessage = ChatMessage()
        
        newMessage.message = received["body","message"].string
        newMessage.name = received["body","displayName"].string
        newMessage.timestamp = received["body","timestamp"].int32
        newMessage.senderType = "agent"
        
        return newMessage
    }
    
    func extractpagePushMessage(received: JSON) ->ChatMessage{
        
        let newMessage = ChatMessage()
        
        newMessage.message = received["body","pagePushURL"].string
        newMessage.name = received["body","displayName"].string
        newMessage.timestamp = received["body","timestamp"].int32
        newMessage.senderType = "pagePush"
        
        return newMessage
    }
    
    func buildNewMesage(text: String){
        
        let message = NewMessage(JSONString: text)
        
        print("Working? \(String(describing: message?.displayName))")
        
        
    }
    
    func extractNewparticiant(received: JSON) -> NewParticipant{
        
        joined.agentId = received["body","agentId"].string
        joined.numberOfParticipants = received["body","numberOfParticipants"].int
        joined.displayName = received["body","displayName"].string
        joined.role = received["body","role"].string
        joined.method = received["body","method"].string
        
        return joined
        
    }
    
    
    func websocketDidReceiveData(socket: WebSocket, data: NSData) {
        print("Data Received: \(data)")
    }
    
    
    func sendMessage(message: String){
            print("Sending message: \(message)")
        socket!.write(string: message)

    }
    
    
    func disconnectWebsocket(){
            socket!.disconnect();
            self.close_btn.isEnabled = false
            print("Socket disconnecting...")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    func reloadMessagesView(){
        self.collectionView?.reloadData()
    }
    
    func showCustomAlert(message: String){
        let toast = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 150, y: self.view.frame.size.height-(100+kbFrameSize), width: 300, height: 35))
        toast.backgroundColor = UIColor.darkGray
        toast.textColor = UIColor.white
        toast.textAlignment = NSTextAlignment.center;
        toast.adjustsFontSizeToFitWidth = true
        toast.minimumScaleFactor = 0.5
        self.view.addSubview(toast)
        toast.text = message
        toast.alpha = 0.8
        toast.layer.cornerRadius = 10;
        toast.clipsToBounds  =  true
        
        
        UIView.animate(withDuration: 5.0, delay: 0.8, options: .curveEaseOut, animations: {
            toast.alpha = 0.0
            }, completion: nil)
    }
}

extension ChatViewController{
    
    func setup(){
        
        self.senderId = UIDevice.current.identifierForVendor?.uuidString
        self.senderDisplayName = "Me"
        self.inputToolbar.contentView.leftBarButtonItem = nil;
        self.inputToolbar.contentView.alpha = 0.0
        self.navigationItem.setHidesBackButton(true, animated: true)
        // No avatars
        collectionView!.collectionViewLayout.incomingAvatarViewSize = CGSize.zero
        collectionView!.collectionViewLayout.outgoingAvatarViewSize = CGSize.zero
        
        print("Chat Request: \(String(describing: chatRequest))")
        socket!.delegate = self
        socket!.connect()
    }
}


//Implementing methods for JSQMessageViewController
extension ChatViewController{
    
    //the number of messages
    override
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        return self.messages.count
    }
    //which message to display
    
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData! {
        let data = self.messages[indexPath.row]
        return data
    }
    
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, didDeleteMessageAt indexPath: IndexPath!) {
        self.messages.remove(at: indexPath.row)
    }
    
    
    //determine the chat bubble type based on the sender
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource! {
        
        
        let data = messages[indexPath.row]
        
        switch data.senderId {
        case self.senderId:
            
            return self.customerBubble
        case "agent":
            return self.agentBubble
        case "pagePush":
            return self.pagePushBubble
        case "onHold":
            return self.onHoldBubble
        default:
            return self.agentBubble
        }
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
        let cell = super.collectionView(collectionView, cellForItemAt: indexPath) as! JSQMessagesCollectionViewCell
        let message = messages[indexPath.item]
        if message.senderId == senderId {
            cell.textView!.textColor = UIColor.white
        } else {
            cell.textView!.textColor = UIColor.black
        }
        return cell
    }
    
    //show the name label above the chat bubble
    
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString! {
        let data = messages[indexPath.row]
        
        switch data.senderId {
        case self.senderId:
            return NSAttributedString(string: "Me")
        case "agent":
            return NSAttributedString(string: "Agent: \(joined.displayName!)")
        case "pagePush":
            return NSAttributedString(string: "Agent: \(joined.displayName!)")
        case "onHold":
            return NSAttributedString(string: "Automated Message")
        default:
            return NSAttributedString(string: "Agent: Unknown")
        }
        
    }
    
    
    //adding date stamp to message
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, attributedTextForCellBottomLabelAt indexPath: IndexPath!) -> NSAttributedString! {
        
        let message: JSQMessage = self.messages[indexPath.row]
        return JSQMessagesTimestampFormatter.shared().attributedTimestamp(for: message.date)
    }
    
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForCellBottomLabelAt indexPath: IndexPath!) -> CGFloat {
        return 13
    }
    
    //can show avatars for users
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource! {
        return nil
    }
    //set the height of the name label
    override
    func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat {
        return 13
    }
    
}


//extension to handle input
extension ChatViewController{
    override
    func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!) {

            let message = JSQMessage(senderId: senderId, senderDisplayName: senderId, date: date as Date?, text: text)
            self.messages.append(message!)
            self.finishSendingMessage()
            
            let outMessage = CustomerMessage()
            outMessage.message = text
            outMessage.method = "newMessage"
            
            let wrapper = NewMessageWrapper()
            wrapper.body = outMessage
            let jsonString: String? = wrapper.toJSONString(prettyPrint: true)
            
            self.sendMessage(message: jsonString!)
            istypingtimerMy?.invalidate()
            istypingtimerMy  = nil

    }
}






