import ConnectionStatus from "./wfc/client/connectionStatus";
import Vue from 'vue'
import wfc from "./wfc/client/wfc";
import EventType from "./wfc/client/wfcEvent";
import ConversationType from "./wfc/model/conversationType";
import {eq, gt, lt, numberValue} from "./wfc/util/longUtil";
import helper from "./ui/util/helper";
import convert from './vendor/pinyin'
import GroupType from "./wfc/model/groupType";
import {imageThumbnail, videoDuration, videoThumbnail} from "./ui/util/imageUtil";
import MessageContentMediaType from "./wfc/messages/messageContentMediaType";
import Conversation from "./wfc/model/conversation";
import MessageContentType from "./wfc/messages/messageContentType";
import MessageStatus from './wfc/messages/messageStatus';
import Message from "./wfc/messages/message";
import ImageMessageContent from "./wfc/messages/imageMessageContent";
import VideoMessageContent from "./wfc/messages/videoMessageContent";
import FileMessageContent from "./wfc/messages/fileMessageContent";
import Push from "push.js";
import MessageConfig from "./wfc/client/messageConfig";
import PersistFlag from "./wfc/messages/persistFlag";
import ForwardType from "./ui/main/conversation/message/forward/ForwardType";
import TextMessageContent from "./wfc/messages/textMessageContent";
import {currentWindow, ipcRenderer, isElectron, remote} from "./platform";
import SearchType from "./wfc/model/searchType";
import Config from "./config";
import {getItem, setItem} from "./ui/util/storageHelper";
import CompositeMessageContent from "./wfc/messages/compositeMessageContent";
import {stringValue, longValue} from "./wfc/util/longUtil";
import DismissGroupNotification from "./wfc/messages/notification/dismissGroupNotification";
import KickoffGroupMemberNotification from "./wfc/messages/notification/kickoffGroupMemberNotification";
import QuitGroupNotification from "./wfc/messages/notification/quitGroupNotification";
import avenginekitproxy from "./wfc/av/engine/avenginekitproxy";
import MediaMessageContent from "./wfc/messages/mediaMessageContent";
import UnreadCount from "./wfc/model/unreadCount";
import LeaveChannelChatMessageContent from "./wfc/messages/leaveChannelChatMessageContent";
import EnterChannelChatMessageContent from "./wfc/messages/enterChannelChatMessageContent";
import ArticlesMessageContent from "./wfc/messages/articlesMessageContent";
import NullUserInfo from "./wfc/model/nullUserInfo";
import NullGroupInfo from "./wfc/model/nullGroupInfo";
import IPCEventType from "./ipcEventType";
import NullChannelInfo from "./wfc/model/NullChannelInfo";
import ModifyGroupSettingNotification from "./wfc/messages/notification/modifyGroupSettingNotification";
import {storeToRefs} from 'pinia'
import {pstore} from './pstore'

import CallStartMessageContent from "./wfc/av/messages/callStartMessageContent";
import SoundMessageContent from "./wfc/messages/soundMessageContent";
import MixMultiMediaTextMessageContent from "./wfc/messages/mixMultiMediaTextMessageContent";
import MixFileTextMessageContent from "./wfc/messages/mixFileTextMessageContent";

/**
 * 一些说明
 * _开头的字段，是为了UI层展示方便，而打补丁上出去的
 * __开头的字段，仅内部使用
 * _开头的函数，是内部函数
 * 外部不直接更新字段，而是通过提供各种action方法更新
 */

let conversationState;
let contactState;
let searchState;
let pickState;
let miscState;

let store = {
    debug: true,
    state: {
        conversation: null,
        contact: null,
        search: null,
        pick: null,
        misc: null,
    },

    init(isMainWindow, subWindowLoadDataOptions) {
        console.log('init store')

        const {conversationStore, contactStore, pickStore, searchStore, miscStore} = storeToRefs(pstore())
        conversationState = conversationStore.value;
        contactState = contactStore.value;
        searchState = searchStore.value;
        pickState = pickStore.value;
        miscState = miscStore.value;

        store.state.conversation = conversationState;
        store.state.contact = contactState;
        store.state.search = searchState;
        store.state.pick = pickState;
        store.state.misc = miscState;

        miscState.connectionStatus = wfc.getConnectionStatus();
        wfc.eventEmitter.on(EventType.ConnectionStatusChanged, (status) => {
            console.log('store ConnectionStatusChanged', status)
            contactState.isEnableMesh = wfc.isEnableMesh();
            miscState.connectionStatus = status;
            miscState.isCommercialServer = wfc.isCommercialServer();
            miscState.isDisableSyncDraft = wfc.isDisableSyncDraft();
            try {
                if (status === ConnectionStatus.ConnectionStatusConnected) {
                    this._loadDefaultData();

                    this.updateTray();
                } else if (status === ConnectionStatus.ConnectionStatusLogout
                    || status === ConnectionStatus.ConnectionStatusRejected
                    || status === ConnectionStatus.ConnectionStatusSecretKeyMismatch
                    || status === ConnectionStatus.ConnectionStatusKickedOff
                    || status === ConnectionStatus.ConnectionStatusTokenIncorrect) {
                    _reset();
                    this.updateTray();
                }
            } catch (e) {
                // do nothing
            }
        });

        wfc.eventEmitter.on(EventType.UserInfosUpdate, (userInfos) => {
            console.log('store UserInfosUpdate', userInfos, miscState.connectionStatus)
            this._reloadSingleConversationIfExist(userInfos);
            // TODO optimize
            this._patchCurrentConversationMessages();
            this._loadFriendList();
            this._loadFriendRequest();
            this._loadSelfUserInfo();
            // TODO 其他相关逻辑
        });

        wfc.eventEmitter.on(EventType.SettingUpdate, () => {
            console.log('store SettingUpdate')
            this._loadDefaultConversationList();
            this._loadFavContactList();
            this._loadFavGroupList();
            this._loadChannelList();
            this.updateTray();
            // 清除远程消息时，WEB SDK会同时触发ConversationInfoUpdate 和 setting更新，但PC SDK不会，只会触发setting更新
            // if (isElectron()) {
            //     this._loadCurrentConversationMessages();
            // }
        });

        wfc.eventEmitter.on(EventType.FriendRequestUpdate, (newFrs) => {
            this._loadFriendRequest();
        });

        wfc.eventEmitter.on(EventType.FriendListUpdate, (updatedFriendIds) => {
            console.log('FriendListUpdate', updatedFriendIds);
            this._loadFriendList();
            this._loadFriendRequest();
            this._loadFavContactList();
            this._loadDefaultConversationList();
            this._patchCurrentConversationMessages();
        });

        wfc.eventEmitter.on(EventType.GroupInfosUpdate, (groupInfos) => {
            // TODO optimize
            console.log('store GroupInfosUpdate', groupInfos)
            this._reloadGroupConversationIfExist(groupInfos);
            this._loadFavGroupList();
            // TODO 其他相关逻辑

        });

        wfc.eventEmitter.on(EventType.GroupMembersUpdate, (groupId, members) => {
            // TODO optimize
            console.log('store GroupMembersUpdate', groupId)
            this._reloadGroupConversationIfExist([new NullGroupInfo(groupId)]);
            // this._loadFavGroupList();
            if (conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.type === ConversationType.Group
                && conversationState.currentConversationInfo.conversation.target === groupId) {
                this._patchCurrentConversationMessages();
            }

            // TODO 其他相关逻辑
        });

        wfc.eventEmitter.on(EventType.ChannelInfosUpdate, (groupInfos) => {
            this._loadDefaultConversationList();
            this._loadChannelList();
        });

        wfc.eventEmitter.on(EventType.ConversationInfoUpdate, (conversationInfo) => {
            this._reloadConversation(conversationInfo.conversation)
            // if (conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.equal(conversationInfo.conversation)) {
            //     this._loadCurrentConversationMessages();
            // }
            // 标记已读未读
            this.updateTray();
        });

        wfc.eventEmitter.on(EventType.ReceiveMessage, (msg, hasMore) => {
            if (miscState.connectionStatus === ConnectionStatus.ConnectionStatusReceiveing) {
                return;
            }
            if (miscState.isMainWindow && !this.isConversationInCurrentWindow(msg.conversation)) {
                return;
            }
            if (msg.messageContent instanceof DismissGroupNotification
                || (msg.messageContent instanceof KickoffGroupMemberNotification && msg.messageContent.kickedMembers.indexOf(wfc.getUserId()) >= 0)
                || (msg.messageContent instanceof QuitGroupNotification && msg.messageContent.operator === wfc.getUserId())
            ) {
                this.setCurrentConversationInfo(null);
                return;
            }

            if (!hasMore) {
                this._reloadConversation(msg.conversation)
            }
            if (conversationState.currentConversationInfo && msg.conversation.equal(conversationState.currentConversationInfo.conversation)) {
                if (msg.messageContent.type === MessageContentType.Typing) {
                    let groupId = msg.conversation.type === 1 ? msg.conversation.target : '';
                    let userInfo = wfc.getUserInfo(msg.from, false, groupId)
                    userInfo = Object.assign({}, userInfo);
                    userInfo._displayName = wfc.getGroupMemberDisplayNameEx(userInfo);
                    conversationState.inputtingUser = userInfo;

                    if (!conversationState.inputClearHandler) {
                        conversationState.inputClearHandler = () => {
                            conversationState.inputtingUser = null;
                        }
                    }
                    clearTimeout(conversationState.inputClearHandler);
                    setTimeout(conversationState.inputClearHandler, 3000)
                } else {
                    clearTimeout(conversationState.inputClearHandler);
                    conversationState.inputtingUser = null;
                }

                if (!this._isDisplayMessage(msg) || msg.messageContent.type === MessageContentType.RecallMessage_Notification) {
                    return;
                }

                if (msg.content.notLoaded) {
                    console.log('message not loaded, reset conversation message list')
                    conversationState.currentConversationMessageList.length = 0
                    conversationState.currentConversationOldestMessageId = 0
                    conversationState.currentConversationOldestMessageUid = 0
                    return;
                }
                // 会把下来加载更多加载的历史消息给清理了
                let lastTimestamp = 0;
                let msgListLength = conversationState.currentConversationMessageList.length;
                if (msgListLength > 0) {
                    lastTimestamp = conversationState.currentConversationMessageList[msgListLength - 1].timestamp;
                }
                this._patchMessage(msg, lastTimestamp);
                let msgIndex = conversationState.currentConversationMessageList.findIndex(m => {
                    return m.messageId === msg.messageId
                        || (gt(m.messageUid, 0) && eq(m.messageUid, msg.messageUid))
                        || (m.messageContent.type === MessageContentType.Streaming_Text_Generating
                            && (msg.messageContent.type === MessageContentType.Streaming_Text_Generating || msg.messageContent.type === MessageContentType.Streaming_Text_Generated)
                            && m.messageContent.streamId === msg.messageContent.streamId
                        )
                });
                if (msgIndex > -1) {
                    // FYI: https://v2.vuejs.org/v2/guide/reactivity#Change-Detection-Caveats
                    conversationState.currentConversationMessageList.splice(msgIndex, 1, msg);
                    console.log('msg duplicate, update message')
                    return;
                } else {
                    let firstMsg = conversationState.currentConversationMessageList[0];
                    if(firstMsg && lt(msg.timestamp, firstMsg.timestamp)) {
                        console.log('msg timestamp is less than first msg, maybe update old message content, ignore')
                        return;
                    }
                }

                conversationState.currentConversationMessageList.push(msg);
            }

            if (miscState.isMainWindow && this.isConversationInCurrentWindow(msg.conversation)) {
                if (msg.conversation.type !== 2 && miscState.isPageHidden && (miscState.enableNotification || msg.status === MessageStatus.AllMentioned || msg.status === MessageStatus.Mentioned)) {
                    this.notify(msg);
                }
                this.updateTray();
            }
            if (msg.messageContent instanceof ModifyGroupSettingNotification) {
                wfc.getGroupInfo(msg.messageContent.groupId, true);
            }
        });

        wfc.eventEmitter.on(EventType.RecallMessage, (operator, messageUid) => {
            this._reloadConversationByMessageUidIfExist(messageUid);
            if (conversationState.currentConversationInfo) {
                let msg = wfc.getMessageByUid(messageUid);
                if (msg) {
                    if (msg.conversation.equal(conversationState.currentConversationInfo.conversation)) {
                        if (conversationState.currentConversationMessageList) {
                            let lastTimestamp = 0;
                            conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.map(msg => {
                                if (eq(msg.messageUid, messageUid)) {
                                    let newMsg = wfc.getMessageByUid(messageUid);
                                    this._patchMessage(newMsg, lastTimestamp);
                                    return newMsg;
                                }
                                lastTimestamp = msg.timestamp;
                                return msg;
                            });
                        }
                    }
                } else {
                    conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.filter(m => !eq(m.messageUid, messageUid));
                }
            }
            this.updateTray();
        });
        wfc.eventEmitter.on(EventType.UserOnlineEvent, (userOnlineStatus) => {
            userOnlineStatus.forEach(e => {
                miscState.userOnlineStateMap.set(e.userId, e);
            })
            // 更新在线状态
            contactState.friendList = this._patchAndSortUserInfos(contactState.friendList, '');
        })
        // 服务端删除
        wfc.eventEmitter.on(EventType.MessageDeleted, (messageUid) => {
            this._reloadConversationByMessageUidIfExist(messageUid);
            if (conversationState.currentConversationInfo) {

                if (conversationState.currentConversationMessageList) {
                    conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.filter(msg => !eq(msg.messageUid, messageUid))
                }
            }
            this.updateTray();
        });
        // 本地删除
        wfc.eventEmitter.on(EventType.DeleteMessage, (messageId) => {
            this._reloadConversationByMessageIdIfExist(messageId);
            if (conversationState.currentConversationInfo) {
                if (conversationState.currentConversationMessageList) {
                    conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.filter(msg => msg.messageId !== messageId)
                }
            }
        });

        wfc.eventEmitter.on(EventType.SecretChatMessageBurned, (target, playedMessageId) => {
            // todo 倒计时等
        });

        wfc.eventEmitter.on(EventType.SecretChatMessageBurned, (messageIds) => {
            this._loadDefaultConversationList();
            if (conversationState.currentConversationInfo) {
                if (conversationState.currentConversationMessageList) {
                    conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.filter(msg => messageIds.indexOf(msg.messageId) < 0)
                }
            }
        });

        wfc.eventEmitter.on(EventType.SecretChatStateChange, (targetId) => {
            this._loadDefaultConversationList();
        });

        wfc.eventEmitter.on(EventType.SendMessage, (message) => {
            // 删除频道，或者从频道会话切到其他会话时，会发送一条离开频道的消息
            if (message.messageContent instanceof LeaveChannelChatMessageContent) {
                return;
            }

            this._reloadConversation(message.conversation);
            if (!this._isDisplayMessage(message)) {
                return;
            }
            if (!conversationState.currentConversationInfo || !message.conversation.equal(conversationState.currentConversationInfo.conversation)) {
                console.log('not current conv')
                return;
            }
            let index = conversationState.currentConversationMessageList.findIndex(m => m.messageId === message.messageId);
            if (index !== -1) {
                return;
            }
            let length = conversationState.currentConversationMessageList.length;
            let lastTimestamp = 0;
            if (length > 0) {
                let lastMessage = conversationState.currentConversationMessageList[length - 1];
                lastTimestamp = lastMessage.timestamp;
            }
            this._patchMessage(message, lastTimestamp)

            conversationState.currentConversationMessageList.push(message);
            const defaultRenderMessageCount = 50;
            if (conversationState.currentConversationMessageList.length > defaultRenderMessageCount) {
                conversationState.currentConversationMessageList = conversationState.currentConversationMessageList.slice(conversationState.currentConversationMessageList.length - defaultRenderMessageCount);
            }
        });

        const messageStatusOrContentUpdateListener = (message) => {
            console.log('message status update', message)
            if (!this._isDisplayMessage(message)) {
                return;
            }

            if (!conversationState.currentConversationInfo || !message.conversation.equal(conversationState.currentConversationInfo.conversation)) {
                console.log('not current conv')
                return;
            }

            let index = conversationState.currentConversationMessageList.findIndex(m => m.messageId === message.messageId);
            if (index < 0) {
                return;
            }
            let msg = conversationState.currentConversationMessageList[index];
            msg = Object.assign(msg, message)
            conversationState.currentConversationMessageList.splice(index, 1, msg)

            if (conversationState.currentConversationInfo.lastMessage && conversationState.currentConversationInfo.lastMessage.messageId === message.messageId) {
                Object.assign(conversationState.currentConversationInfo.lastMessage, message);
            }
        }

        wfc.eventEmitter.on(EventType.MessageStatusUpdate, messageStatusOrContentUpdateListener);

        wfc.eventEmitter.on(EventType.MessageContentUpdate, messageStatusOrContentUpdateListener);

        wfc.eventEmitter.on(EventType.MessageRead, (readEntries) => {
            // optimization
            if (conversationState.currentConversationInfo) {
                // wfc.getConversationRead 每次返回同一个对象，只是该对象的值不一样。
                // 由于 VUE 不能检测到初始化时，不存在的属性的变更，故此处重新 new 一个新的对象，并赋值。
                // FYI:https://vuejs.org/v2/guide/reactivity.html
                conversationState.currentConversationRead = new Map(wfc.getConversationRead(conversationState.currentConversationInfo.conversation));
            }
        });

        avenginekitproxy.onVoipCallStatusCallback = this.updateVoipStatus
        if (isElectron()) {
            if (isMainWindow) {
                ipcRenderer.on('deep-link', (event, args) => {
                    console.log('deep-link', args)
                    if (!wfc.isLogin()) {
                        return;
                    }
                    // 下面是示例
                    // 可以根据 pathname 和 query parameter 进行相应的逻辑处理，这儿是跳转到对应的会话
                    let url = new URL(args);
                    let pathname = url.pathname;
                    let searchParams = url.searchParams;
                    if ('//conversation' === pathname || '//conversation/' === pathname) {
                        let target = searchParams.get('target');
                        let line = Number(searchParams.get('line'));
                        let type = Number(searchParams.get('type'))
                        let conversation = new Conversation(type, target, line)
                        this.setCurrentConversation(conversation);
                    }
                })

                ipcRenderer.on('floating-conversation-window-closed', (event, args) => {
                    let type = args.type;
                    let target = args.target;
                    let line = args.line;

                    let conv = new Conversation(type, target, line);
                    this.removeFloatingConversation(conv)
                    this._reloadConversation(conv);
                });

            }
            ipcRenderer.on('file-downloaded', (event, args) => {
                let messageUid = args.messageUid;
                let localPath = args.filePath;
                console.log('file-downloaded', args)

                conversationState.downloadingMessages = conversationState.downloadingMessages.filter(v => !eq(v.messageUid, messageUid));
                let msg = wfc.getMessageByUid(messageUid);
                console.log('xxxxx downloaded file', msg)
                if (msg) {
                    msg.messageContent.localPath = localPath;
                    wfc.updateMessageContent(msg.messageId, msg.messageContent);

                    conversationState.currentConversationMessageList.forEach(m => {
                        if (m.messageUid === messageUid) {
                            m.messageContent = msg.messageContent;
                        }
                    });
                }
            });

            ipcRenderer.on('file-download-failed', (event, args) => {
                let messageUid = args.messageUid;
                conversationState.downloadingMessages = conversationState.downloadingMessages.filter(v => !eq(v.messageUid, messageUid));
                // TODO 其他下载失败处理
            });

            ipcRenderer.on('file-download-progress', (event, args) => {
                let messageUid = args.messageUid;
                let receivedBytes = args.receivedBytes;
                let totalBytes = args.totalBytes;
                let dm = conversationState.downloadingMessages.find(dm => eq(dm.messageUid, messageUid));
                if (dm) {
                    dm.progress = receivedBytes;
                    dm.total = totalBytes;
                }
                // console.log('file download progress', messageId, receivedBytes, totalBytes);
            });

            miscState.subWindowLoadDataOptions = subWindowLoadDataOptions ? subWindowLoadDataOptions : {};

            if (!isMainWindow && wfc.getConnectionStatus() === ConnectionStatus.ConnectionStatusConnected) {
                // 根据 subWindowLoadDataOptions 配置去加载
                this._loadDefaultData();
            }
            window.__wfc = wfc;
        }
        miscState.isMainWindow = isMainWindow;
        window.__cw = currentWindow;
    },

    _loadDefaultData() {
        let loadOptions = miscState.subWindowLoadDataOptions;
        let isMainWindow = miscState.isMainWindow;
        (isMainWindow || loadOptions.loadFavContactList) && this._loadFavGroupList();
        (isMainWindow || loadOptions.loadChannelList) && this._loadChannelList();
        (isMainWindow || loadOptions.loadFriendList) && this._loadFriendList();
        (isMainWindow || loadOptions.loadFavContactList) && this._loadFavContactList();
        (isMainWindow || loadOptions.loadFriendRequestList) && this._loadFriendRequest();
        (isMainWindow || loadOptions.loadDefaultConversationList) && this._loadDefaultConversationList();
        this._loadSelfUserInfo();
        this._loadUserLocalSettings();
        conversationState.isMessageReceiptEnable = wfc.isReceiptEnabled() && wfc.isUserReceiptEnabled();
        conversationState.isGroupMessageReceiptEnable = wfc.isGroupReceiptEnabled() && wfc.isUserReceiptEnabled();
        // if (conversationState.currentConversationInfo) {
        //     this._loadCurrentConversationMessages();
        // }
    },

    // conversation actions

    _isDisplayMessage(message) {
        // return [PersistFlag.Persist, PersistFlag.Persist_And_Count].indexOf(MessageConfig.getMessageContentPersitFlag(message.messageContent.type)) > -1;
        return message.messageId !== 0 || message.messageContent.type === MessageContentType.Streaming_Text_Generating;
    },

    _loadDefaultConversationList() {
        console.log('store _loadDefaultConversationList');
        let conversationTypes = isElectron() ? [0, 1, 3, 5] : [0, 1, 3];
        this._loadConversationList(conversationTypes, [0])
    },

    _loadConversationList(conversationType = [0, 1, 3], lines = [0]) {
        let conversationList = wfc.getConversationList(conversationType, lines);
        let toLoadUserIdSet = new Set();
        let toLoadGroupIds = [];
        conversationList.forEach(info => {
            if (info.conversation.type === ConversationType.Single) {
                toLoadUserIdSet.add(info.conversation.target)
                if (info.lastMessage && info.lastMessage.from) {
                    toLoadUserIdSet.add(info.lastMessage.from);
                }
            } else if (info.conversation.type === ConversationType.Group) {
                toLoadGroupIds.push(info.conversation.target)
                if (info.lastMessage && info.lastMessage.from) {
                    toLoadUserIdSet.add(info.lastMessage.from);
                }
            }
        })
        let userInfoMap = new Map();
        let groupInfoMap = new Map();
        toLoadUserIdSet.forEach(uid => {
            userInfoMap.set(uid, new NullUserInfo(uid));
        })
        toLoadGroupIds.forEach(gid => {
            groupInfoMap.set(gid, new NullGroupInfo(gid))
        })

        console.log('to load userIds', [...toLoadUserIdSet]);
        wfc.getUserInfos([...toLoadUserIdSet])
            .forEach(u => {
                userInfoMap.set(u.uid, u);
            });
        console.log('to load groupIds', toLoadGroupIds);
        wfc.getGroupInfos(toLoadGroupIds)
            .forEach(g => {
                groupInfoMap.set(g.target, g);
            });

        conversationList.forEach(info => {
            this._patchConversationInfo(info, true, userInfoMap, groupInfoMap);
            // side affect
            if (conversationState.currentConversationInfo
                && conversationState.currentConversationInfo.conversation.equal(info.conversation)) {
                conversationState.currentConversationInfo = info;
                this._patchCurrentConversationOnlineStatus();
            }
        });
        conversationState.conversationInfoList = conversationList;
    },

    _reloadConversation(conversation, insertIfNoExist = true) {
        let conversationInfo = wfc.getConversationInfo(conversation);
        if (conversationInfo) {
            conversationInfo = this._patchConversationInfo(conversationInfo, true);
            conversationInfo.conversation._targetOnlineStateDesc = this.getUserOnlineState(conversation.target);
        } else {
            return
        }
        let index = conversationState.conversationInfoList.findIndex(info => info.conversation.equal(conversation));
        if (index >= 0) {
            Object.assign(conversationState.conversationInfoList[index], conversationInfo);
        } else {
            if (insertIfNoExist && gt(conversationInfo.timestamp, 0) && conversation.type !== ConversationType.ChatRoom) {
                conversationState.conversationInfoList.push(conversationInfo);
            } else {
                return conversationInfo;
            }
        }

        if (conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.equal(conversation)) {
            let isClearConversationMessageHistory = !conversationInfo.lastMessage && !!conversationState.currentConversationInfo.lastMessage;
            // 清除聊天记录
            if (isClearConversationMessageHistory) {
                conversationState.currentConversationMessageList = [];
            }
            conversationState.currentConversationInfo = conversationInfo;
        }

        // sort
        conversationState.conversationInfoList.sort((a, b) => {
            if ((a.top && b.top) || (!a.top && !b.top)) {
                return gt(a.timestamp, b.timestamp) ? -1 : 1;
            } else {
                if (a.top) {
                    return -1;
                } else {
                    return 1;
                }
            }
        })
        return conversationInfo;
    },

    _reloadSingleConversationIfExist(userInfos) {
        let cl = conversationState.conversationInfoList
        if (!cl || cl.length === 0) {
            return;
        }
        if (userInfos.length > 10) {
            this._loadDefaultConversationList();
        } else {
            let toReloadConversations = [];
            if (cl) {
                let uids = userInfos.map(info => info.uid);
                cl.forEach(ci => {
                    let conv = ci.conversation;
                    if (conv.type === ConversationType.Single) {
                        if (uids.indexOf(conv.target) >= 0) {
                            toReloadConversations.push(conv);
                        }
                    } else {
                        let lastMsg = ci.lastMessage;
                        if (lastMsg && uids.indexOf(lastMsg.from) >= 0
                            && toReloadConversations.findIndex(c => c.type === conv.type && c.target === conv.target && c.line === conv.line) === -1) {
                            toReloadConversations.push(conv)
                        }
                    }
                })
            }
            for (let conv of toReloadConversations) {
                this._reloadConversation(conv, false);
            }
        }
    },

    _reloadGroupConversationIfExist(groupInfos) {
        if (groupInfos.length > 10) {
            this._loadDefaultConversationList();
        } else {
            groupInfos.forEach(gi => {
                let conv = new Conversation(ConversationType.Group, gi.target, 0);
                this._reloadConversation(conv, false);
            })
        }
    },

    _reloadConversationByMessageIdIfExist(messageId) {
        if (messageId === 0) {
            return;
        }
        let toLoadConversationInfo = null;
        for (let i = 0; i < conversationState.conversationInfoList.length; i++) {
            let info = conversationState.conversationInfoList[i];
            if (info.lastMessage && info.lastMessage.messageId === messageId) {
                toLoadConversationInfo = info;
                break;
            }
        }

        if (toLoadConversationInfo) {
            this._reloadConversation(toLoadConversationInfo.conversation)
        }
    },
    _reloadConversationByMessageUidIfExist(messageUid) {
        let toLoadConversationInfo = null;
        for (let i = 0; i < conversationState.conversationInfoList.length; i++) {
            let info = conversationState.conversationInfoList[i];
            if (info.lastMessage && eq(info.lastMessage.messageUid, messageUid)) {
                toLoadConversationInfo = info;
                break;
            }
        }

        if (toLoadConversationInfo) {
            this._reloadConversation(toLoadConversationInfo.conversation)
        }
    },

    setCurrentConversation(conversation) {
        if (!conversation) {
            this.setCurrentConversationInfo(null)
            return;
        }
        if (conversationState.currentConversationInfo && conversation.equal(conversationState.currentConversationInfo.conversation)) {
            return;
        }
        let convs = conversationState.conversationInfoList.filter(info => info.conversation.equal(conversation));
        let info;
        if (convs && convs.length > 0) {
            info = convs[0];
        } else {
            wfc.setConversationTimestamp(conversation, new Date().getTime());
            info = this._reloadConversation(conversation);
        }
        this.setCurrentConversationInfo(info);
    },

    setCurrentConversationInfo(conversationInfo) {
        if (!conversationInfo) {
            if (conversationState.currentConversationInfo) {
                let conversation = conversationState.currentConversationInfo.conversation;
                if (wfc.isUserOnlineStateEnabled() && ((conversation.type === ConversationType.Single || conversation.type === ConversationType.SecretChat) && !wfc.isMyFriend(conversation.target))) {
                    wfc.unwatchOnlineState(conversation.type, [conversation.target]);
                }
                if (conversation.type === ConversationType.Channel) {
                    let content = new LeaveChannelChatMessageContent();
                    wfc.sendConversationMessage(conversation, content);
                }
            }
            conversationState.currentConversationInfo = null;
            conversationState.shouldAutoScrollToBottom = false;
            conversationState.currentConversationMessageList.length = 0;
            conversationState.currentConversationOldestMessageId = 0;
            conversationState.currentConversationOldestMessageUid = 0;
            conversationState.currentConversationRead = null;
            conversationState.enableMessageMultiSelection = false;
            conversationState.showChannelMenu = false;
            return;
        }

        if (conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.equal(conversationInfo.conversation)) {
            return;
        }
        let conversation = conversationInfo.conversation;
        if (wfc.isUserOnlineStateEnabled() && ((conversation.type === ConversationType.Single || conversation.type === ConversationType.SecretChat) && !wfc.isMyFriend(conversation.target))) {
            wfc.watchOnlineState(conversation.type, [conversation.target], 1000, (states) => {
                states.forEach((e => {
                    miscState.userOnlineStateMap.set(e.userId, e);
                }))
                this._patchCurrentConversationOnlineStatus();

            }, (err) => {
                console.log('watchOnlineState error', err);
            })
        }
        if (conversation.type === ConversationType.Channel) {
            let content = new EnterChannelChatMessageContent();
            wfc.sendConversationMessage(conversation, content);
        }
        conversationState.currentConversationInfo = conversationInfo;
        conversationState.shouldAutoScrollToBottom = true;
        conversationState.currentConversationMessageList.length = 0;
        conversationState.currentConversationOldestMessageId = 0;
        conversationState.currentConversationOldestMessageUid = 0;
        // 会话页面会触发调用 loadConversationHistoryMessages，这儿不用提前加载消息
        // this._loadCurrentConversationMessages();
        this._patchCurrentConversationOnlineStatus();

        conversationState.currentConversationRead = wfc.getConversationRead(conversationInfo.conversation);

        conversationState.enableMessageMultiSelection = false;
        conversationState.showChannelMenu = false;
        if (conversation.type === ConversationType.Channel) {
            let channelInfo = wfc.getChannelInfo(conversation.target, true);
            if (channelInfo.menus && channelInfo.menus.length > 0) {
                conversationState.showChannelMenu = true;
            }
        } else if (conversation.type === ConversationType.Group) {
            wfc.getGroupInfo(conversation.target, true);
        } else if (conversation.type === ConversationType.Single) {
            wfc.getUserInfo(conversation.target, true);
        }
        conversationState.quotedMessage = null;
        conversationState.currentVoiceMessage = null;

        clearTimeout(conversationState.inputClearHandler);
        conversationState.inputtingUser = null;

        pickState.messages.length = 0;
    },

    quitGroup(groupId) {
        wfc.quitGroup(groupId, [0], null, () => {
            this.setCurrentConversationInfo(null)
        }, (err) => {
            console.log('quit group error', err)
        })
    },
    dismissGroup(groupId) {
        wfc.dismissGroup(groupId, [0], null, () => {
            this.setCurrentConversationInfo(null)
        }, (err) => {
            console.log('dismiss group error', err)
        })
    },
    subscribeChannel(channelId, subscribe) {
        wfc.listenChannel(channelId, subscribe, () => {
            //this.setCurrentConversationInfo(null)
        }, (err) => {
            console.log('unsubscribe channel error', err)
        })
    },

    toggleMessageMultiSelection(message) {
        conversationState.enableMessageMultiSelection = !conversationState.enableMessageMultiSelection;
        pickState.messages.length = 0;
        if (conversationState.enableMessageMultiSelection && message) {
            pickState.messages.push(message);
        }
    },

    toggleChannelMenu(toggle) {
        conversationState.showChannelMenu = toggle;
    },

    selectOrDeselectMessage(message) {
        let index = pickState.messages.findIndex(m => m.messageId === message.messageId);
        if (index >= 0) {
            pickState.messages.splice(index, 1);
        } else {
            pickState.messages.push(message);
        }
    },

    deleteSelectedMessages(deleteRemoteMessages = false) {
        conversationState.enableMessageMultiSelection = false;
        if (pickState.messages.length < 1) {
            return;
        }
        pickState.messages.sort((m1, m2) => m1.messageId - m2.messageId);
        pickState.messages.forEach(m => {
            if (deleteRemoteMessages) {
                wfc.deleteRemoteMessageByUid(m.messageUid);
            } else {
                wfc.deleteMessage(m.messageId);
            }
        });
        pickState.messages.length = 0;
    },

    forwardMessage(forwardType, targetConversations, messages, extraMessageText) {
        // web 端，避免撤回消息等操作，影响组合消息
        if (!isElectron()) {
            messages = messages.map(m => Object.assign({}, m));
        }
        targetConversations.forEach(conversation => {
            // let msg =new Message(conversation, message.messageContent)
            // wfc.sendMessage(msg)
            // 或者下面这种
            if (forwardType === ForwardType.NORMAL || forwardType === ForwardType.ONE_BY_ONE) {
                messages.forEach(message => {
                    if (message.messageContent instanceof ArticlesMessageContent) {
                        let linkContents = message.messageContent.toLinkMessageContent();
                        linkContents.forEach(lm => {
                            wfc.sendConversationMessage(conversation, lm);
                        })

                    } else {
                        message.messageContent = this._filterForwardMessageContent(message)
                        wfc.sendConversationMessage(conversation, message.messageContent);
                    }
                });
            } else {
                // 合并转发
                let compositeMessageContent = new CompositeMessageContent();
                let title = '';
                let msgConversation = messages[0].conversation;
                if (msgConversation.type === ConversationType.Single) {
                    let users = store.getUserInfos([wfc.getUserId(), msgConversation.target], '');
                    title = users[0]._displayName + '和' + users[1]._displayName + '的聊天记录';
                } else {
                    title = '群的聊天记录';
                }
                compositeMessageContent.title = title;
                let msgs = messages.map(m => {
                    m.messageContent = this._filterForwardMessageContent(m)
                    return m
                })
                compositeMessageContent.setMessages(msgs);

                wfc.sendConversationMessage(conversation, compositeMessageContent);
            }

            if (extraMessageText) {
                let textMessage = new TextMessageContent(extraMessageText)
                wfc.sendConversationMessage(conversation, textMessage);
            }
        });
    },

    forwardByCreateConversation(forwardType, users, messages, extraMessageText) {
        this.createConversation(users,
            (conversation) => {
                this.forwardMessage(forwardType, [conversation], messages, extraMessageText)
            },
            (err) => {
                console.error('createConversation error', err)
            })
    },

    setShouldAutoScrollToBottom(scroll) {
        conversationState.shouldAutoScrollToBottom = scroll;
    },

    /**
     *
     * @param src {String} 媒体url
     * @param thumbUrl {String} 缩略图url
     * @param thumb {String} base64格式的缩略图，但不包含'data:image/png;base64,'
     * @param autoplay
     */
    previewMedia(src, thumbUrl, thumb, autoplay = true) {
        conversationState.previewMediaItems.length = 0;
        conversationState.previewMediaItems.push({
            src: src,
            thumb: thumbUrl ? thumbUrl : 'data:image/png;base64,' + thumb,
            autoplay: autoplay,
        });
        conversationState.previewMediaIndex = 0;
        console.log('preview media', conversationState.previewMediaItems, conversationState.previewMediaIndex)
    },
    previewMedias(mediaItems, index) {
        conversationState.previewMediaItems.length = 0;
        conversationState.previewMediaItems.push(...mediaItems);
        conversationState.previewMediaIndex = index;
        console.log('preview medias', conversationState.previewMediaItems, conversationState.previewMediaIndex)
    },

    playVoice(message) {
        if (conversationState.currentVoiceMessage) {
            conversationState.currentVoiceMessage._isPlaying = false;
        }
        conversationState.currentVoiceMessage = message;
    },

    /**
     *
     * @param message
     * @param {Boolean} continuous  true，预览周围的媒体消息；false，只预览第一个参数传入的那条媒体消息
     */
    previewMessage(message, continuous) {
        conversationState.previewMediaItems.length = 0;
        conversationState.previewMediaIndex = 0;
        if (continuous && conversationState.currentConversationMessageList.length > 0) {
            let mediaMsgs = conversationState.currentConversationMessageList.filter(m => [MessageContentType.Image, MessageContentType.Video].indexOf(m.messageContent.type) > -1)
            let msg;
            for (let i = 0; i < mediaMsgs.length; i++) {
                msg = mediaMsgs[i];
                if (msg.messageId === message.messageId) {
                    conversationState.previewMediaIndex = i;
                }
                let mediaUrl = msg.messageContent.remotePath;
                if (!mediaUrl) {
                    if (msg.messageContent.file) {
                        mediaUrl = URL.createObjectURL(msg.messageContent.file)
                    }
                }
                conversationState.previewMediaItems.push({
                    src: mediaUrl,
                    thumb: 'data:image/png;base64,' + msg.messageContent.thumbnail,
                    autoplay: true,
                });
            }
        } else {
            conversationState.previewMediaIndex = 0;
            let mediaUrl = message.messageContent.remotePath;
            if (!mediaUrl) {
                if (message.messageContent.file) {
                    mediaUrl = URL.createObjectURL(message.messageContent.file)
                }
            }
            conversationState.previewMediaItems.push({
                src: mediaUrl,
                thumb: 'data:image/png;base64,' + message.messageContent.thumbnail,
                autoplay: true,
            });
        }
    },

    previewCompositeMessage(compositeMessage, focusMessageUid) {
        conversationState.previewMediaItems.length = 0;
        conversationState.previewMediaIndex = 0;

        let mediaMsgs = compositeMessage.messageContent.messages.filter(m => [MessageContentType.Image, MessageContentType.Video].indexOf(m.messageContent.type) > -1)
        let msg;
        for (let i = 0; i < mediaMsgs.length; i++) {
            msg = mediaMsgs[i];
            if (eq(msg.messageUid, focusMessageUid)) {
                conversationState.previewMediaIndex = i;
            }
            conversationState.previewMediaItems.push({
                src: msg.messageContent.remotePath,
                thumb: 'data:image/png;base64,' + msg.messageContent.thumbnail,
                autoplay: true,
            });
        }
    },

    /**
     *
     * @param conversation :Conversation 会话
     * @param files : File[] 需要发送的媒体文件
     * @param text : string 描述
     * @return {Promise<void>}
     */
    async sendMixMediaMessage(conversation, files, text) {
        console.log('sendMixMediaMessage', conversation, files, text);
        let isAllImageOrVideoFile = true
        for (let i = 0; i < files.length; i++) {
            if (files[i].type.indexOf('image') === -1 && files[i].type.indexOf('video') === -1) {
                isAllImageOrVideoFile = false
                break
            }
        }

        let content;
        let entries
        if (isAllImageOrVideoFile) {
            entries = await Promise.all(
                files.map(async f => {
                    let isImg = f.type.indexOf('image') >= 0
                    let {thumbnail: it, width: iw, height: ih} = isImg ? await imageThumbnail(f) : await videoThumbnail(f);
                    it = it ? it : Config.DEFAULT_THUMBNAIL_URL;
                    if (it.length > 6 * 1024) {
                        console.warn('generated thumbnail is too large, use default thumbnail', it.length);
                        it = Config.DEFAULT_THUMBNAIL_URL;
                    }
                    return {
                        url: '',
                        type: isImg ? 'image' : 'video',
                        thumbnail: it.split(',')[1],
                        width: iw,
                        height: ih,
                        tmpFile: f,
                    }
                })
            )

            content = new MixMultiMediaTextMessageContent(entries, text)
        } else {
            entries = files.map(f => {
                return {
                    url: '',
                    name: f.name,
                    size: f.size,
                    iv: f.type.indexOf('image') >= 0 || f.type.indexOf('video') >= 0,
                    tmpFile: f,
                }
            })
            content = new MixFileTextMessageContent(entries, text)
        }
        let msg = wfc.insertMessage(conversation, content, MessageStatus.Sending, true)

        let entriesWithUrl = await Promise.all(
            entries.map(entry => {
                let file = entry.tmpFile;
                return new Promise((resolve, reject) => {
                    wfc.uploadMedia(file.name, file, file.type.indexOf('image') >= 0 ? MessageContentMediaType.Image : MessageContentMediaType.Video, remoteUrl => {
                        entry.url = remoteUrl
                        delete entry.tmpFile
                        resolve(entry)
                    }, err => {
                        resolve(entry)
                        console.error('upload file failed', f, err);
                    }, (process, total) => {

                    })
                })
            })
        )
        if (isAllImageOrVideoFile) {
            content = new MixMultiMediaTextMessageContent(entriesWithUrl, text)
        } else {
            content = new MixFileTextMessageContent(entries, text)
        }
        wfc.updateMessageContent(msg.messageId, content)
        msg.messageContent = content
        wfc.sendSavedMessage(msg, 0, (messageUid, timestamp) => {

        }, err => {
            console.log('send msg failed', err)
        })
    },

    /**
     *
     * @param conversation
     * @param {File | string} file html File 类型或者url，绝对路径只在electron里面生效
     * @return {Promise<boolean>}
     */
    async sendFile(conversation, file) {
        console.log('send file', file)
        if (file.size && file.size > 100 * 1024 * 1024) {
            if (!wfc.isSupportBigFilesUpload() || conversation.type === ConversationType.SecretChat) {
                console.log('file too big, and not support upload big file')
                return true;
            }
        }

        let fileOrLocalPath = null;
        let remotePath = null;
        if (typeof file === 'string') {
            if (!file.startsWith('http')) {
                fileOrLocalPath = file;
            } else {
                remotePath = file;
            }

            file = {
                path: file,
                name: file.substring((file.lastIndexOf('/') + 1))
            }
        } else {
            fileOrLocalPath = file;
        }
        let msg = new Message();
        msg.conversation = conversation;

        let mediaType = helper.getMediaType(file.name.split('.').slice(-1).pop());
        // todo other file type
        let messageContentmediaType = {
            'pic': MessageContentMediaType.Image,
            'video': MessageContentMediaType.Video,
            'doc': MessageContentMediaType.File,
        }[mediaType];

        let messageContent;
        switch (messageContentmediaType) {
            case MessageContentMediaType.Image:
                let {thumbnail: it, width: iw, height: ih} = await imageThumbnail(file);
                it = it ? it : Config.DEFAULT_THUMBNAIL_URL;
                console.log('image file',it.length, file )
                if (it.length > 10 * 1024) {
                    console.warn('generated thumbnail is too large, use default thumbnail', it.length);
                    it = Config.DEFAULT_THUMBNAIL_URL;
                }
                messageContent = new ImageMessageContent(fileOrLocalPath, remotePath, it.split(',')[1]);
                messageContent.imageWidth = iw;
                messageContent.imageHeight = ih;
                break;
            case MessageContentMediaType.Video:
                let vtr = await videoThumbnail(file);
                if (vtr) {
                    let {thumbnail: vt, width: vw, height: vh} = vtr;
                    let duration = await videoDuration(file)
                    duration = Math.ceil(duration * 1000);
                    if (vt.length > 10 * 1024) {
                        console.warn('generated thumbnail is too large, use default thumbnail', vt.length);
                        vt = Config.DEFAULT_THUMBNAIL_URL;
                    }
                    messageContent = new VideoMessageContent(fileOrLocalPath, remotePath, vt.split(',')[1]);
                    // TODO width and height
                    break;
                } else {
                    // fallback to file message
                }
            case MessageContentMediaType.File:
            // do nothing
            default:
                messageContent = new FileMessageContent(fileOrLocalPath, remotePath);
                break;
        }
        msg.messageContent = messageContent;
        wfc.sendMessage(msg,
            (messageId) => {
                msg.messageId = messageId;
                console.log('sf, pr', messageId)
            },
            (progress, total) => {
                // console.log('sf p', msg.messageId, Math.ceil(progress / total * 100))
                let sm = conversationState.sendingMessages.find(e => e.messageId === msg.messageId);
                if (sm) {
                    sm.progress = progress;
                    sm.total = total;
                } else {
                    conversationState.sendingMessages.push({messageId: msg.messageId, progress, total});
                }
            },
            (messageUid) => {
                console.log('sf s', messageUid)
                conversationState.sendingMessages = conversationState.sendingMessages.filter(e => e.messageId !== msg.messageId);
            },
            (error) => {
                console.log('sf e', error)
                conversationState.sendingMessages = conversationState.sendingMessages.filter(e => e.messageId !== msg.messageId);
            }
        );
    },

    quoteMessage(message) {
        conversationState.quotedMessage = message;
        conversationState.currentConversationInfo._quotedMessage = message;
    },

    getConversationInfo(conversation) {
        let info = wfc.getConversationInfo(conversation);
        return this._patchConversationInfo(info, false);
    },


    /**
     * 获取会话消息
     * @param {Conversation} conversation 会话
     * @param {number} fromIndex 其实消息的 messageId
     * @param {boolean} before 获取其实消息之前，还是之后的消息
     * @param {string} withUser 过滤该用户发送或接收的消息
     * @param {function (Message[]) } callback 消息列表会回调
     */
    getMessages(conversation, fromIndex = 0, before = true, withUser = '', callback) {
        wfc.getMessagesV2(conversation, fromIndex, before, 20, withUser, msgs => {
            msgs = msgs.map(m => this._patchMessage(m, 0));
            //callback && callback(msgs);
            setTimeout(() => callback && callback(msgs), 200)
        }, err => {
            console.error('getMessageV2 error', err)
            callback && callback([]);
        });
    },

    getMessageInTypes(conversation, contentTypes, timestamp, before = true, withUser = '', callback) {
        wfc.getMessagesByTimestampV2(conversation, contentTypes, timestamp, before, 20, withUser, msgs => {
            msgs = msgs.map(m => this._patchMessage(m, 0));
            callback && callback(msgs);
        }, err => {
            callback && callback([]);
        });
    },

    _loadCurrentConversationMessages() {
        console.log('_loadCurrentConversationMessages')
        if (!conversationState.currentConversationInfo) {
            return;
        }
        // TODO 可以在这儿加载所有未读消息，以实现滚动到一条未读消息的地方
        let conversation = conversationState.currentConversationInfo.conversation;
        wfc.getMessagesV2(conversation, 0, true, 20, '', msgs => {
            conversationState.currentConversationMessageList = msgs;
            this._patchCurrentConversationMessages();
            if (msgs.length) {
                conversationState.currentConversationOldestMessageId = msgs[0].messageId;
            }
            for (let i = 0; i < msgs.length; i++) {
                if (gt(msgs[i].messageUid, 0)) {
                    conversationState.currentConversationOldestMessageUid = msgs[0].messageUid;
                    break;
                }
            }
        }, err => {
            console.error('_loadCurrentConversationMessages error', err);
        });
    },

    _patchCurrentConversationMessages() {
        let lastTimestamp = 0;
        let msgs = conversationState.currentConversationMessageList;
        msgs.forEach(m => {
            this._patchMessage(m, lastTimestamp);
            lastTimestamp = m.timestamp;
        });
    },

    _onloadConversationMessages(conversation, messages) {
        if (!messages || messages.length === 0) {
            return false;
        }
        let loadNewMsg = false;
        let lastTimestamp = 0;
        let newMsgs = [];
        messages.forEach(m => {
            let index = conversationState.currentConversationMessageList.findIndex(cm => cm.messageId === m.messageId)
            if (index === -1) {
                this._patchMessage(m, lastTimestamp);
                lastTimestamp = m.timestamp;
                newMsgs.push(m);
                loadNewMsg = true;
            }
        });
        conversationState.currentConversationMessageList = newMsgs.concat(conversationState.currentConversationMessageList);
        return loadNewMsg;
    },

    loadConversationHistoryMessages(loadedCB, completeCB, enableLoadRemoteHistoryMessage = true) {
        if (!conversationState.currentConversationInfo) {
            return;
        }
        let conversation = conversationState.currentConversationInfo.conversation;
        console.log('loadConversationHistoryMessage', conversation, conversationState.currentConversationOldestMessageId, stringValue(conversationState.currentConversationOldestMessageUid));
        let loadRemoteHistoryMessageFunc = () => {
            wfc.loadRemoteConversationMessages(conversation, [], conversationState.currentConversationOldestMessageUid, 20,
                (msgs) => {
                    console.log('loadRemoteConversationMessages response', msgs.length);
                    if (msgs.length === 0) {
                        completeCB();
                    } else {
                        // 可能拉回来的时候，本地已经切换会话了
                        if (conversation.equal(conversationState.currentConversationInfo.conversation)) {
                            let filteredMsgs = msgs.filter(m => {
                                return m.messageId !== 0 && conversationState.currentConversationMessageList.findIndex(cm => eq(cm.messageUid, m.messageUid)) === -1
                            })
                            if (filteredMsgs.length === 0) {
                                completeCB()
                                return;
                            }

                            conversationState.currentConversationOldestMessageUid = filteredMsgs[0].messageUid;
                            this._onloadConversationMessages(conversation, filteredMsgs);
                            loadedCB();
                        }
                        if (!conversationState.currentConversationInfo.lastMessage) {
                            this._reloadConversation(conversation);
                        }
                    }
                },
                (error) => {
                    completeCB();
                });
        }

        wfc.getMessagesV2(conversation, conversationState.currentConversationOldestMessageId, true, 20, '', lmsgs => {
            if (lmsgs.length > 0) {
                if (!conversation.equal(conversationState.currentConversationInfo.conversation)) {
                    return;
                }
                conversationState.currentConversationOldestMessageId = lmsgs[0].messageId;
                if (gt(lmsgs[0].messageUid, 0)) {
                    conversationState.currentConversationOldestMessageUid = lmsgs[0].messageUid;
                }
                this._onloadConversationMessages(conversation, lmsgs)
                if (lmsgs.length === 0) {
                    if (enableLoadRemoteHistoryMessage) {
                        loadRemoteHistoryMessageFunc();
                    } else {
                        completeCB();
                    }
                } else {
                    // loadedCB();
                    setTimeout(() => loadedCB(), 200)
                }
            } else {
                if (enableLoadRemoteHistoryMessage) {
                    loadRemoteHistoryMessageFunc();
                } else {
                    completeCB();
                }
            }
        }, err => {
            completeCB();
        });
    },

    setConversationTop(conversation, top) {
        wfc.setConversationTop(conversation, top,
            () => {
                this._reloadConversation(conversation);
            },
            (err) => {
                console.log('setConversationTop error', err)
            });
    },

    setConversationSilent(conversation, silent) {
        wfc.setConversationSlient(conversation, silent,
            () => {
                this._reloadConversation(conversation);
            },
            (err) => {
                console.log('setConversationSilent error', err)
            });
    },

    removeConversation(conversation) {
        wfc.removeConversation(conversation, false);
        if (conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.equal(conversation)) {
            this.setCurrentConversationInfo(null);
        }
        conversationState.conversationInfoList = conversationState.conversationInfoList.filter(info => !info.conversation.equal(conversation));
        this.updateTray();
    },

    getMessageById(messageId) {
        let msg = wfc.getMessageById(messageId);
        if (msg) {
            this._patchMessage(msg, 0);
        }
        return msg;
    },

    getMessageByUid(messageUid) {
        let msg = wfc.getMessageByUid(messageUid);
        if (msg) {
            this._patchMessage(msg, 0);
        }
        return msg;
    },

    _patchMessage(m, lastTimestamp = 0, userInfoMap) {
        // TODO
        // _from
        // _showTime
        if (m.conversation.type === ConversationType.Single) {
            m._from = userInfoMap ? userInfoMap.get(m.from) : wfc.getUserInfo(m.from, false, '');
        }
        if (!m._from || !m._from.updateDt) {
            let u = wfc.getUserInfo(m.from, false, m.conversation.type === ConversationType.Group ? m.conversation.target : '');
            // clone for modify
            // TODO sdk 返回的时候，直接返回 clone copy，而不是直接返回底层的数据，防止上层修改，影响到底层数据模型
            m._from = Object.assign({}, u);
        }
        if (m.conversation.type === ConversationType.Group) {
            m._from._displayName = wfc.getGroupMemberDisplayNameEx(m._from);
        } else {
            m._from._displayName = wfc.getUserDisplayNameEx(m._from);
        }
        if (m.conversation.type === ConversationType.SecretChat) {
            if (m.messageContent instanceof MediaMessageContent && m.messageContent.remotePath && m.messageContent.remotePath.startsWith("http")) {
                m.messageContent.remotePath = `http://localhost:${Config.SECRET_CHAT_MEDIA_DECODE_SERVER_PORT}?target=${m.conversation.target}&url=${m.messageContent.remotePath}`
            }
        }

        if (lastTimestamp === 0 || (numberValue(lastTimestamp) > 0 && numberValue(m.timestamp) - numberValue(lastTimestamp) > 5 * 60 * 1000)) {
            m._showTime = true;
        }
        m._timeStr = helper.timeFormat(m.timestamp)

        if (m.messageContent instanceof CompositeMessageContent) {
            this._patchCompositeMessageContent(m.messageContent);
        }

        // TODO 如果Im server支持备选网络，需要根据当前的网络情况，判断当前是处于主网络，还是备选网络，并动态修改媒体类消息的remotePath，不然可能会出现不能正常加载的情况
        // 如何判断是主网络，还是备选网络，这儿提供一种思路：分别通过主网络和备选网络测试访问im server的/api/version接口
        // 判断是主网络，还是备选网络，一般启动的时候，检测到网络网络变化的时候，在判断一次。
        // if(m.messageContent instanceof MediaMessageContent){
        // TODO 动态修改remotePath
        // }

        return m;
    },

    _patchCompositeMessageContent(compositeMessageContent) {
        let messages = compositeMessageContent.messages;
        messages.forEach(m => {
            this._patchMessage(m, 0)
        })
    },

    _patchConversationInfo(info, patchLastMessage = true, userInfoMap, groupInfoMap) {
        if (info.conversation.type === ConversationType.Single) {
            info.conversation._target = userInfoMap ? userInfoMap.get(info.conversation.target) : wfc.getUserInfo(info.conversation.target, false);
            if (info.conversation._target) {
                info.conversation._target._displayName = wfc.getUserDisplayNameEx(info.conversation._target);
            }
        } else if (info.conversation.type === ConversationType.Group) {
            info.conversation._target = groupInfoMap ? groupInfoMap.get(info.conversation.target) : wfc.getGroupInfo(info.conversation.target, false);
            if (info.conversation._target) {
                info.conversation._target._isFav = wfc.isFavGroup(info.conversation.target);
                info.conversation._target._displayName = info.conversation._target.remark ? info.conversation._target.remark : info.conversation._target.name;
            }
        } else if (info.conversation.type === ConversationType.Channel) {
            info.conversation._target = wfc.getChannelInfo(info.conversation.target, false);
            info.conversation._target._displayName = info.conversation._target.name;
        } else if (info.conversation.type === ConversationType.SecretChat) {
            let secretChatInfo = wfc.getSecretChatInfo(info.conversation.target);
            if (secretChatInfo) {
                let userId = secretChatInfo.userId;
                let userInfo = wfc.getUserInfo(userId, false);
                info.conversation._target = userInfo;
                info.conversation._target._displayName = wfc.getUserDisplayNameEx(userInfo);
            } else {
                info.conversation._target = {};
            }
        } else if (info.conversation.type === ConversationType.ChatRoom) {
            wfc.getChatroomInfo(info.conversation.target, 0, (chatRoomInfo) => {
                info.conversation._target = chatRoomInfo;
            }, err => {
                console.log('get chatRoomInfo error', err);
                info.conversation._target = {};
            });
        }
        if (gt(info.timestamp, 0)) {
            info._timeStr = helper.dateFormat(info.timestamp);
        } else {
            info._timeStr = '';
        }


        if (patchLastMessage && info.lastMessage && info.lastMessage.conversation !== undefined && (!info.lastMessage._from || !info.lastMessage._from.updateDt)) {
            this._patchMessage(info.lastMessage, 0, userInfoMap)
        }

        if (info.unreadCount) {
            info._unread = info.unreadCount.unread + info.unreadCount.unreadMention + info.unreadCount.unreadMentionAll;
        }
        if (info.conversation.equal(avenginekitproxy.conversation)) {
            info._isVoipOngoing = true;
        } else {
            info._isVoipOngoing = false;
        }

        return info;
    },

    addDownloadingMessage(messageUid) {
        conversationState.downloadingMessages.push({
            messageUid: messageUid,
            progress: 0,
            total: Number.MAX_SAFE_INTEGER,
        });
        console.log('add downloading')
    },

    isDownloadingMessage(messageUid) {
        // web端尚未测试，先屏蔽
        if (!isElectron()) {
            return false;
        }
        return conversationState.downloadingMessages.findIndex(dm => eq(dm.messageUid, messageUid)) >= 0;
    },

    isSendingMessage(messageId) {
        return conversationState.sendingMessages.has(messageId);
    },

    getDownloadingMessageStatus(messageUid) {
        return conversationState.downloadingMessages.find(dm => eq(dm.messageUid, messageUid));
    },

    getSendingStatus(messageId) {
        return conversationState.sendingMessages.find(e => e.messageId === messageId);
    },

    addFloatingConversation(conversation) {
        conversationState.floatingConversations.push(conversation);
    },

    removeFloatingConversation(conversation) {
        conversationState.floatingConversations = conversationState.floatingConversations.filter(c => !c.equal(conversation))
    },

    isConversationInCurrentWindow(conversation) {
        if (miscState.isMainWindow) {
            let index = conversationState.floatingConversations.findIndex(fc => fc.equal(conversation));
            return index === -1;
        } else {
            return conversationState.currentConversationInfo && conversationState.currentConversationInfo.conversation.equal(conversation);
        }
    },

    // contact actions

    _loadSelfUserInfo() {
        contactState.selfUserInfo = wfc.getUserInfo(wfc.getUserId(), false);
    },

    _loadFriendList() {
        let friends = wfc.getMyFriendList(false);
        if(Config.FILE_HELPER_ID){
            let fileHelperIndex = friends.indexOf(Config.FILE_HELPER_ID);
            if (fileHelperIndex < 0 && Config.FILE_HELPER_ID) {
                friends.push(Config.FILE_HELPER_ID);
            }
        }
        if (friends && friends.length > 0) {
            let friendList = wfc.getUserInfos(friends, '');
            contactState.friendList = this._patchAndSortUserInfos(friendList, '');
        }
    },

    getUserOnlineState(userId) {
        let userOnlineState = miscState.userOnlineStateMap.get(userId);
        if (userOnlineState) {
            return userOnlineState.desc();
        }
        return '';
    },

    _patchCurrentConversationOnlineStatus() {
        let convInfo = conversationState.currentConversationInfo;
        if (convInfo && convInfo.conversation.type === ConversationType.Single) {
            // 在 将 object 和 ui 绑定之前， 向 object 中新增的属性是 reactive 的，但绑定之后，才新增的属性，不是 reactive 的，
            // 故需要通过下面这种方法，让其成为 reactive 的属性
            // conversationState.currentConversationInfo.conversation._targetOnlineStateDesc = userOnlineStatus.desc();
            // Vue.set(conversationState.currentConversationInfo.conversation, '_targetOnlineStateDesc', this.getUserOnlineState(convInfo.conversation.target))
            conversationState.currentConversationInfo.conversation._targetOnlineStateDesc = this.getUserOnlineState(convInfo.conversation.target);
        } else {
            //TODO
        }
    },
    _loadFriendRequest() {
        let requests = wfc.getIncommingFriendRequest()

        requests.sort((a, b) => numberValue(b.timestamp) - numberValue(a.timestamp))
        requests = requests.length >= 20 ? requests.slice(0, 20) : requests;
        let uids = [];
        requests.forEach(fr => {
            uids.push(fr.target);
        });
        let userInfos = wfc.getUserInfos(uids, '')
        requests.forEach(fr => {
            let userInfo = userInfos.find((u => u.uid === fr.target));
            fr._target = userInfo;
        });

        contactState.friendRequestList = requests;
        contactState.unreadFriendRequestCount = wfc.getUnreadFriendRequestCount();
    },

    _patchAndSortUserInfos(userInfos, groupId = '', compareFn) {
        userInfos = userInfos.map(u => {
            if (groupId) {
                u._displayName = wfc.getGroupMemberDisplayNameEx(u);
                u._displayNameIgnoreFriendAlias = wfc.getGroupMemberDisplayNameEx(u, true);
            } else {
                u._displayName = wfc.getUserDisplayNameEx(u);
            }
            u._pinyin = convert(u._displayName, {style: 0}).join('').trim().toLowerCase();
            let firstLetter = u._pinyin[0];
            if (firstLetter >= 'a' && firstLetter <= 'z') {
                u.__sortPinyin = 'a' + u._pinyin;
            } else {
                u.__sortPinyin = 'z' + u._pinyin;
            }
            u._firstLetters = convert(u._displayName, {style: 4}).join('').trim().toLowerCase();
            return u;
        });
        if (compareFn) {
            userInfos = userInfos.sort(compareFn);
        } else {
            userInfos = userInfos.sort((a, b) => a.__sortPinyin.localeCompare(b.__sortPinyin));
        }

        userInfos.forEach(u => {
            let uFirstLetter = u.__sortPinyin[1];
            if (uFirstLetter >= 'a' && uFirstLetter <= 'z') {
                u._category = uFirstLetter;
            } else {
                u._category = '#';
            }
            u._userOnlineStatusDesc = this.getUserOnlineState(u.uid);
        });
        return userInfos;
    },

    _loadFavGroupList() {
        contactState.favGroupList = wfc.getFavGroupList();
    },

    _loadChannelList() {
        wfc.getRemoteListenedChannels(channelIds => {
            if (channelIds) {
                contactState.channelList = channelIds.map(channelId => wfc.getChannelInfo(channelId, false));
                contactState.channelList = contactState.channelList.filter(ch => {
                    return !(ch instanceof NullChannelInfo)
                });
            }
        }, err => {
            console.error('getRemoteListenedChannels error', err)
        });
    },


    _loadFavContactList() {
        let favUserIds = wfc.getFavUsers();
        if (favUserIds.length > 0) {
            contactState.favContactList = this.getUserInfos(favUserIds, '')
            contactState.favContactList.forEach(u => {
                u._category = '☆ 星标朋友';
            })
        } else {
            contactState.favContactList = [];
        }
    },

    reloadFavGroupList() {
        this._loadFavGroupList();
    },

    setCurrentFriendRequest(friendRequest) {
        contactState.currentFriendRequest = friendRequest;
        contactState.currentFriend = null;
        contactState.currentOrganization = null;
        contactState.currentChatroom = null;
        contactState.currentGroup = null;
        contactState.currentChannel = null;
        contactState.currentExternalDomain = null;
    },

    setCurrentFriend(friend) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = friend;
        contactState.currentOrganization = null;
        contactState.currentChatroom = null;
        contactState.currentGroup = null;
        contactState.currentChannel = null;
        contactState.currentExternalDomain = null;
    },

    setCurrentGroup(group) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = null;
        contactState.currentOrganization = null;
        contactState.currentChatroom = null;
        contactState.currentGroup = group;
        contactState.currentChannel = null;
        contactState.currentExternalDomain = null;
    },

    setCurrentChannel(channel) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = null;
        contactState.currentOrganization = null;
        contactState.currentChatroom = null;
        contactState.currentGroup = null;
        contactState.currentChannel = channel;
        contactState.currentExternalDomain = null;
    },

    setCurrentOrganization(organization) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = null;
        contactState.currentGroup = null;
        contactState.currentChannel = null;
        contactState.currentChatroom = null;
        contactState.currentOrganization = organization;
        contactState.currentExternalDomain = null;
    },

    setCurrentExternalDomain(domainInfo) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = null;
        contactState.currentGroup = null;
        contactState.currentChannel = null;
        contactState.currentChatroom = null;
        contactState.currentOrganization = null;
        contactState.currentExternalDomain = domainInfo;
    },

    setCurrentChatroom(chatroom) {
        contactState.currentFriendRequest = null;
        contactState.currentFriend = null;
        contactState.currentGroup = null;
        contactState.currentChannel = null;
        contactState.currentOrganization = null;
        contactState.currentChatroom = chatroom;
        contactState.currentExternalDomain = null;
    },
    toggleGroupList() {
        contactState.expandGroup = !contactState.expandGroup;
    },

    toggleChannelList() {
        contactState.expandChanel = !contactState.expandChanel;
    },

    toggleFriendRequestList() {
        contactState.expandFriendRequestList = !contactState.expandFriendRequestList;
    },

    toggleFriendList() {
        contactState.expandFriendList = !contactState.expandFriendList;
    },

    toggleOrganizationList() {
        contactState.expandOrganization = !contactState.expandOrganization;
    },

    toggleExternalDomainList() {
        contactState.expandExternalDomain = !contactState.expandExternalDomain;
    },

    toggleChatroom() {
        contactState.expandChatroom = !contactState.expandChatroom;
    },

    // search actions
    hideSearchView() {
        searchState.query = '';
    },

    setSearchQuery(query) {
        searchState.query = query;
        if (query) {
            console.log('search', query)
            if (searchState.searchDomainInfo) {
                this.searchUser(query, searchState.searchDomainInfo.domainId);
            } else {
                searchState.contactSearchResult = this.filterContact(query);
                searchState.groupSearchResult = this.filterGroupConversation(query);
                searchState.conversationSearchResult = this.filterConversation(query);
                // searchState.messageSearchResult = this.searchMessage(query);
                this.searchUser(query);
                this.searchChannel(query);
            }

        } else {
            searchState._reset();
        }
    },

    setSearchDomainInfo(domainInfo) {
        searchState.searchDomainInfo = domainInfo;
    },

    searchUser(query, domainId = '') {
        console.log('search user', query)
        wfc.searchUserEx(domainId, query, SearchType.General, 0, ((keyword, userInfos) => {
            console.log('search user result', query, userInfos)
            if (searchState.query === keyword) {
                searchState.userSearchResult = userInfos.filter(u => !wfc.isMyFriend(u.uid));
            }
        }), (err) => {
            console.log('search user error', query, err)
            if (searchState.query === query) {
                searchState.userSearchResult = [];
            }
        });
    },

    searchChannel(query) {
        console.log('search channel')
        wfc.searchChannel(query, true, (keyword, channelInfos) => {
            console.log('search channel result', channelInfos);
            if (searchState.query === keyword) {
                console.log('search channel result', channelInfos);
                searchState.channelSearchResult = channelInfos;
            }
        }, err => {
            console.log('search channel error', query, err)
            if (searchState.query === query) {
                searchState.channelSearchResult = [];
            }
        })
    },

    // TODO 到底是什么匹配了
    filterContact(query) {
        let result = contactState.friendList.filter(u => {
            return u._displayName.indexOf(query) > -1 || u._firstLetters.indexOf(query.toLowerCase()) > -1 || u._pinyin.indexOf(query.toLowerCase()) > -1
        });

        console.log('friend searchResult', result)
        return result;
    },

    searchFiles(keyword, beforeMessageUid, successCB, failCB) {
        if (!keyword) {
            return;
        }
        wfc.searchFiles(keyword, null, '', beforeMessageUid, 0, 20,
            (files) => {
                this._patchFileRecords(files);
                successCB && successCB(files);
            },
            (errorCode) => {
                console.log('search file error', errorCode);
                failCB && failCB(errorCode);
            })
    },

    filterUsers(users, filter) {
        if (!users || !filter || !filter.trim()) {
            return users;
        }
        let queryPinyin = convert(filter, {style: 0}).join('').trim().toLowerCase();
        let result = users.filter(u => {
            return u._displayName.indexOf(filter) > -1 || u._displayName.indexOf(queryPinyin) > -1
                || u._pinyin.indexOf(filter) > -1 || u._pinyin.indexOf(queryPinyin) > -1
                || u._firstLetters.indexOf(filter) > -1 || u._firstLetters.indexOf(queryPinyin) > -1
        });
        return result;
    },

    // TODO 匹配类型，是群名称匹配上了，还是群成员的名称匹配上了？
    // 目前只搜索群名称
    filterFavGroup(query) {
        console.log('to search group', contactState.favGroupList)
        let queryPinyin = convert(query, {style: 0}).join('').trim().toLowerCase();
        let result = contactState.favGroupList.filter(g => {
            let groupNamePinyin = convert(g.name, {style: 0}).join('').trim().toLowerCase();
            return g.name.indexOf(query) > -1 || g.name.indexOf(queryPinyin) > -1
                || groupNamePinyin.indexOf(query) > -1 || groupNamePinyin.indexOf(queryPinyin) > -1
        });

        console.log('group searchResult', result)
        return result;
    },

    filterConversation(query) {
        return conversationState.conversationInfoList.filter(info => {
            let displayNamePinyin = convert(info.conversation._target._displayName, {style: 0}).join('').trim().toLowerCase();
            let firstLetters = convert(info.conversation._target._displayName, {style: 4}).join('').trim().toLowerCase();
            return info.conversation._target._displayName.indexOf(query) > -1 || displayNamePinyin.indexOf(query.toLowerCase()) > -1 || firstLetters.indexOf(query) > -1
        })
    },

    filterGroupConversation(query) {
        // query = query.toLowerCase();
        // let groups = conversationState.conversationInfoList.filter(info => info.conversation.type === ConversationType.Group).map(info => info.conversation._target);
        // return groups.filter(groupInfo => {
        //     let namePinyin = convert(groupInfo.name, {style: 0}).join('').trim().toLowerCase();
        //     let firstLetters = convert(groupInfo.name, {style: 4}).join('').trim().toLowerCase();
        //     return groupInfo.name.indexOf(query) > -1 || namePinyin.indexOf(query) > -1 || firstLetters.indexOf(query) > -1
        // })
        let gsr = wfc.searchGroups(query)
        return gsr.map(r => r.groupInfo);
    },

    searchMessage(conversation, query) {
        let msgs = wfc.searchMessage(conversation, query)
        msgs = msgs.reverse();
        return msgs.map(m => this._patchMessage(m, 0));
    },

    searchMessageInTypes(conversation, contentTypes, query, offset) {
        let msgs = wfc.searchMessageByTypes(conversation, query, contentTypes, true, 20, offset)
        return msgs.map(m => this._patchMessage(m, 0));
    },

    searchConversation(query, types = [0, 1, 2], lines = [0, 1, 2]) {
        let results = wfc.searchConversation(query, types, lines);
        return results.map(r => {
            let info = wfc.getConversationInfo(r.conversation);
            r._conversationInfo = this._patchConversationInfo(info, false);
            return r;
        })
    },

    // pick actions
    pickOrUnpickUser(user) {
        let index = pickState.users.findIndex(u => u.uid === user.uid);
        if (index >= 0) {
            pickState.users = pickState.users.filter(u => user.uid !== u.uid)
        } else {
            pickState.users.push(user);
        }
    },

    isUserPicked(user) {
        let index = pickState.users.findIndex(u => u.uid === user.uid);
        return index >= 0;
    },

    // pick actions
    pickOrUnpickOrganization(org) {
        let index = pickState.organizations.findIndex(o => o.id === org.id);
        if (index >= 0) {
            pickState.organizations = pickState.organizations.filter(o => o.id !== org.id)
        } else {
            pickState.organizations.push(org);
        }
    },

    isOrganizationPicked(org) {
        let index = pickState.organizations.findIndex(o => o.id === org.id);
        return index >= 0;
    },

    pickOrUnpickConversation(conversation) {
        let index = pickState.conversations.findIndex(c => (conversation.target === c.target && conversation.line === c.line && conversation.type === c.type))
        if (index >= 0) {
            pickState.conversations = pickState.conversations.filter(c => !(conversation.target === c.target && conversation.line === c.line && conversation.type === c.type))
        } else {
            pickState.conversations.push(conversation);
        }
    },

    // misc actions
    createConversation(users, successCB, failCB) {
        if (users.length === 1) {
            let conversation = new Conversation(ConversationType.Single, users[0].uid, 0);
            this.setCurrentConversation(conversation);
            successCB && successCB(conversation);
            return;
        }

        let groupName = contactState.selfUserInfo.displayName;
        let groupMemberIds = [];
        for (let i = 0; i < users.length; i++) {
            groupMemberIds.push(users[i].uid)
            if (i <= 3) {
                groupName += '、' + users[i].displayName;
            }
        }
        groupName = groupName.substr(0, groupName.length - 1);
        wfc.createGroup(null, GroupType.Restricted, groupName, null, null, groupMemberIds, null, [0], null,
            (groupId) => {
                let conversation = new Conversation(ConversationType.Group, groupId, 0)
                this.setCurrentConversation(conversation);
                successCB && successCB(conversation);
            }, (error) => {
                console.log('create group error', error)
                failCB && failCB(error);
            });
    },

    _loadUserLocalSettings() {
        let userId = wfc.getUserId();
        // 默认允许通知
        let setting = getItem(userId + '-' + 'notification');
        miscState.enableNotification = setting === null || setting === '1'
        setting = getItem(userId + '-' + 'notificationDetail');
        miscState.enableNotificationMessageDetail = setting === null || setting === '1'
        miscState.enableCloseWindowToExit = getItem(userId + '-' + 'closeWindowToExit') === '1'
        miscState.enableAutoLogin = getItem(userId + '-' + 'autoLogin') === '1'
        setting = getItem('minimizable')
        miscState.enableMinimize = setting === null || setting === '1'
    },

    setEnableNotification(enable) {
        miscState.enableNotification = enable;
        setItem(contactState.selfUserInfo.uid + '-' + 'notification', enable ? '1' : '0')
    },

    setEnableMinimize(enable) {
        miscState.enableMinimize = enable;
        setItem('minimizable', enable ? '1' : '0')
        currentWindow.minimizable = enable;
    },

    setEnableNotificationDetail(enable) {
        miscState.enableNotificationMessageDetail = enable;
        setItem(contactState.selfUserInfo.uid + '-' + 'notificationDetail', enable ? '1' : '0')
    },

    setEnableCloseWindowToExit(enable) {
        miscState.enableCloseWindowToExit = enable;
        setItem(contactState.selfUserInfo.uid + '-' + 'closeWindowToExit', enable ? '1' : '0')
        ipcRenderer.send(IPCEventType.ENABLE_CLOSE_WINDOW_TO_EXIT, enable)
    },

    setEnableAutoLogin(enable) {
        miscState.enableAutoLogin = enable;
        setItem(contactState.selfUserInfo.uid + '-' + 'autoLogin', enable ? '1' : '0')
    },

    // clone一下，别影响到好友列表
    getUserInfos(userIds, groupId) {
        let userInfos = wfc.getUserInfos(userIds, groupId);
        let userInfosCloneCopy = userInfos.map(u => Object.assign({}, u));
        return this._patchAndSortUserInfos(userInfosCloneCopy, groupId);
    },

    // clone一下，别影响到好友列表
    getGroupMemberUserInfos(groupId, includeSelf = true, sortByPinyin = false) {

        let memberIds = wfc.getGroupMemberIds(groupId);
        let userInfos = wfc.getUserInfos(memberIds, groupId);
        if (!includeSelf) {
            userInfos = userInfos.filter(u => u.uid !== wfc.getUserId())
        }
        let userInfosCloneCopy = userInfos.map(u => Object.assign({}, u));
        if (sortByPinyin) {
            return this._patchAndSortUserInfos(userInfosCloneCopy, groupId);
        } else {
            let compareFn = (u1, u2) => {
                let index1 = memberIds.findIndex(id => id === u1.uid)
                let index2 = memberIds.findIndex(id => id === u2.uid)
                return index1 - index2;
            }
            return this._patchAndSortUserInfos(userInfosCloneCopy, groupId, compareFn);
        }
    },

    // clone一下，别影响到好友列表
    getConversationMemberUsrInfos(conversation) {
        let userInfos = [];
        if (conversation.type === 0) {
            if (conversation.target !== contactState.selfUserInfo.uid) {
                userInfos.push(wfc.getUserInfo(wfc.getUserId(), false));
            }
            userInfos.push(wfc.getUserInfo(conversation.target, false));
            let userInfosCloneCopy = userInfos.map(u => Object.assign({}, u));
            userInfos = this._patchAndSortUserInfos(userInfosCloneCopy, '');
        } else if (conversation.type === 1) {
            userInfos = this.getGroupMemberUserInfos(conversation.target, true);
        }
        return userInfos;
    },

    getMyFileRecords(beforeUid, count, successCB, failCB) {
        if (!successCB) {
            return;
        }
        wfc.getMyFileRecords(beforeUid, 0, count, fileRecords => {
            this._patchFileRecords(fileRecords)
            successCB(fileRecords);
        }, failCB)
    },

    getConversationFileRecords(conversation, fromUser, beforeMessageUid, count, successCB, failCB) {
        wfc.getConversationFileRecords(conversation, fromUser, beforeMessageUid, 0, count, fileRecords => {
            this._patchFileRecords(fileRecords)
            successCB(fileRecords);
        }, failCB);
    },

    deleteFriend(target) {
        wfc.deleteFriend(target, () => {
            let conv = new Conversation(ConversationType.Single, target, 0);
            wfc.removeConversation(conv, true);
            conversationState.conversationInfoList = conversationState.conversationInfoList.filter(info => !info.conversation.equal(conv))
        }, (err) => {
            console.log('deleteFriend error', err);
        });
    },

    _patchFileRecords(fileRecords) {
        fileRecords.forEach(fileRecord => {
            let groupId = fileRecord.conversation.type === 1 ? fileRecord.conversation.target : '';
            if (groupId) {
                fileRecord._userDisplayName = wfc.getGroupMemberDisplayName(groupId, fileRecord.userId);
            } else {
                fileRecord._userDisplayName = wfc.getUserDisplayName(fileRecord.userId);
            }
            let conversationInfo = wfc.getConversationInfo(fileRecord.conversation);
            this._patchConversationInfo(conversationInfo, false);

            if (fileRecord.conversation.type === 0) {
                fileRecord._conversationDisplayName = '与' + conversationInfo.conversation._target._displayName + '的聊天';
            } else {
                fileRecord._conversationDisplayName = conversationInfo.conversation._target._displayName;
            }
            if (fileRecord.name.indexOf(FileMessageContent.FILE_NAME_PREFIX) === 0) {
                fileRecord.name = fileRecord.name.substring(fileRecord.name.indexOf(FileMessageContent.FILE_NAME_PREFIX) + FileMessageContent.FILE_NAME_PREFIX.length);
            }
            fileRecord._timeStr = helper.dateFormat(fileRecord.timestamp);
            fileRecord._sizeStr = helper.humanSize(fileRecord.size)
            fileRecord._fileIconName = helper.getFiletypeIcon(fileRecord.name.substring(fileRecord.name.lastIndexOf('.')))
        });
    },

    setPageVisibility(visible) {
        miscState.isPageHidden = !visible;
        if (!visible) {
            conversationState.shouldAutoScrollToBottom = false;
        } else if (conversationState.currentConversationInfo) {
            conversationState.shouldAutoScrollToBottom = true;
        }
    },

    clearConversationUnreadStatus(conversation) {
        let info = wfc.getConversationInfo(conversation);
        if (info && (info.unreadCount.unread + info.unreadCount.unreadMention + info.unreadCount.unreadMentionAll) > 0) {
            wfc.clearConversationUnreadStatus(conversation);
            this.updateTray();
        }
    },

    clearAllUnreadStatus() {
        wfc.clearAllUnreadStatus();
        conversationState.conversationInfoList.forEach(info => {
            info.unreadCount = new UnreadCount();
        });
        this.updateTray();
    },

    notify(msg) {
        let content = msg.messageContent;
        let icon = require('@/assets/images/icon.png');
        let tip
        let now = new Date().getTime();
        if (this._lastNotificationTime && now - this._lastNotificationTime < 4000) {
            return;
        }
        this._lastNotificationTime = now;

        if (msg.direction === 0 /* && !(type===0 && target===file_transfer_id)*/) {
            return;
        }
        if (MessageConfig.getMessageContentPersitFlag(content.type) === PersistFlag.Persist_And_Count) {
            if (msg.status !== MessageStatus.AllMentioned && msg.status !== MessageStatus.Mentioned) {
                let silent = false;
                for (const info of conversationState.conversationInfoList) {
                    if (info.conversation.equal(msg.conversation)) {
                        silent = info.isSilent;
                        break;
                    }
                }
                if (silent) {
                    return;
                }
                tip = "新消息来了";
            } else {
                tip = "有人@你";
            }

            Push.create(tip, {
                body: miscState.enableNotificationMessageDetail ? content.digest(msg) : '',
                // TODO 下面好像不生效，更新成图片链接
                icon: icon,
                timeout: 4000,
                requireInteraction: true,
                onClick: () => {
                    if (isElectron()) {
                        ipcRenderer.send(IPCEventType.CLICK_NOTIFICATION, currentWindow.getMediaSourceId())
                    } else {
                        window.focus();
                        this.close();
                    }
                    this.setCurrentConversation(msg.conversation)
                }
            }).catch(e => {
                console.error('Please enable notification')
            });
        }
    },

    updateTray() {
        if (!isElectron() || !miscState.isMainWindow) {
            return;
        }
        let count = 0;
        conversationState.conversationInfoList.forEach(info => {
            if (info.isSilent) {
                return;
            }
            let unreadCount = info.unreadCount;
            count += unreadCount.unread;
        });
        if (process.platform === 'linux') {
            this.updateLinuxTitle(count);
            ipcRenderer.send(IPCEventType.UPDATE_BADGE, count)
        } else {
            ipcRenderer.send(IPCEventType.UPDATE_BADGE, count)
        }
    },

    updateLinuxTitle(unreadCount) {
        this.updateLinuxTitle.title = '野火IM';
        this.updateLinuxTitle.unreadCount = unreadCount;
        this.updateLinuxTitle.showTitle = true;
        if (!miscState.linuxUpdateTitleInterval) {
            miscState.linuxUpdateTitleInterval = setInterval(() => {
                if (this.updateLinuxTitle.showTitle || this.updateLinuxTitle.unreadCount < 1) {
                    document.title = this.updateLinuxTitle.title;
                } else {
                    document.title = this.updateLinuxTitle.title + ' ' + this.updateLinuxTitle.unreadCount;
                }
                this.updateLinuxTitle.showTitle = !this.updateLinuxTitle.showTitle;
            }, 1000)
        }
    },

    updateVoipStatus(conversation, isOngoing) {
        miscState.isVoipOngoing = isOngoing;
        if (!conversation) {
            return
        }
        conversationState.conversationInfoList.forEach(ci => {
            if (ci.conversation.equal(conversation)) {
                ci._isVoipOngoing = isOngoing;
            } else {
                ci._isVoipOngoing = false;
            }
        })
    },

    _filterForwardMessageContent(message) {
        let content = message.messageContent
        if (content instanceof CallStartMessageContent) {
            content = new TextMessageContent(content.digest(message))
        } else if (content instanceof SoundMessageContent) {
            content = new TextMessageContent(content.digest(message) + ' ' + content.duration + "''");
        }
        return content
    }
}


function _reset() {
    conversationState._reset();
    contactState._reset();
    searchState._reset();
    pickState._reset();
    miscState._reset();
}

window.__store = store;
window.stringValue = stringValue;
window.longValue = longValue;
export default store
