//
//  IMChatViewController.swift
//  O2Platform
//
//  Created by FancyLou on 2020/6/8.
//  Copyright © 2020 zoneland. All rights reserved.
//

import UIKit
import CocoaLumberjack

import BSImagePicker
import Photos
import Alamofire
import AlamofireImage

import QuickLook
import SnapKit

class IMChatViewController: UIViewController {

    // MARK: - IBOutlet
    //消息列表
    @IBOutlet weak var tableView: UITableView!
    //消息输入框
    @IBOutlet weak var messageInputView: UITextField!
    //底部工具栏的高度约束
    @IBOutlet weak var bottomBarHeightConstraint: NSLayoutConstraint!
    //底部工具栏
    @IBOutlet weak var bottomBar: UIView!
    
    // 底部工具栏 底部约束 输入法弹出的时候使用
    @IBOutlet weak var bottomBarBottomConstraint: NSLayoutConstraint!
    

    private let emojiBarHeight = 196
    //表情窗口
    private lazy var emojiBar: IMChatEmojiBarView = {
        let view = Bundle.main.loadNibNamed("IMChatEmojiBarView", owner: self, options: nil)?.first as! IMChatEmojiBarView
        view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
        return view
    }()
    //语音录制按钮
    private lazy var audioBtnView: IMChatAudioView = {
        let view = Bundle.main.loadNibNamed("IMChatAudioView", owner: self, options: nil)?.first as! IMChatAudioView
        view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: emojiBarHeight.toCGFloat)
        view.delegate = self
        return view
    }()
    //录音的时候显示的view
    private var voiceIconImage: UIImageView?
    private var voiceIocnTitleLable: UILabel?
    private var voiceImageSuperView: UIView?
    
    
    //预览文件
    private lazy var previewVC: CloudFilePreviewController = {
        return CloudFilePreviewController()
    }()

    private lazy var viewModel: IMViewModel = {
        return IMViewModel()
    }()

    // MARK: - properties
    var conversation: IMConversationInfo? = nil
    
    //private
    private var chatMessageList: [IMMessageInfo] = []
    private var page = 0
    private var isShowEmoji = false
    private var isShowAudioView = false
    private var bottomBarHeight = 64 //底部输入框 表情按钮 的高度
    private let bottomToolbarHeight = 46 //底部工具栏 麦克风 相册 相机等按钮的位置
    
    private var playingAudioMessageId: String? // 正在播放音频的消息id

    // 当前选中的消息对象 长按菜单需要使用
    private var currentSelectMsg: IMMessageInfo? = nil
    
    private var imConfig = IMConfig()
    
    deinit {
        AudioPlayerManager.shared.delegate = nil
    }

    // MARK: - functions
    override func viewDidLoad() {
        super.viewDidLoad()
        // 配置文件
        if let config = O2UserDefaults.shared.imConfig {
            imConfig = config
        } else {
            imConfig.enableClearMsg = false
            imConfig.enableRevokeMsg = false
        }
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.register(UINib(nibName: "IMChatMessageViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageViewCell")
        self.tableView.register(UINib(nibName: "IMChatMessageSendViewCell", bundle: nil), forCellReuseIdentifier: "IMChatMessageSendViewCell")
        self.tableView.separatorStyle = .none
//        self.tableView.rowHeight = UITableView.automaticDimension
//        self.tableView.estimatedRowHeight = 144
        self.tableView.backgroundColor = UIColor(hex: "#f3f3f3")
        self.tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: {
           self.loadMsgList()
        })
        
        // 输入框 delegate
        self.messageInputView.delegate = self
        
        // 播放audio delegate
        AudioPlayerManager.shared.delegate = self

        //底部安全距离 老机型没有
        self.bottomBarHeight = Int(iPhoneX ? 64 + IPHONEX_BOTTOM_SAFE_HEIGHT: 64) + self.bottomToolbarHeight
        self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
        self.bottomBar.topBorder(width: 1, borderColor: base_gray_color.alpha(0.5))
        self.messageInputView.backgroundColor = base_gray_color

        //标题
        if self.conversation?.type == o2_im_conversation_type_single {
            if let c = self.conversation {
                var person = ""
                c.personList?.forEach({ (p) in
                    if p != O2AuthSDK.shared.myInfo()?.distinguishedName {
                        person = p
                    }
                })
                if !person.isEmpty {
                    self.title = person.split("@").first ?? ""
                }
            }
        } else {
            self.title = self.conversation?.title
        }
        //群会话 添加修改标题的按钮
        if self.conversation?.type == o2_im_conversation_type_group &&
            O2AuthSDK.shared.myInfo()?.distinguishedName == self.conversation?.adminPerson {

            navigationItem.rightBarButtonItem = UIBarButtonItem(title: "修改", style: .plain, target: self, action: #selector(clickUpdate))
        } else if self.conversation?.type == o2_im_conversation_type_single {
            if imConfig.enableClearMsg == true {
                navigationItem.rightBarButtonItem = UIBarButtonItem(title: "清除聊天记录", style: .plain, target: self, action: #selector(clearAllChatMsg))
            }
        }
        
        //获取聊天数据
        self.loadMsgList()
        //阅读
        self.viewModel.readConversation(conversationId: self.conversation?.id)
    }

    override func viewWillAppear(_ animated: Bool) {
        // websocket 消息监听
        NotificationCenter.default.addObserver(self, selector: #selector(receiveMessageFromWs(notice:)), name: OONotification.websocket.notificationName, object: nil)
        // 监听 键盘打开
        NotificationCenter.default.addObserver(self, selector: #selector(openKeyboard(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        // 监听 键盘大小变化
//        NotificationCenter.default.addObserver(self, selector: #selector(openKeyboard(notification:)), name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
        // 监听 键盘关闭
        NotificationCenter.default.addObserver(self, selector: #selector(closeKeyboard(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        
    }
    override func viewWillDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(self)
    }

    
    // 打开键盘的时候
    @objc private func openKeyboard(notification: Notification) {
        if let userInfo = notification.userInfo {
            let v = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
            let y = v?.cgRectValue.origin.y ?? 0
            DDLogDebug("当前键盘高度： \(y)")
            let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber
            UIView.animate(withDuration: duration?.doubleValue ?? 0.3, animations: {
                self.bottomBarBottomConstraint.constant = y
            })
            
        }
    }
    // 关闭键盘
    @objc private func closeKeyboard(notification: Notification) {
        UIView.animate(withDuration:  0.3, animations: {
            self.bottomBarBottomConstraint.constant = 0
        })
    }

    @objc private func receiveMessageFromWs(notice: Notification) {
        DDLogDebug("接收到websocket im 消息")
        if let message = notice.object as? IMMessageInfo {
            if message.conversationId == self.conversation?.id {
                self.chatMessageList.append(message)
                self.scrollMessageToBottom()
                self.viewModel.readConversation(conversationId: self.conversation?.id)
            }
        }
    }
    
    @objc private func clickUpdate() {
        var arr = [
            UIAlertAction(title: "修改群名", style: .default, handler: { (action) in
                self.updateTitle()
            }),
            UIAlertAction(title: "修改成员", style: .default, handler: { (action) in
                self.updatePeople()
            })
        ]
        if imConfig.enableClearMsg == true {
            arr.append(UIAlertAction(title: "清除聊天记录", style: .default, handler: { (action) in
                self.clearAllChatMsg()
            }))
        }
        self.showSheetAction(title: "", message: "选择要修改的项", actions: arr)
    }
    
    @objc private func clearAllChatMsg() {
        self.showDefaultConfirm(title: "提示", message: "确定要清空聊天记录吗，清空后当前会话所有人都将看不到这些聊天记录？") { action in
            // 清空聊天记录
            if let id = self.conversation?.id {
                self.viewModel.clearAllChatMsg(conversationId:  id).then { result in
                    if result {
                        self.showMessage(msg: "清空聊天记录成功！")
                        self.chatMessageList.removeAll()
                        self.tableView.reloadData()
                        self.page = 0
                        self.loadMsgList()
                    } else {
                        self.showError(title: "清空失败！")
                    }
                }
            }
        }
    }
    
    private func updateTitle() {
        self.showPromptAlert(title: "", message: "修改群名", inputText: "") { (action, result) in
            if result.isEmpty {
                self.showError(title: "请输入群名")
            }else {
                self.showLoading()
                self.viewModel.updateConversationTitle(id: (self.conversation?.id!)!, title: result)
                    .then { (c) in
                        self.title = result
                        self.conversation?.title = result
                        self.showSuccess(title: "修改成功")
                }.catch { (err) in
                    DDLogError(err.localizedDescription)
                    self.showError(title: "修改失败")
                }
            }
        }
    }
    
    private func updatePeople() {
        //选择人员 反选已经存在的成员
        if let users = self.conversation?.personList  {
            self.showContactPicker(modes: [.person], callback: { (result) in
                if let people = result.users  {
                    if people.count >= 3 {
                        var peopleDNs: [String] = []
                        var containMe = false
                        people.forEach { (item) in
                            peopleDNs.append(item.distinguishedName!)
                            if O2AuthSDK.shared.myInfo()?.distinguishedName == item.distinguishedName {
                                containMe = true
                            }
                        }
                        if !containMe {
                            peopleDNs.append((O2AuthSDK.shared.myInfo()?.distinguishedName)!)
                        }
                        self.showLoading()
                        self.viewModel.updateConversationPeople(id: (self.conversation?.id!)!, users: peopleDNs)
                            .then { (c)  in
                                self.conversation?.personList = peopleDNs
                                self.showSuccess(title: "修改成功")
                        }.catch { (err) in
                            DDLogError(err.localizedDescription)
                            self.showError(title: "修改失败")
                        }
                    }else {
                        self.showError(title: "选择人数不足3人")
                    }
                }else {
                    self.showError(title: "请选择人员")
                }
            }, initUserPickedArray: users)
        }else {
            self.showError(title: "成员列表数据错误！")
        }
    }

    //获取消息
    private func loadMsgList() {
        if let c = self.conversation, let id = c.id {
            self.viewModel.myMsgPageList(page: self.page + 1, conversationId: id).then { (list) in
                if !list.isEmpty {
                    self.page += 1
                    self.chatMessageList.insert(contentsOf: list, at: 0)
                    if self.page ==  1 {
                        self.scrollMessageToBottom()
                    }else {
                        DispatchQueue.main.async {
                            self.tableView.reloadData()
                        }
                    }
                }
                if self.tableView.mj_header.isRefreshing(){
                    self.tableView.mj_header.endRefreshing()
                }
                
            }.catch { (error) in
                DDLogError(error.localizedDescription)
                if self.tableView.mj_header.isRefreshing(){
                    self.tableView.mj_header.endRefreshing()
                }
            }
        } else {
            self.showError(title: "参数错误！！！")
        }
    }
    //刷新tableview 滚动到底部
    private func scrollMessageToBottom() {
        DispatchQueue.main.async {
            self.tableView.reloadData()
            if self.chatMessageList.count > 0 {
                self.tableView.scrollToRow(at: IndexPath(row: self.chatMessageList.count - 1, section: 0), at: .bottom, animated: false)
            }
        }
    }

    //发送文本消息
    private func sendTextMessage() {
        guard let msg = self.messageInputView.text else {
            return
        }
        self.messageInputView.text = ""
        let body = IMMessageBodyInfo()
        body.type = o2_im_msg_type_text
        body.body = msg
        sendMessage(body: body)
    }
    //发送表情消息
    private func sendEmojiMessage(emoji: String) {
        let body = IMMessageBodyInfo()
        body.type = o2_im_msg_type_emoji
        body.body = emoji
        sendMessage(body: body)
    }

    //发送地图消息消息
    private func sendLocationMessage(loc: O2LocationData) {
        let body = IMMessageBodyInfo()
        body.type = o2_im_msg_type_location
        body.body = o2_im_msg_body_location
        body.address = loc.address
        body.addressDetail = loc.addressDetail
        body.longitude = loc.longitude
        body.latitude = loc.latitude
        sendMessage(body: body)
    }

    //发送消息到服务器
    private func sendMessage(body: IMMessageBodyInfo) {
        let message = IMMessageInfo()
        message.body = body.toJSONString()
        message.id = UUID().uuidString
        message.conversationId = self.conversation?.id
        message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
        message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
        //添加到界面
        self.chatMessageList.append(message)
        self.scrollMessageToBottom()
        //发送消息到服务器
        self.viewModel.sendMsg(msg: message)
            .then { (result) in
                DDLogDebug("发送消息成功 \(result)")
                self.viewModel.readConversation(conversationId: self.conversation?.id)
            }.catch { (error) in
                DDLogError(error.localizedDescription)
                self.showError(title: "发送消息失败!")
        }
    }

    //选择照片
    private func chooseImage() {
        self.choosePhotoWithImagePicker { (fileName, newData) in
            let localFilePath = self.storageLocalImage(imageData: newData, fileName: fileName)
            let msgId = self.prepareForSendImageMsg(filePath: localFilePath)
            self.uploadFileAndSendMsg(messageId: msgId, data: newData, fileName: fileName, type: o2_im_msg_type_image)
        }         
    }
    //临时存储本地
    private func storageLocalImage(imageData: Data, fileName: String) -> String {
        let fileTempPath = FileUtil.share.cacheDir().appendingPathComponent(fileName)
        do {
            try imageData.write(to: fileTempPath)
            return fileTempPath.path
        } catch {
            print(error.localizedDescription)
            return fileTempPath.path
        }
    }
    //发送消息前 先载入界面
    private func prepareForSendImageMsg(filePath: String) -> String {
        let body = IMMessageBodyInfo()
        body.type = o2_im_msg_type_image
        body.body = o2_im_msg_body_image
        body.fileTempPath = filePath
        let message = IMMessageInfo()
        let msgId = UUID().uuidString
        message.body = body.toJSONString()
        message.id = msgId
        message.conversationId = self.conversation?.id
        message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
        message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
        //添加到界面
        self.chatMessageList.append(message)
        self.scrollMessageToBottom()
        return msgId
    }
    
    private func prepareForSendFileMsg(filePath: String) ->  String {
        let body = IMMessageBodyInfo()
        body.type = o2_im_msg_type_file
        body.body = o2_im_msg_body_file
        body.fileTempPath = filePath
        let message = IMMessageInfo()
        let msgId = UUID().uuidString
        message.body = body.toJSONString()
        message.id = msgId
        message.conversationId = self.conversation?.id
        message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
        message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
        //添加到界面
        self.chatMessageList.append(message)
        self.scrollMessageToBottom()
        return msgId
    }

    //发送消息前 先载入界面
    private func prepareForSendAudioMsg(tempMessage: IMMessageBodyInfo) -> String {
        let message = IMMessageInfo()
        let msgId = UUID().uuidString
        message.body = tempMessage.toJSONString()
        message.id = msgId
        message.conversationId = self.conversation?.id
        message.createPerson = O2AuthSDK.shared.myInfo()?.distinguishedName
        message.createTime = Date().formatterDate(formatter: "yyyy-MM-dd HH:mm:ss")
        //添加到界面
        self.chatMessageList.append(message)
        self.scrollMessageToBottom()
        return msgId
    }

     

    //上传图片 音频 文档 等文件到服务器并发送消息
    private func uploadFileAndSendMsg(messageId: String, data: Data, fileName: String, type: String) {
        guard let cId = self.conversation?.id else {
            return
        }
        self.viewModel.uploadFile(conversationId: cId, type: type, fileName: fileName, file: data).then { back in
            DDLogDebug("上传文件成功")
            guard let message = self.chatMessageList.first (where: { (info) -> Bool in
                return info.id == messageId
            }) else {
                DDLogDebug("没有找到对应的消息")
                return
            }
            let body = IMMessageBodyInfo.deserialize(from: message.body)
            body?.fileId = back.id
            body?.fileExtension = back.fileExtension
            body?.fileName = back.fileName
            body?.fileTempPath = nil
            message.body = body?.toJSONString()
            //发送消息到服务器
            self.viewModel.sendMsg(msg: message)
                .then { (result) in
                    DDLogDebug("消息 发送成功 \(result)")
                    self.viewModel.readConversation(conversationId: self.conversation?.id)
                }.catch { (error) in
                    DDLogError(error.localizedDescription)
                    self.showError(title: "发送消息失败!")
            }
        }.catch { err in
            self.showError(title: "上传错误，\(err.localizedDescription)")
        }
    }
    
    // 选择外部文件
    private func chooseFile() {
        let documentTypes = ["public.content",
                                 "public.text",
                                 "public.source-code",
                                 "public.image",
                                 "public.audiovisual-content",
                                 "com.adobe.pdf",
                                 "com.apple.keynote.key",
                                 "com.microsoft.word.doc",
                                 "com.microsoft.excel.xls",
                                 "com.microsoft.powerpoint.ppt"]
        let picker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .open)
        picker.delegate = self
        self.present(picker, animated: true, completion: nil)
    }
    
    private func playAudioGif(id: String?) {
        self.playingAudioMessageId = id
        self.tableView.reloadData()
    }
    
    private func stopPlayAudioGif() {
        self.playingAudioMessageId = nil
        self.tableView.reloadData()
    }
    
    // 播放audio
    private func playAudio(url: URL) {
        do {
            let data = try Data(contentsOf: url)
            AudioPlayerManager.shared.managerAudioWithData(data, toplay: true)
        } catch {
            DDLogError(error.localizedDescription)
        }
    }


    // MARK: - IBAction
    //点击表情按钮
    @IBAction func clickEmojiBtn(_ sender: UIButton) {
        self.isShowEmoji.toggle()
        self.view.endEditing(true)
        if self.isShowEmoji {
            //audio view 先关闭
            self.isShowAudioView = false
            self.audioBtnView.removeFromSuperview()
            //开始添加emojiBar
            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
            self.emojiBar.delegate = self
            self.emojiBar.translatesAutoresizingMaskIntoConstraints = false
            self.bottomBar.addSubview(self.emojiBar)
            let top = NSLayoutConstraint(item: self.emojiBar, attribute: .top, relatedBy: .equal, toItem: self.emojiBar.superview!, attribute: .top, multiplier: 1, constant: CGFloat(self.bottomBarHeight))
            let width = NSLayoutConstraint(item: self.emojiBar, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: SCREEN_WIDTH)
            let height = NSLayoutConstraint(item: self.emojiBar, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.emojiBarHeight.toCGFloat)
            NSLayoutConstraint.activate([top, width, height])
        } else {
            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
            self.emojiBar.removeFromSuperview()
        }
        self.view.layoutIfNeeded()
    }

    @IBAction func micBtnClick(_ sender: UIButton) {
        DDLogDebug("点击了麦克风按钮")
        self.isShowAudioView.toggle()
        self.view.endEditing(true)
        if self.isShowAudioView {
            //emoji view 先关闭
            self.isShowEmoji = false
            self.emojiBar.removeFromSuperview()
            //开始添加emojiBar
            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat + self.emojiBarHeight.toCGFloat
            self.audioBtnView.translatesAutoresizingMaskIntoConstraints = false
            self.bottomBar.addSubview(self.audioBtnView)
            let top = NSLayoutConstraint(item: self.audioBtnView, attribute: .top, relatedBy: .equal, toItem: self.audioBtnView.superview!, attribute: .top, multiplier: 1, constant: CGFloat(self.bottomBarHeight))
            let width = NSLayoutConstraint(item: self.audioBtnView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: SCREEN_WIDTH)
            let height = NSLayoutConstraint(item: self.audioBtnView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.emojiBarHeight.toCGFloat)
            NSLayoutConstraint.activate([top, width, height])
        } else {
            self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
            self.audioBtnView.removeFromSuperview()
        }
        self.view.layoutIfNeeded()
    }

    @IBAction func imgBtnClick(_ sender: UIButton) {
        DDLogDebug("点击了图片按钮")
        self.chooseImage()
    }
    @IBAction func cameraBtnClick(_ sender: UIButton) {
        DDLogDebug("点击了相机按钮")
        self.takePhoto(delegate: self)
    }
    @IBAction func locationBtnClick(_ sender: UIButton) {
        DDLogDebug("点击了位置按钮")
        let vc = IMLocationChooseController.openChooseLocation { (data) in
            self.sendLocationMessage(loc: data)
        }
        self.navigationController?.pushViewController(vc, animated: false)
    }

    @IBAction func fileBtnClick(_ sender: UIButton) {
        DDLogDebug("点击了文件按钮")
        self.chooseFile()
    }
    
    // MARK: - UIMenuController 消息操作
    
    /// 长按事件
    @objc private func longpressEvent(gestureRecognizer: UILongPressGestureRecognizer) {
        if (gestureRecognizer.state == .began) {
            if let cell = gestureRecognizer.view {
                if cell.isKind(of: IMChatMessageSendViewCell.self), let myCell = cell as? IMChatMessageSendViewCell {
                    myCell.becomeFirstResponder()
                    self.showMenu(frame: myCell.frame, msg: myCell.msgInfo)
                }
                if cell.isKind(of: IMChatMessageViewCell.self), let myCell = cell as? IMChatMessageViewCell {
                    myCell.becomeFirstResponder()
                    self.showMenu(frame: myCell.frame, msg: myCell.msgInfo)
                }
            }
        }
    }
    
   
    /// 展现菜单
    private func showMenu(frame: CGRect, msg: IMMessageInfo?) {
        //定义菜单
        var menus:[UIMenuItem] = []
        if self.imConfig.enableRevokeMsg == true, let cp = msg?.createPerson {
            if cp == O2AuthSDK.shared.myInfo()?.distinguishedName {
                //发送者
                menus.append(UIMenuItem(title: "撤回", action: #selector(revokeMsg)))
            } else if self.conversation?.type == o2_im_conversation_type_group &&
                    O2AuthSDK.shared.myInfo()?.distinguishedName == self.conversation?.adminPerson {
                // 群主
                menus.append(UIMenuItem(title: "撤回成员消息", action: #selector(revokeMsg)))
            }
        }
        // 文字消息 添加复制按钮
        if let body = msg?.body, let bodyInfo = IMMessageBodyInfo.deserialize(from: body) {
            if bodyInfo.type == o2_im_msg_type_text {
                menus.append(UIMenuItem(title: "复制", action: #selector(copyTextMsg)))
            }
        }
        if menus.count > 0 {
            self.currentSelectMsg = msg
            UIMenuController.shared.setTargetRect(frame, in: self.tableView)
            UIMenuController.shared.menuItems = menus
            UIMenuController.shared.update()
            UIMenuController.shared.setMenuVisible(true, animated: true)
            DDLogDebug("showMenu")
        }
    }
    
    /// 撤回菜单
    @objc private func revokeMsg() {
        DDLogDebug("点击了撤回消息")
        if let msg = self.currentSelectMsg, let id = msg.id {
            DDLogDebug("撤回消息，id: \(id)")
            self.viewModel.revokeChatMsg(msgId:  id).then { result in
                if result {
                    self.showMessage(msg: "撤回成功！")
                    var newList: [IMMessageInfo] = []
                    for item in self.chatMessageList {
                        if item.id != id {
                            newList.append(item)
                        }
                    }
                    self.chatMessageList = newList
                    self.tableView.reloadData()
                } else {
                    self.showError(title: "撤回失败！")
                }
            }
        }
    }
    
    // 复制文字消息
    @objc private func copyTextMsg() {
        DDLogDebug("复制文字消息")
        if let msg = self.currentSelectMsg, let body = msg.body, let bodyInfo = IMMessageBodyInfo.deserialize(from: body) {
            UIPasteboard.general.string = bodyInfo.body
            self.showSuccess(title: "复制成功！")
        }
    }
    
}

// MARK: - 外部文件选择代理
extension IMChatViewController: UIDocumentPickerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        if urls.count > 0 {
            let file = urls[0]
            if file.startAccessingSecurityScopedResource() { //访问权限
                let fileName = file.lastPathComponent
                if let data = try? Data(contentsOf: file) {
                    let fileext = file.pathExtension
                    if O2.isImageExt(fileext) { // 图片消息
                        let localFilePath = self.storageLocalImage(imageData: data, fileName: fileName)
                        let msgId = self.prepareForSendImageMsg(filePath: localFilePath)
                        self.uploadFileAndSendMsg(messageId: msgId, data: data, fileName: fileName, type: o2_im_msg_type_image)
                    }else { // 文件消息
                        let localFilePath = self.storageLocalImage(imageData: data, fileName: fileName)
                        let msgId = self.prepareForSendFileMsg(filePath: localFilePath)
                        self.uploadFileAndSendMsg(messageId: msgId, data: data, fileName: fileName, type: o2_im_msg_type_file)
                    }
                } else {
                    self.showError(title: "读取文件失败")
                }
            } else {
                self.showError(title: "没有获取文件的权限")
            }
        }
        
    }

}

// MARK: - 录音delegate
extension IMChatViewController: IMChatAudioViewDelegate {
    
    private func audioRecordingGif() -> UIImage? {
        let url: URL? = Bundle.main.url(forResource: "listener08_anim", withExtension: "gif")
        guard let u = url else {
            return nil
        }
        guard let data = try? Data.init(contentsOf: u) else {
            return nil
        }
        return UIImage.sd_animatedGIF(with: data)
    }
    
    func showAudioRecordingView() {
        if self.voiceIconImage == nil {
            self.voiceImageSuperView = UIView()
            self.view.addSubview(self.voiceImageSuperView!)
            self.voiceImageSuperView?.backgroundColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.6)
             
            self.voiceImageSuperView?.snp_makeConstraints { (make) in
                make.center.equalTo(self.view)
                make.size.equalTo(CGSize(width:140, height:140))
            }
        
            self.voiceIconImage = UIImageView()
            self.voiceImageSuperView?.addSubview(self.voiceIconImage!)
            self.voiceIconImage?.snp_makeConstraints { (make) in
                make.top.left.equalTo(self.voiceImageSuperView!).inset(UIEdgeInsets(top: 20, left: 35, bottom: 0, right: 0))
                make.size.equalTo(CGSize(width: 70, height: 70))
            }
            let voiceIconTitleLabel = UILabel()
            self.voiceIocnTitleLable = voiceIconTitleLabel
            self.voiceIconImage?.addSubview(voiceIconTitleLabel)
            voiceIconTitleLabel.textColor = UIColor.white
            voiceIconTitleLabel.font = .systemFont(ofSize: 12)
            voiceIconTitleLabel.text = "松开发送，上滑取消"
            voiceIconTitleLabel.snp_makeConstraints { (make) in
                make.bottom.equalTo(self.voiceImageSuperView!).offset(-15)
                make.centerX.equalTo(self.voiceImageSuperView!)
            }
        }
        self.voiceImageSuperView?.isHidden = false
        if let gifImage = self.audioRecordingGif() {
            self.voiceIconImage?.image = gifImage
        }else {
            self.voiceIconImage?.image = UIImage(named: "chat_audio_voice")
        }
        self.voiceIocnTitleLable?.text = "松开发送，上滑取消";
       
    }
    
    func hideAudioRecordingView() {
        self.voiceImageSuperView?.isHidden = true
    }
    
    func changeRecordingView2uplide() {
        self.voiceIocnTitleLable?.text = "松开手指，取消发送";
        self.voiceIconImage?.image = UIImage(named: "chat_audio_cancel")
    }
    
    func changeRecordingView2down() {
        if let gifImage = self.audioRecordingGif() {
            self.voiceIconImage?.image = gifImage
        }else {
            self.voiceIconImage?.image = UIImage(named: "chat_audio_voice")
        }
        self.voiceIocnTitleLable?.text = "松开发送，上滑取消";
    }
    
    func sendVoice(path: String, voice: Data, duration: String) {
        let msg = IMMessageBodyInfo()
        msg.fileTempPath = path
        msg.body = o2_im_msg_body_audio
        msg.type = o2_im_msg_type_audio
        msg.audioDuration = duration
        let msgId = self.prepareForSendAudioMsg(tempMessage: msg)
        let fileName = path.split("/").last ?? "MySound.ilbc"
        DDLogDebug("音频文件：\(fileName)")
        self.uploadFileAndSendMsg(messageId: msgId, data: voice, fileName: fileName, type: o2_im_msg_type_audio)
    }
    
}

// MARK: - 拍照delegate
extension IMChatViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        if let image = info[.editedImage] as? UIImage {
            let fileName = "\(UUID().uuidString).png"
            let newData = image.pngData()!
            let localFilePath = self.storageLocalImage(imageData: newData, fileName: fileName)
            let msgId = self.prepareForSendImageMsg(filePath: localFilePath)
            self.uploadFileAndSendMsg(messageId: msgId, data: newData, fileName: fileName, type: o2_im_msg_type_image)
        } else {
            DDLogError("没有选择到图片！")
        }
        picker.dismiss(animated: true, completion: nil)
//        var newData = data
//        //处理图片旋转的问题
//        if imageOrientation != UIImage.Orientation.up {
//            let newImage = UIImage(data: data)?.fixOrientation()
//            if newImage != nil {
//                newData = newImage!.pngData()!
//            }
//        }
//        var fileName = ""
//        if dict?["PHImageFileURLKey"] != nil {
//            let fileURL = dict?["PHImageFileURLKey"] as! URL
//            fileName = fileURL.lastPathComponent
//        } else {
//            fileName = "\(UUID().uuidString).png"
//        }
    }

}

// MARK: - audio 播放 delegate
extension IMChatViewController: AudioPlayerManagerDelegate {
    func didAudioPlayerBeginPlay(_ AudioPlayer: AVAudioPlayer) {
        DDLogDebug("播放开始")
    }
    
    func didAudioPlayerStopPlay(_ AudioPlayer: AVAudioPlayer) {
        DDLogDebug("播放结束")
        self.stopPlayAudioGif()
    }
    
    func didAudioPlayerPausePlay(_ AudioPlayer: AVAudioPlayer) {
        DDLogDebug("播放暂停")
    }
    
    
}

// MARK: - 消息点击 delegate
extension IMChatViewController: IMChatMessageDelegate {
    
    func openApplication(storyboard: String) {
        if storyboard == "mind" {
//            let flutterViewController = O2FlutterViewController()
//            flutterViewController.setInitialRoute("mindMap")
//            flutterViewController.modalPresentationStyle = .fullScreen
//            self.present(flutterViewController, animated: false, completion: nil)
        }else {
            let storyBoard = UIStoryboard(name: storyboard, bundle: nil)
            guard let destVC = storyBoard.instantiateInitialViewController() else {
                return
            }
            destVC.modalPresentationStyle = .fullScreen
            if destVC.isKind(of: ZLNavigationController.self) {
                self.show(destVC, sender: nil)
            }else{
                self.navigationController?.pushViewController(destVC, animated: true)
            }
        }
    }
    
    func openWork(workId: String) {
        self.openWorkPage(work: workId)
        // 已经支持 未结束和结束的工作打开
//        self.showLoading()
//        self.viewModel.isWorkCompleted(work: workId).always {
//            self.hideLoading()
//        }.then{ result in
//            if result {
//                self.showMessage(msg: "工作已经完成了！")
//            }else {
//                self.openWorkPage(work: workId)
//            }
//        }.catch {_ in
//            self.showMessage(msg: "工作已经完成了！")
//        }
        
        
    }
    
    private func openWorkPage(work: String) {
        let storyBoard = UIStoryboard(name: "task", bundle: nil)
        let destVC = storyBoard.instantiateViewController(withIdentifier: "todoTaskDetailVC") as! TodoTaskDetailViewController
        let json = """
        {"work":"\(work)", "workCompleted":"", "title":""}
        """
        let todo = TodoTask(JSONString: json)
        destVC.todoTask = todo
        destVC.backFlag = 3 //隐藏就行
        self.show(destVC, sender: nil)
    }
    
    
    func openLocatinMap(info: IMMessageBodyInfo) {
        IMShowLocationViewController.pushShowLocation(vc: self, latitude: info.latitude, longitude: info.longitude,
                                                      address: info.address, addressDetail: info.addressDetail)
    }
    
    func openImageOrFileMessage(info: IMMessageBodyInfo) {
        if let id = info.fileId {
            self.showLoading()
            var ext = info.fileExtension ?? "png"
            if ext.isEmpty {
                ext = "png"
            }
            O2IMFileManager.shared
                .getFileLocalUrl(fileId: id, fileExtension: ext)
                .always {
                    self.hideLoading()
                }.then { (path) in
                    let currentURL = NSURL(fileURLWithPath: path.path)
                    DDLogDebug(currentURL.description)
                    DDLogDebug(path.path)
                    if QLPreviewController.canPreview(currentURL) {
                        self.previewVC.currentFileURLS.removeAll()
                        self.previewVC.currentFileURLS.append(currentURL)
                        self.previewVC.reloadData()
                        self.pushVC(self.previewVC)
                    } else {
                        self.showError(title: "当前文件类型不支持预览！")
                    }
                }
                .catch { (error) in
                    DDLogError(error.localizedDescription)
                    self.showError(title: "获取文件异常！")
            }
        } else if let temp = info.fileTempPath {
            let currentURL = NSURL(fileURLWithPath: temp)
            DDLogDebug(currentURL.description)
            DDLogDebug(temp)
            if QLPreviewController.canPreview(currentURL) {
                self.previewVC.currentFileURLS.removeAll()
                self.previewVC.currentFileURLS.append(currentURL)
                self.previewVC.reloadData()
                self.pushVC(self.previewVC)
            } else {
                self.showError(title: "当前文件类型不支持预览！")
            }
        }
    }
     
    func playAudio(info: IMMessageBodyInfo, id: String?) {
        if self.playingAudioMessageId != nil && self.playingAudioMessageId == id {
            DDLogError("正在播放中。。。。。")
            return
        }
        if let fileId = info.fileId {
            var ext = info.fileExtension ?? "mp3"
            if ext.isEmpty {
                ext = "mp3"
            }
            O2IMFileManager.shared.getFileLocalUrl(fileId: fileId, fileExtension: ext)
                .then { (url) in
                    self.playAudio(url: url)
            }.catch { (e) in
                DDLogError(e.localizedDescription)
            }
        } else if let filePath = info.fileTempPath {
            self.playAudio(url: URL(fileURLWithPath: filePath))
        }
        self.playAudioGif(id: id)
    }
    
    
}

// MARK: - 表情点击 delegate
extension IMChatViewController: IMChatEmojiBarClickDelegate {
    func clickEmoji(emoji: String) {
        DDLogDebug("发送表情消息 \(emoji)")
        self.sendEmojiMessage(emoji: emoji)
    }
}

// MARK: - tableview delegate
extension IMChatViewController: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.chatMessageList.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let msg = self.chatMessageList[indexPath.row]
        let isPlaying = self.playingAudioMessageId == nil ? false : (self.playingAudioMessageId == msg.id)
        if msg.createPerson == O2AuthSDK.shared.myInfo()?.distinguishedName { //发送者
            if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageSendViewCell", for: indexPath) as? IMChatMessageSendViewCell {
                cell.setContent(item: self.chatMessageList[indexPath.row], isPlayingAudio: isPlaying)
                cell.delegate = self
                let longpress = UILongPressGestureRecognizer()
                longpress.addTarget(self, action: #selector(longpressEvent))
                cell.addGestureRecognizer(longpress)
                return cell
            }
        } else {
            if let cell = tableView.dequeueReusableCell(withIdentifier: "IMChatMessageViewCell", for: indexPath) as? IMChatMessageViewCell {
                cell.setContent(item: self.chatMessageList[indexPath.row], isPlayingAudio: isPlaying)
                cell.delegate = self
                let longpress = UILongPressGestureRecognizer()
                longpress.addTarget(self, action: #selector(longpressEvent))
                cell.addGestureRecognizer(longpress)
                return cell
            }
        }
        return UITableViewCell()
    }
    

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let msg = self.chatMessageList[indexPath.row]
        return cellHeight(item: msg)
    }
    
    func cellHeight(item: IMMessageInfo) -> CGFloat {
        if let jsonBody = item.body, let body = IMMessageBodyInfo.deserialize(from: jsonBody){
            if body.type == o2_im_msg_type_emoji {
                 // 上边距 69 + emoji高度 + 内边距 + 底部空白高度
                 return 69 + 36 + 20 + 10
            } else if body.type == o2_im_msg_type_image {
                 // 上边距 69 + 图片高度 + 内边距 + 底部空白高度
                 return 69 + 192 + 20 + 10
            } else if o2_im_msg_type_audio == body.type {
                 // 上边距 69 + audio高度 + 内边距 + 底部空白高度
                 return 69 + IMAudioView.IMAudioView_height + 20 + 10
            } else if o2_im_msg_type_location == body.type {
                 // 上边距 69 + 位置图高度 + 内边距 + 底部空白高度
                 return 69 + IMLocationView.IMLocationViewHeight + 20 + 10
            }  else if o2_im_msg_type_file == body.type {
                
                return 69 + IMFileView.IMFileView_height + 20 + 10
            }   else if o2_im_msg_type_process == body.type {
               
                return 69 + IMProcessCardView.IMProcessCardView_height + 20 + 10
            } else {
                if let bodyText = body.body {
                    let size = bodyText.getSizeWithMaxWidth(fontSize: 16, maxWidth: messageWidth)
                    // 上边距 69 + 文字高度 + 内边距 + 底部空白高度
                    return 69 + size.height + 28 + 10
                }
            }
        }
        return 132
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: false)
    }

}

// MARK: - textField delegate
extension IMChatViewController: UITextFieldDelegate {
    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        DDLogDebug("准备开始输入......")
        closeOtherView()
        return true
    }

    private func closeOtherView() {
        self.isShowEmoji = false
        self.isShowAudioView = false
        self.bottomBarHeightConstraint.constant = self.bottomBarHeight.toCGFloat
        self.view.layoutIfNeeded()
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        DDLogDebug("回车。。。。")
        self.sendTextMessage()
        return true
    }
}
