<file_sep>#ifndef BACKEND_H
#define BACKEND_H

#include <QObject>
#include <contactsmodel.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QtSql/QSqlDatabase>

class Backend : public QObject
{
    Q_OBJECT
public:
    explicit Backend(QObject *parent = nullptr);
    ~Backend();

    ContactsModel* contactsModel;
    ChatModel* chatModel;

    Q_INVOKABLE void on_connectionDialog_closed(const QString ipAddress);
    Q_INVOKABLE void on_sendButton_clicked(QString message);
    Q_INVOKABLE void openContact(QString ipAddress);
    Q_INVOKABLE void removeContact(QString ipAddress);
    Q_INVOKABLE void reconnectContact(QString ipAddress);

private slots:
    void incomming_connection();
    void ready_read();
    void on_successful_connection();
    void ready_read_connection_params();
    void on_disconnected();


private:
    const quint16 defaultPort = 25565;
    QTcpServer* server;
    QList<QTcpSocket*> connections;
    QString _userName;
    QSqlDatabase _database;


    void make_connection(const QString ipAddress);
    void getUserName();
    void startServer();
    bool checkIpAddress(QString ipAddress);
    QTcpSocket* findSocket(QString ipAddress);
    QTcpSocket* getCurrentSocketSender();

    bool openDatabase(QString database, QString databaseName);
    bool checkTables(QList<QString>& tables) const;
    bool createDatabaseTables();
    ContactsModel* loadDataFromDatabase();
    bool saveMessage(Message mes, QString ipAddress);
    bool saveContact(Contact* contact);
    bool updateUserName(QString ipAddress,QString name);
    bool deleteContact(QString ipAddress);


};

#endif // BACKEND_H
<file_sep>#ifndef CONTACTSTMODEL_H
#define CONTACTSTMODEL_H

#include <QAbstractListModel>
#include <QVector>
#include <QMap>
#include "contact.h"
#include "chatmodel.h"

class ContactsModel : public QAbstractListModel
{
    Q_OBJECT
public:
    ContactsModel();
    ContactsModel(QObject *parent = nullptr);

    enum{
        IpAddressRole = Qt::UserRole,
        UserNameRole,
        IsActiveRole,
    };

    void addContact(Contact* contact);
    void removeContact(QString ipAddress);
    Contact* getContact(QString contactIp);
    void setUserName(QString contactIp,QString name);
    void setActiveContact(QString contactIp);
    void setActiveContact(Contact* contact);

    ChatModel* getChatModel();

    void addMessage(QString contactIp, Message mes);

private:
    QMap<QString,int> ipAssociation;
    QVector<Contact*> model;
    ChatModel* chatModel;

    // QAbstractItemModel interface
public:
    QVariant data(const QModelIndex &index, int role) const;
    int rowCount(const QModelIndex &parent) const;
    QHash<int, QByteArray> roleNames() const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
};

#endif // CONTACTSTMODEL_H
<file_sep>#include "backend.h"
#include "contact.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QProcessEnvironment>
#include <QDebug>
#include <QTime>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlError>
#include <QtSql/QSqlQuery>

Backend::Backend(QObject *parent) : QObject(parent)
{
    getUserName();

    if(openDatabase("QSQLITE", "chat.db"))
    {
        qDebug() << "success open database";


        QList<QString> tables; //Можно как-то получше этот участок сделать?
        tables.append("contacts");
        tables.append("messages");


        if(checkTables(tables))
        {
            contactsModel = loadDataFromDatabase();
        }
        else
        {
            if(!createDatabaseTables())
            {
                qDebug() << "Exception: not full created database";
                _database.close();
            }
            contactsModel = new ContactsModel(this);
        }

    }

    startServer();

    connect(server, &QTcpServer::newConnection, this, &Backend::incomming_connection);
}

Backend::~Backend()
{
}

void Backend::on_connectionDialog_closed(const QString ipAddress)
{
    if(checkIpAddress(ipAddress))
        make_connection(ipAddress);
}

void Backend::incomming_connection()
{
    qDebug() << "new connection from user";
    QTcpSocket* newConnection = server->nextPendingConnection();
    connections.append(newConnection);

    Contact* contact = new Contact(newConnection->peerAddress().toString());
    contactsModel->addContact(contact);
    if(!saveContact(contact))
        qDebug() << "Failed save contact: " << contact->name << "ip: " << contact->ipAddress;

    connect(newConnection, &QTcpSocket::readyRead, this, &Backend::ready_read_connection_params);

    newConnection->write(_userName.toUtf8());
}

void Backend::ready_read()
{
    QTcpSocket * socket = getCurrentSocketSender();
    qDebug() << "Новое сообщение от " << socket->peerAddress();

    QString message(socket->readAll());
    qDebug() << message;

    Message mes(Message(message, false,QTime::currentTime().toString("h:m:s ap")));
    contactsModel->addMessage(socket->peerAddress().toString(), mes);
    if(!saveMessage(mes, socket->peerAddress().toString()))
        qDebug() << "Exception failed save message for contact: " << socket->peerAddress().toString();
}

void Backend::on_successful_connection()
{
    QTcpSocket * socket = getCurrentSocketSender();

    connections.append(socket);

    socket->write(_userName.toUtf8());

    connect(socket, &QTcpSocket::readyRead, this, &Backend::ready_read_connection_params);
    //disconnect(socket, &QTcpSocket::connected, this, &Backend::on_successful_connection);
}

void Backend::ready_read_connection_params()
{
    QTcpSocket * socket = getCurrentSocketSender();

    QString userName(socket->readAll());

    contactsModel->getChatModel()->setConnectionStatus("Подключено");
    contactsModel->setUserName(socket->peerAddress().toString(), userName);
    if(!updateUserName(socket->peerAddress().toString(), userName))
        qDebug() << "Exception: failed update username ip:" << socket->peerAddress().toString() << " new name: " << userName;

    disconnect(socket, &QTcpSocket::readyRead, this, &Backend::ready_read_connection_params);
    connect(socket, &QTcpSocket::readyRead, this, &Backend::ready_read);
    connect(socket, &QTcpSocket::disconnected, this, &Backend::on_disconnected);
}

void Backend::on_disconnected()
{
    QTcpSocket* socket = getCurrentSocketSender();
    if(connections.removeOne(socket))
    {
        qDebug() << "disconnected";

        if(contactsModel->getChatModel()->getContact() == contactsModel->getContact(socket->peerAddress().toString())) //Открыт ли чат
            contactsModel->getChatModel()->setConnectionStatus("Отключено");
        disconnect(socket, &QTcpSocket::readyRead, this, &Backend::ready_read);

        //delete socket; Почему из-за этого вылет?!
    }
}

void Backend::make_connection(const QString ipAddress)
{
    if(!contactsModel->getContact(ipAddress))
    {
        QTcpSocket* connection = new QTcpSocket(this);
        Contact* newContact = new Contact(ipAddress);

        contactsModel->addContact(newContact);
        contactsModel->getChatModel()->setConnectionStatus("Соединение...");

        if(!saveContact(newContact))
            qDebug() << "Failed save contact: " << newContact->name << "ip: " << newContact->ipAddress;

        connection->connectToHost(ipAddress, defaultPort);
        connect(connection, &QTcpSocket::connected, this, &Backend::on_successful_connection);
    }
}

void Backend::getUserName()
{
    QString name = QProcessEnvironment::systemEnvironment().value("USER");
    if(name.isEmpty())
        name = QProcessEnvironment::systemEnvironment().value("USERNAME");
    _userName = name.toUtf8();
}

void Backend::startServer()
{
    server = new QTcpServer(this);
    bool res = server->listen(QHostAddress::AnyIPv4, defaultPort);
    qDebug() << "server listen = " << res;
    if(!res)
    {
        qDebug() << "repeat server listen = " << server->listen(QHostAddress::AnyIPv4, defaultPort+1);
    }
}

bool Backend::checkIpAddress(QString ipAddress)
{
    QRegExp re("(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)");
    qDebug() << ipAddress;
    return  re.exactMatch(ipAddress);
}

QTcpSocket *Backend::findSocket(QString ipAddress)
{
    if(connections.empty())
        return nullptr;
    for(auto it = connections.cbegin(); it != connections.end(); ++it)
        if((*it)->peerAddress().toString() == ipAddress)
            return *it;

    return nullptr;
}

QTcpSocket *Backend::getCurrentSocketSender()
{
    QObject * object = QObject::sender();
    if (!object)
        return nullptr;
    return static_cast<QTcpSocket *>(object);
}

bool Backend::openDatabase(QString database, QString databaseName)
{
    _database = QSqlDatabase::addDatabase(database,"mainbase"); //Почему нет указателя???
    _database.setDatabaseName(databaseName);
    if(!_database.open()){
        qDebug() << "can't open database: " << database << " name:" << databaseName;
        qCritical() << _database.lastError().text();
        return false;
    }

    return true;
}

bool Backend::checkTables(QList<QString> &tables) const
{
    auto databaseTables = _database.tables();
    for(auto table = tables.cbegin(); table != tables.cend(); ++table)
    {
        if(!databaseTables.contains(*table))
            return false;
    }
    return true;
}

bool Backend::createDatabaseTables()
{
    QSqlQuery query(_database);
    if(!query.exec("PRAGMA foreign_keys=on"))
        qDebug() << "Failed set up foreign_keys";

    qDebug() << "Creating table contacts...";
    if(!query.exec("CREATE TABLE contacts("
               "ip_address varchar(15) NOT NULL PRIMARY KEY,"
               "name VARCHAR(32) NOT NULL);"))
    {
        qDebug() << "failed create table contacts";
        return false;
    }

    qDebug() << "Creating table messages...";
    if(!query.exec("CREATE TABLE messages("
                           "message_PK INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
                           "message TEXT NOT NULL,"
                           "is_my_message Boolean NOT NULL,"
                           "contact_FK varchar(15) NOT NULL,"
                           "time TEXT NOT NULL,"
                           "FOREIGN KEY(contact_FK) REFERENCES contacts(ip_address) On DELETE CASCADE);"))
    {
        qDebug() << "failed create table messages";
        return false;
    }
    return true;
}

ContactsModel *Backend::loadDataFromDatabase()
{
    QSqlQuery contactQuery(_database);
    QSqlQuery messageQuery(_database);

    messageQuery.prepare("SELECT message, is_my_message, time FROM messages WHERE contact_FK = ?");

    contactQuery.exec("SELECT ip_address, ip_address, name FROM contacts");
    ContactsModel* contacts = new ContactsModel(this);
    while (contactQuery.next())
    {
        Contact* contact = new Contact(contactQuery.value("ip_address").toString(), contactQuery.value("name").toString());
        messageQuery.addBindValue(contactQuery.value("ip_address").toString());
        messageQuery.exec();
        while(messageQuery.next())
        {
            Message mes(messageQuery.value("message").toString(), messageQuery.value("is_my_message").toBool(), messageQuery.value("time").toString());
            contact->AddMessage(mes);
        }
        contacts->addContact(contact);
    }
    return contacts;
}

bool Backend::saveMessage(Message mes, QString ipAddress)
{
    if(!_database.isOpen())
        return false;
    QSqlQuery query(_database);
    qDebug() << "Saving message";
    query.prepare("INSERT INTO messages(message, is_my_message, contact_fk, time) VALUES (?,?,?,?)");
    query.addBindValue(mes.text);
    query.addBindValue(mes.isMyMessage);
    query.addBindValue(ipAddress);
    query.addBindValue(mes.time);

    return query.exec();
}

bool Backend::saveContact(Contact *contact)
{
    if(!_database.isOpen())
        return false;
    QSqlQuery query(_database);
    qDebug() << "Saving contact";

    query.prepare("INSERT INTO contacts (ip_address, name) VALUES (?,?)");
    query.addBindValue(contact->ipAddress);
    query.addBindValue(contact->name);
    return query.exec();
}

bool Backend::updateUserName(QString ipAddress, QString name)
{
    if(!_database.isOpen())
        return false;
    QSqlQuery query(_database);
    qDebug() << "Updating name: " << name << " ip:" << ipAddress;

    query.prepare("UPDATE contacts SET name = ? WHERE ip_address = ?");
    query.addBindValue(name);
    query.addBindValue(ipAddress);
    return query.exec();
}

bool Backend::deleteContact(QString ipAddress)
{
    if(!_database.isOpen())
        return false;
    QSqlQuery query(_database);
    qDebug() << "Deliting contact ip address: " << ipAddress;

    query.prepare("DELETE FROM contacts WHERE ip_address == ?");
    query.addBindValue(ipAddress);
    return query.exec();
}

void Backend::on_sendButton_clicked(QString message)
{
    QString ip = contactsModel->getChatModel()->getContact()->ipAddress;
    auto socketReciever = findSocket(ip);
    if(socketReciever)
    {
        socketReciever->write(message.toUtf8());
        Message mes(Message(message, true,QTime::currentTime().toString("h:m:s ap")));
        contactsModel->getChatModel()->addMessage(mes);
        if(!saveMessage(mes, ip))
            qDebug() << "Exception failed save message for contact: " << ip;
    }
}

void Backend::openContact(QString ipAddress)
{
    Contact* contact = contactsModel->getContact(ipAddress);
    ChatModel* chatmodel = contactsModel->getChatModel();
    if(contact == chatmodel->getContact())
        return;
    if(contact) //Хотя это вроде невозможно
    {

        contactsModel->setActiveContact(contact);

        QTcpSocket* contactSocket = findSocket(ipAddress);
        if(contactSocket)
        {

            if(contactSocket->isOpen())
                chatmodel->setConnectionStatus("Подключено");
            else
                chatmodel->setConnectionStatus("Соединение...");
        }
        else
        {
            qDebug() << "Socket doesn't exist";
            chatmodel->setConnectionStatus("Не подключено (Архивная запись)");
        }
    }
}

void Backend::removeContact(QString ipAddress)
{
    QTcpSocket* socket = findSocket(ipAddress);
    if(socket)
    {
        if(socket->isOpen())
            socket->disconnected();
        connections.removeOne(socket);
    }

    Contact* contact = contactsModel->getChatModel()->getContact();
    if(contact)
    {
        if(contact->ipAddress == ipAddress)
        {
            contactsModel->getChatModel()->setContact(nullptr);
        }
    }
    contactsModel->removeContact(ipAddress);

    if(deleteContact(ipAddress))
        qDebug() << "Contact: " << ipAddress << " deleted";
}

void Backend::reconnectContact(QString ipAddress)
{
    return;
}
<file_sep>#include "message.h"

Message::Message()
{

}

Message::Message(QString text, bool isMyMessage, QString time)
{
    this->text = text;
    this->isMyMessage = isMyMessage;
    this->time = time;
}
<file_sep>#ifndef CONTACT_H
#define CONTACT_H

#include <QVector>
#include <message.h>

class Contact
{
public:
    Contact(QString ipAddress = QString(), QString name = "Unknown");


    QString ipAddress;
    QString name;

    QVector<Message> chat;

    void AddMessage(Message mes);

};

#endif // CONTACT_H
<file_sep>#ifndef MESSAGE_H
#define MESSAGE_H

#include <QString>

class Message
{
public:
    Message();
    Message(QString text, bool isMyMessage, QString time);

    QString text;
    bool isMyMessage;
    QString time;
};

#endif // MESSAGE_H
<file_sep>#include "contactsmodel.h"
#include "contact.h"
#include <QtDebug>

ContactsModel::ContactsModel()
{
    chatModel = new ChatModel(this);
}

ContactsModel::ContactsModel(QObject *parent): QAbstractListModel(parent)
{
    chatModel = new ChatModel(this);
}

void ContactsModel::addContact(Contact* contact)
{
    beginInsertRows(QModelIndex(), model.size(), model.size());

    model.append(contact);
    ipAssociation.insert(contact->ipAddress, model.size()-1);
    //chatModel->setContact(contact);

    endInsertRows();

    if(model.size() < 2) //Да, это костыль, но оно того стоит
        chatModel->setContact(contact);
    else
        setActiveContact(contact);
}

void ContactsModel::removeContact(QString ipAddress)
{
    int index = ipAssociation[ipAddress];
    beginRemoveRows(QModelIndex(),index,index);

    model.remove(index);
    ipAssociation.remove(ipAddress);

    for(auto it = ipAssociation.begin(); it != ipAssociation.end(); ++it)
    {
        if((*it) > index)
            --(*it);
    }

    endRemoveRows();
}

Contact* ContactsModel::getContact(QString contactIp)
{
    if(ipAssociation.contains(contactIp))
        return model[ipAssociation[contactIp]];
    else
        return nullptr;

}

void ContactsModel::setUserName(QString contactIp, QString name)
{
    model[ipAssociation[contactIp]]->name = name;
    if(model[ipAssociation[contactIp]] == chatModel->getContact())
        chatModel->userNameChanged();

    QModelIndex changedIndex = createIndex(ipAssociation[contactIp],0);
    dataChanged(changedIndex,changedIndex);
}

void ContactsModel::setActiveContact(QString contactIp)
{
    QModelIndex oldIndex;
    if(chatModel->getContact())
        oldIndex = createIndex(ipAssociation[chatModel->getContact()->ipAddress],0);
    else
        oldIndex = createIndex(model.size(),0);

    chatModel->setContact(model[ipAssociation[contactIp]]);


    QModelIndex newIndex = createIndex(ipAssociation[contactIp],0);
    if(oldIndex < newIndex)
        dataChanged(oldIndex,newIndex);
    else
        dataChanged(newIndex,oldIndex);
}

void ContactsModel::setActiveContact(Contact *contact)
{
    QModelIndex oldIndex;
    if(chatModel->getContact())
        oldIndex = createIndex(ipAssociation[chatModel->getContact()->ipAddress],0);
    else
        oldIndex = createIndex(model.size(),0);

    chatModel->setContact(contact);

    QModelIndex newIndex = createIndex(ipAssociation[contact->ipAddress],0);
    if(oldIndex < newIndex)
        dataChanged(oldIndex,newIndex);
    else
        dataChanged(newIndex,oldIndex);
}

ChatModel *ContactsModel::getChatModel()
{
    return chatModel;
}

void ContactsModel::addMessage(QString contactIp, Message mes)
{
    if(model[ipAssociation[contactIp]] == chatModel->getContact())
        chatModel->addMessage(mes);
    else
        model[ipAssociation[contactIp]]->AddMessage(mes);
}

QVariant ContactsModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();
    switch (role) {
        case IpAddressRole:
            return QVariant(model.at(index.row())->ipAddress);
        case UserNameRole:
            return QVariant(model.at(index.row())->name);
        case IsActiveRole:
            return chatModel->getContact() == model[index.row()];

    }

    return QVariant();
}


int ContactsModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return model.size();
}

QHash<int, QByteArray> ContactsModel::roleNames() const
{
    QHash<int, QByteArray> names;

    names[IpAddressRole] = "ipAddress";
    names[UserNameRole] = "userName";
    names[IsActiveRole] = "isActive";
    return names;
}


Qt::ItemFlags ContactsModel::flags(const QModelIndex &index) const
{
        return Qt::NoItemFlags;
}
<file_sep>#include "chatmodel.h"
#include "contact.h"



ChatModel::ChatModel(QObject* parent): QAbstractListModel(parent)
{
}

ChatModel::ChatModel(Contact *model, QObject *parent):QAbstractListModel(parent)
{
    _contactModel = model;
}

void ChatModel::setContact(Contact *model)
{
    beginResetModel();
    _contactModel = model;
    endResetModel();
    userNameChanged();
}

Contact *ChatModel::getContact()
{
    return _contactModel;
}

void ChatModel::setConnectionStatus(QString connectionStatus)
{
    _connectionStatus = connectionStatus;
    connectionStatusChanged();
}

void ChatModel::setUserName(QString name)
{
    _contactModel->name = name;
    userNameChanged();
}

QString ChatModel::ipAddress()
{
    if(_contactModel)
        return _contactModel->ipAddress;
    else
        return "";
}

QString ChatModel::userName()
{
    if(_contactModel)
        return _contactModel->name;
    else
        return "";
}

QString ChatModel::connectionStatus()
{
    return _connectionStatus;
}

void ChatModel::addMessage(Message mes)
{
    if(_contactModel)
    {
        beginInsertRows(QModelIndex(), _contactModel->chat.size(), _contactModel->chat.size());
        _contactModel->chat.append(mes);
        endInsertRows();
    }
}


int ChatModel::rowCount(const QModelIndex &parent) const
{
    if(_contactModel)
        return _contactModel->chat.size();
    else
        return 0;
}

QVariant ChatModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid() || !_contactModel)
        return QVariant();

    switch (role) {
        case TextRole:
            return _contactModel->chat.at(index.row()).text;
        case IsMyMessage:
            return _contactModel->chat.at(index.row()).isMyMessage;
        case TimeRole:
            return _contactModel->chat.at(index.row()).time;
    }

    return QVariant();
}


QHash<int, QByteArray> ChatModel::roleNames() const
{
    QHash<int, QByteArray> names;

    names[TextRole] = "text";
    names[IsMyMessage] = "isMyMessage";
    names[TimeRole] = "time";

    return names;
}


Qt::ItemFlags ChatModel::flags(const QModelIndex &index) const
{
        return Qt::NoItemFlags;
}
<file_sep>#ifndef CHATMODEL_H
#define CHATMODEL_H

#include <QAbstractListModel>
#include <QVector>
#include <message.h>
#include <contact.h>

class ChatModel : public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(QString userName READ userName NOTIFY userNameChanged)
    Q_PROPERTY(QString ipAddress READ ipAddress NOTIFY ipAddressChanged)
    Q_PROPERTY(QString connectionStatus READ connectionStatus NOTIFY connectionStatusChanged)
public:
    ChatModel(QObject* parent = nullptr);
    ChatModel(Contact* model, QObject* parent = nullptr);

    enum{
        TextRole = Qt::UserRole,
        IsMyMessage,
        TimeRole,
    };

    void setContact(Contact* model);
    Contact* getContact();
    void setConnectionStatus(QString connectionStatus);
    void setUserName(QString name);


    QString ipAddress();
    QString userName();
    QString connectionStatus();

    void addMessage(Message mes);
signals:
    void userNameChanged();
    void ipAddressChanged();
    void connectionStatusChanged();

private:
    Contact* _contactModel;
    QString _connectionStatus = "";

    // QAbstractItemModel interface
public:
    int rowCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    QHash<int, QByteArray> roleNames() const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
};

#endif // CHATMODEL_H
<file_sep>#include "contact.h"

Contact::Contact(QString ipAddress, QString name)
{
    this->ipAddress = ipAddress;
    this->name = name;
}

void Contact::AddMessage(Message mes)
{
    chat.append(mes);
}
