/*
* Copyright (C) 2019 Tianjin KYLIN Information Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/&gt;.
*
*/
#include "singleApplication.h"
#include <QtNetwork/QLocalSocket>
#include <QFileInfo>

#define TIME_OUT                (500)    // 500ms

SingleApplication::SingleApplication(int &argc, char **argv)
    : QApplication(argc, argv)
    , w(NULL)
    , _isRunning(false)
    , _localServer(NULL) {

    // 取应用程序名作为LocalServer的名字
    // Take the application name as the name of localserver
    _serverName = QFileInfo(QCoreApplication::applicationFilePath()).fileName() + QLatin1String(getenv("DISPLAY"));
    _initLocalConnection();
}


// 检查是否已經有一个实例在运行, true - 有实例运行， false - 没有实例运行
// Check whether there is an instance running, true - there is an instance running, false - there is no instance running
bool SingleApplication::isRunning() {
    return _isRunning;
}


// 通过socket通讯实现程序单实例运行，监听到新的连接时触发该函数
// The single instance of the program is run through socket communication, which is triggered when listening to a new connection
void SingleApplication::_newLocalConnection()
{
    //返回下一个挂起的连接，作为连接的QLocalSocket对象。
    QLocalSocket *socket = _localServer->nextPendingConnection();
    if (socket) {
        //该函数将阻塞，直到可以读取数据并且发出readyRead（）信号为止。 该函数将在毫秒毫秒后超时； 默认超时为30000毫秒。
       // 如果有可供读取的数据，则该函数返回true；否则返回false 否则返回false（如果发生错误或操作超时）。
        socket->waitForReadyRead(2*TIME_OUT);
        //关闭socket
        delete socket;

        // 其他处理，如：读取启动参数
        // Other processing, such as reading start parameters
        _activateWindow();
    }
}

// 通过socket通讯实现程序单实例运行，
// Through socket communication, the single instance of program can be run,
// 初始化本地连接，如果连接不上server，则创建，否则退出
// Initialize the local connection. If the server cannot be connected, create it. Otherwise, exit
void SingleApplication::_initLocalConnection()
{
    _isRunning = false;

    QLocalSocket socket;
    socket.connectToServer(_serverName);
    //建立链接，超时时间500ms
    if (socket.waitForConnected(TIME_OUT)) {
        fprintf(stderr, "%s already running.\n",
                _serverName.toLocal8Bit().constData());
        //有连接，改状态，直接返回
        _isRunning = true;
        // 其他处理，如：将启动参数发送到服务端
        // Other processing, such as sending startup parameters to the server
        return;
    }

    //连接不上服务器，就创建一个
    // If you can't connect to the server, create a
    _newLocalServer();
}

// 创建LocalServer
// Create localserver
void SingleApplication::_newLocalServer()
{
    _localServer = new QLocalServer(this);
    //信号：每当有新的连接
    connect(_localServer, SIGNAL(newConnection()), this, SLOT(_newLocalConnection()));
    if (!_localServer->listen(_serverName)) {
        // 此时监听失败，可能是程序崩溃时,残留进程服务导致的,移除之
        // At this time, listening fails. It may be caused by the residual process service when the program crashes. Remove it
        if (_localServer->serverError() == QAbstractSocket::AddressInUseError) {
            QLocalServer::removeServer(_serverName); // <-- 重点  a key
            _localServer->listen(_serverName); // 再次监听
                                               // Monitor again
        }
    }
}

// 激活主窗口
// Activate main window
void SingleApplication::_activateWindow() {
    if (w) {
        w->show();
        w->raise();
        w->activateWindow(); // 激活窗口
                             //  Activate window
    }
}
