#include "expertboxlayoutwin.h"
#include "expertwin.h"
#include "gutil/qgui.h"
#include "gutil/qjson.h"
#include "globalfunc.h"
#include <QPushButton>
#include <QCheckBox>
#include <QMessageBox>
#include <QScrollArea>
#include <QFileDialog>
#include <QPainter>
#include <QMouseEvent>
#include <QToolButton>
#include <QPainterPath>
#include <QGroupBox>

ExpertBoxLayoutWin::ExpertBoxLayoutWin(ExpertWin *parent) : BaseWin{parent} {
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowTitle("箱体高级布局");
    resize(1024, 720);
    setStyleSheet("QToolButton {border: none; }");

    auto vBox = new VBox(center);
    vBox->setContentsMargins(0,0,0,0);
    vBox->setSpacing(3);
    vBox->addLayout(addBtns(new QHBoxLayout));

    auto hBox = new HBox(vBox);
    vBox = new VBox(hBox);

    auto gBox = new QGroupBox("模组编辑");
    vBox->addWidget(gBox);
    auto grid = new Grid(gBox);

    auto btnAdd = new QPushButton("添加");
    connect(btnAdd, &QPushButton::clicked, this, [=] {
        auto file = QFileDialog::getOpenFileName(this, tr("打开单元板文件"), gFileHome, tr("单元板文件 (*.module)"));
        if(file.isEmpty()) return;
        gFileHome = QFileInfo(file).absolutePath();
        QFile qFile(file);
        if(! qFile.open(QFile::ReadOnly)) {
            QMessageBox::critical(this, tr("打开单元板文件失败"), file);
            return;
        }
        QString err;
        auto json = JFrom(&qFile, &err);
        qFile.close();
        if(! err.isEmpty()) {
            QMessageBox::critical(this, tr("解析 json 失败"), err+" "+file);
            return;
        }
        QDialog dlg(this);
        dlg.setWindowTitle(tr("添加模组"));
        auto vBox = new VBox(&dlg);
        auto hBox = new HBox(vBox);
        hBox->addLabel(tr("横向数量"));
        auto fdHNum = new QSpinBox;
        fdHNum->setRange(1, 999);
        fdHNum->setValue(1);
        hBox->addWidget(fdHNum);
        hBox->addStretch();

        hBox = new HBox(vBox);
        hBox->addLabel(tr("纵向数量"));
        auto fdVNum = new QSpinBox;
        fdVNum->setRange(1, 999);
        fdVNum->setValue(1);
        hBox->addWidget(fdVNum);
        hBox->addStretch();
        vBox->addStretch();
        connect(vBox->addBtnBox(&dlg), &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
        if(dlg.exec()!=QDialog::Accepted) return;
        auto hNum = fdHNum->value();
        auto vNum = fdVNum->value();
        auto width = json["ModuleWidth"].toInt();
        auto height = json["ModuleHeight"].toInt();
        for(int vv=0; vv < vNum; ++vv) for(int hh=0; hh < hNum; ++hh) {
            auto unit = new ModuleUnit(width*hh, height*vv, width, height, json.toObj(), "", box);
            unit->show();
        }
        fdModNum->setNum(box->children().size());
        box->fitSize();
    });
    grid->addWidget(btnAdd, 0, 0);
    auto btnDel = new QPushButton("删除");
    grid->addWidget(btnDel, 0, 1);
    auto btnMove = new QPushButton("移动");
    grid->addWidget(btnMove, 1, 0);
    auto btnRotate = new QPushButton("旋转");
    grid->addWidget(btnRotate, 1, 1);

    auto btnDraw = new QPushButton("开始走线");
    btnDraw->setCheckable(true);
    connect(btnDraw, &QPushButton::clicked, this, [=](bool checked) {
        box->isDrawing = checked;
        btnDraw->setText(checked ? "结束走线" : "开始走线");
    });
    grid->addWidget(btnDraw, 2, 0);

    auto btnClearLines = new QPushButton("清空所有走线");
    connect(btnClearLines, &QPushButton::clicked, this, [=] {
        for(auto btn : grpGrp->buttons()) if(btn->property("mods").isValid()) btn->setProperty("mods", QVariant());
        for(auto child : box->children()) ((ModuleUnit*)child)->name = QString();
        box->update();
    });
    grid->addWidget(btnClearLines, 3, 0);

    auto btnClearLine = new QPushButton("清空当前走线");
    connect(btnClearLine, &QPushButton::clicked, this, [=] {
        for(auto btn : grpGrp->buttons()) {
            auto mods = btn->property("mods").toList();
            for(auto mod : mods) if(((ModuleUnit*) mod.value<void*>())->isSel) {
                btn->setProperty("mods", QVariant());
                for(auto mod : mods) ((ModuleUnit*) mod.value<void*>())->name = QString();
                break;
            }
        }
        box->update();
    });
    grid->addWidget(btnClearLine, 3, 1);

    vBox->addSpacing(10);

    gBox = new QGroupBox("数据组数设置");
    vBox->addWidget(gBox);
    auto hh = new HBox(gBox);

    hh->addLabel("组数:");
    auto fdGNum = new QSpinBox;
    fdGNum->setRange(1, 128);
    fdGNum->setValue(16);
    hh->addWidget(fdGNum);

    auto btnSet = new QPushButton("设置");
    hh->addWidget(btnSet);

    grid = new Grid(vBox);
    grpGrp = new QButtonGroup(fdGNum);
    connect(btnSet, &QPushButton::clicked, this, [=] {
        auto newCnt = fdGNum->value();
        auto oldCnt = grpGrp->buttons().size();
        if(newCnt>oldCnt) for(int i=oldCnt; i<newCnt; ++i) {
            auto btn = new QPushButton("G"+QString::number(i+1));
            btn->setCheckable(true);
            btn->setMaximumWidth(40);
            btn->setFixedHeight(24);
            grpGrp->addButton(btn, i);
            grid->addWidget(btn, i / 4, i % 4);
        } else if(newCnt<oldCnt) for(int i=oldCnt-1; i>=newCnt; --i) {
            auto btn = grpGrp->button(i);
            grpGrp->removeButton(btn);
            grid->removeWidget(btn);
            delete btn;
        }
        if(grpGrp->checkedButton()==0) grpGrp->buttons()[0]->setChecked(true);
    });
    emit btnSet->clicked();

    vBox->addSpacing(10);

    gBox = new QGroupBox("显示");
    vBox->addWidget(gBox);
    grid = new Grid(gBox);

    fdHasPos = new QCheckBox("位置");
    fdHasPos->setChecked(true);
    grid->addWidget(fdHasPos, 0, 0);
    fdHasSize = new QCheckBox("大小");
    fdHasSize->setChecked(true);
    grid->addWidget(fdHasSize, 0, 1);
    fdHasConn = new QCheckBox("连接");
    fdHasConn->setChecked(true);
    grid->addWidget(fdHasConn, 1, 0);
    fdHasName = new QCheckBox("名称");
    fdHasName->setChecked(true);
    grid->addWidget(fdHasName, 1, 1);
    fdHasOutline = new QCheckBox("轮廓");
    fdHasOutline->setChecked(true);
    grid->addWidget(fdHasOutline, 2, 0);
    fdSnap = new QCheckBox("吸附");
    fdSnap->setChecked(true);
    grid->addWidget(fdSnap, 2, 1);

    vBox->addSpacing(10);

    gBox = new QGroupBox("对齐");
    vBox->addWidget(gBox);
    hh = new HBox(gBox);

    QSize imgSize(32,32);

    auto bnAlignLeft = new QToolButton;
    bnAlignLeft->setIconSize(imgSize);
    bnAlignLeft->setIcon(QIcon(":/imgs/align-left.png"));
    connect(bnAlignLeft, &QToolButton::clicked, this, [=] {
        int bound = INT_MAX;
        ModuleUnit* mod;
        for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() < bound) bound = mod->x();
        bool needFit = false;
        if(bound != INT_MAX) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() != bound) {
            mod->move(bound, mod->y());
            mod->mX = qRound(mod->x() / box->rate);
            needFit = true;
        }
        if(needFit) box->fitSize();
    });
    hh->addWidget(bnAlignLeft);

    auto bnAlignRight = new QToolButton;
    bnAlignRight->setIconSize(imgSize);
    bnAlignRight->setIcon(QIcon(":/imgs/align-right.png"));
    connect(bnAlignRight, &QToolButton::clicked, this, [=] {
        int bound = INT_MIN, temp;
        ModuleUnit* mod;
        for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && (temp = mod->x()+mod->width()) > bound) bound = temp;
        bool needFit = false;
        if(bound != INT_MIN) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->x() != (temp = bound-mod->width())) {
            mod->move(temp, mod->y());
            mod->mX = qRound(mod->x() / box->rate);
            needFit = true;
        }
        if(needFit) box->fitSize();
    });
    hh->addWidget(bnAlignRight);

    auto bnAlignTop = new QToolButton;
    bnAlignTop->setIconSize(imgSize);
    bnAlignTop->setIcon(QIcon(":/imgs/align-top.png"));
    connect(bnAlignTop, &QToolButton::clicked, this, [=] {
        int bound = INT_MAX;
        ModuleUnit* mod;
        for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() < bound) bound = mod->y();
        bool needFit = false;
        if(bound != INT_MAX) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() != bound) {
            mod->move(mod->x(), bound);
            mod->mY = qRound(mod->y() / box->rate);
            needFit = true;
        }
        if(needFit) box->fitSize();
    });
    hh->addWidget(bnAlignTop);

    auto bnAlignBottom = new QToolButton;
    bnAlignBottom->setIconSize(imgSize);
    bnAlignBottom->setIcon(QIcon(":/imgs/align-bottom.png"));
    connect(bnAlignBottom, &QToolButton::clicked, this, [=] {
        int bound = INT_MIN, temp;
        ModuleUnit* mod;
        for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && (temp = mod->y()+mod->height()) > bound) bound = temp;
        bool needFit = false;
        if(bound != INT_MIN) for(auto child : box->children()) if((mod = (ModuleUnit*)child)->isSel && mod->y() != (temp = bound-mod->height())) {
            mod->move(mod->x(), temp);
            mod->mY = qRound(mod->y() / box->rate);
            needFit = true;
        }
        if(needFit) box->fitSize();
    });
    hh->addWidget(bnAlignBottom);

    gBox = new QGroupBox("排列");
    vBox->addWidget(gBox);
    hh = new HBox(gBox);

    auto bnArrangeLeft = new QToolButton;
    bnArrangeLeft->setIconSize(imgSize);
    bnArrangeLeft->setIcon(QIcon(":/imgs/arrange-left.png"));
    connect(bnArrangeLeft, &QToolButton::clicked, this, [=] {
        int bound = INT_MAX;
        ModuleUnit* tmod;
        std::vector<ModuleUnit*> mods;
        for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) {
            auto it = mods.begin();
            while(it!=mods.end() && (*it)->y() <= tmod->y()) it++;
            mods.insert(it, tmod);
            if(tmod->x() < bound) bound = tmod->x();
        }
        tmod = 0;
        for(auto mod : mods) {
            mod->move(bound, tmod ? tmod->y()+tmod->height() : mod->y());
            tmod = mod;
            mod->mX = qRound(mod->x() / box->rate);
            mod->mY = qRound(mod->y() / box->rate);
        }
        if(! mods.empty()) box->fitSize();
    });
    hh->addWidget(bnArrangeLeft);

    auto bnArrangeRight = new QToolButton;
    bnArrangeRight->setIconSize(imgSize);
    bnArrangeRight->setIcon(QIcon(":/imgs/arrange-right.png"));
    connect(bnArrangeRight, &QToolButton::clicked, this, [=] {
        int bound = INT_MIN;
        ModuleUnit* tmod;
        std::vector<ModuleUnit*> mods;
        for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) {
            auto it = mods.begin();
            while(it!=mods.end() && (*it)->y() <= tmod->y()) it++;
            mods.insert(it, tmod);
            if(tmod->x()+tmod->width() > bound) bound = tmod->x()+tmod->width();
        }
        tmod = 0;
        for(auto mod : mods) {
            mod->move(bound-mod->width(), tmod ? tmod->y()+tmod->height() : mod->y());
            tmod = mod;
            mod->mX = qRound(mod->x() / box->rate);
            mod->mY = qRound(mod->y() / box->rate);
        }
        if(! mods.empty()) box->fitSize();
    });
    hh->addWidget(bnArrangeRight);

    auto bnArrangeTop = new QToolButton;
    bnArrangeTop->setIconSize(imgSize);
    bnArrangeTop->setIcon(QIcon(":/imgs/arrange-top.png"));
    connect(bnArrangeTop, &QToolButton::clicked, this, [=] {
        int bound = INT_MAX;
        ModuleUnit* tmod;
        std::vector<ModuleUnit*> mods;
        for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) {
            auto it = mods.begin();
            while(it!=mods.end() && (*it)->x() <= tmod->x()) it++;
            mods.insert(it, tmod);
            if(tmod->y() < bound) bound = tmod->y();
        }
        tmod = 0;
        for(auto mod : mods) {
            mod->move(tmod ? tmod->x()+tmod->width() : mod->x(), bound);
            tmod = mod;
            mod->mX = qRound(mod->x() / box->rate);
            mod->mY = qRound(mod->y() / box->rate);
        }
        if(! mods.empty()) box->fitSize();
    });
    hh->addWidget(bnArrangeTop);

    auto bnArrangeBottom = new QToolButton;
    bnArrangeBottom->setIconSize(imgSize);
    bnArrangeBottom->setIcon(QIcon(":/imgs/arrange-bottom.png"));
    connect(bnArrangeBottom, &QToolButton::clicked, this, [=] {
        int bound = INT_MIN;
        ModuleUnit* tmod;
        std::vector<ModuleUnit*> mods;
        for(auto child : box->children()) if((tmod = (ModuleUnit*)child)->isSel) {
            auto it = mods.begin();
            while(it!=mods.end() && (*it)->x() <= tmod->x()) it++;
            mods.insert(it, tmod);
            if(tmod->y()+tmod->height() > bound) bound = tmod->y()+tmod->height();
        }
        tmod = 0;
        for(auto mod : mods) {
            mod->move(tmod ? tmod->x()+tmod->width() : mod->x(), bound-mod->height());
            tmod = mod;
            mod->mX = qRound(mod->x() / box->rate);
            mod->mY = qRound(mod->y() / box->rate);
        }
        if(! mods.empty()) box->fitSize();
    });
    hh->addWidget(bnArrangeBottom);

    vBox->addStretch();

    vBox = new VBox(hBox);
    hBox = new HBox(vBox);
    auto fdRoom = new QComboBox;
    fdRoom->setEditable(true);
    fdRoom->addItem("1600%");
    fdRoom->addItem("800%");
    fdRoom->addItem("400%");
    fdRoom->addItem("200%");
    fdRoom->addItem("100%");
    fdRoom->addItem("50%");
    fdRoom->addItem("25%");
    connect(fdRoom, &QComboBox::currentTextChanged, this, [=](const QString &text) {
        if(box==0) return;
        box->rate = text.trimmed().replace('%',"").toInt() / 100.0;
        auto childs = box->children();
        foreach(auto child, childs) {
            auto mod = (ModuleUnit*)child;
            mod->setGeometry(mod->mX*box->rate, mod->mY*box->rate, mod->mW*box->rate, mod->mH*box->rate);
        }
        box->fitSize();
    });
    hBox->addWidget(fdRoom);

    hBox->addLabel("模组数量: ");
    fdModNum = hBox->addLabel();
    hBox->addSpacing(20);

    hBox->addLabel("箱体大小: ");
    fdBoxSize = hBox->addLabel();

    hBox->addStretch();

    auto scroll = new QScrollArea;
    vBox->addWidget(scroll);

    box = new BoxPanel(this);
    scroll->setWidget(box);
    box->resize(1024, 1024);
    box->setAutoFillBackground(true);
    auto pal = palette();
    pal.setColor(QPalette::Window, QColor(0x222222));
    box->setPalette(pal);
    SetCurText(fdRoom, "400%");

    connect(btnDel, &QPushButton::clicked, box, &BoxPanel::del);
    connect(fdHasPos, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update);
    connect(fdHasSize, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update);
    connect(fdHasConn, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update);
    connect(fdHasName, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update);
    connect(fdHasOutline, &QCheckBox::stateChanged, box, (void(QWidget::*)())&QWidget::update);

    auto ModuleWidth = parent->mModule["ModuleWidth"].toInt();
    auto ModuleHeight = parent->mModule["ModuleHeight"].toInt();
    new ModuleUnit(0, 0, ModuleWidth, ModuleHeight, parent->mModule, "", box);
    new ModuleUnit(ModuleWidth, 0, ModuleWidth, ModuleHeight, parent->mModule, "", box);
    new ModuleUnit(0, ModuleHeight, ModuleWidth, ModuleHeight, parent->mModule, "", box);
    new ModuleUnit(ModuleWidth, ModuleHeight, ModuleWidth, ModuleHeight, parent->mModule, "", box);
    fdModNum->setNum(4);
    box->fitSize();
}

void BoxPanel::del() {
    for(auto child=children().begin(); child<children().end();) {
        if(((ModuleUnit*) *child)->isSel) {
            for(auto copied = copieds.begin(); copied < copieds.end(); copied++) if(*copied==*child) {
                copieds.erase(copied);
                break;
            }
            if(! ((ModuleUnit*) *child)->name.isEmpty()) for(auto btn : boxWin->grpGrp->buttons()) {
                auto mods = btn->property("mods").toList();
                for(auto mod=mods.begin(); mod<mods.end(); mod++) if(mod->value<void*>() == *child) {
                    mods.erase(mod);
                    btn->setProperty("mods", mods);
                    goto end;
                }
            }
            end:
            delete *child;
        } else child++;
    }
    boxWin->fdModNum->setNum(children().size());
    fitSize();
}
void BoxPanel::keyPressEvent(QKeyEvent *event) {
    if(event->modifiers() & Qt::ControlModifier) {
        if(event->key()==Qt::Key_C) {
            copieds.clear();
            for(auto child : children()) if(((ModuleUnit*) child)->isSel) copieds.emplace_back((ModuleUnit*) child);
        } else if(event->key()==Qt::Key_V) {
            for(auto child : children()) if(((ModuleUnit*) child)->isSel) ((ModuleUnit*) child)->isSel = false;
            auto off = 16 / rate;
            for(auto copied = copieds.begin(); copied < copieds.end(); copied++) {
                auto unit = new ModuleUnit((*copied)->mX+off, (*copied)->mY+off, (*copied)->mW, (*copied)->mH, (*copied)->mModule, "", this);
                unit->isSel = true;
                unit->show();
                *copied = unit;
            }
            boxWin->fdModNum->setNum(children().size());
            fitSize();
        }
    } else if(event->key()==Qt::Key_Delete) del();
}
void BoxPanel::mousePressEvent(QMouseEvent *event) {
    if(event->button() != Qt::LeftButton) return;
    if(isDrawing) return;
    rect = QRectF(event->localPos(), event->localPos());
    for(auto child : children()) ((ModuleUnit*) child)->isSel = false;
    update();
}
void BoxPanel::mouseReleaseEvent(QMouseEvent *event) {
    if(event->button() != Qt::LeftButton) return;
    if(rect.isNull()) return;
    rect = QRectF();
    update();
}
void BoxPanel::mouseMoveEvent(QMouseEvent *event) {
    if(! (event->buttons() & Qt::LeftButton)) return;
    if(isDrawing) {
        auto mod = (ModuleUnit*) childAt(event->pos());
        if(mod && mod->name.isEmpty()) mod->drawed();
        return;
    }
    rect.setBottomRight(event->localPos());
    for(auto child : children()) {
        auto mod = (ModuleUnit*) child;
        mod->isSel = rect.intersects(mod->geometry());
    }
    update();
}
void BoxPanel::paintEvent(QPaintEvent *) {
    QPainter painter(this);
    if(rate>=2) {
        QBrush bra(0x666666);
        for(int xx=rate; xx<width(); xx+=rate) painter.fillRect(QRectF(xx, 0, -0.5, height()), bra);
        for(int yy=rate; yy<height(); yy+=rate) painter.fillRect(QRectF(0, yy, width(), -0.5), bra);
    }
    ModuleUnit *mod;
    for(auto child : children()) {
        mod = (ModuleUnit*) child;
        QPointF pos(mod->x(), mod->y());
        painter.translate(pos);
        painter.fillRect(QRectF(1, 1, mod->width()-2, mod->height()-2), mod->isSel ? QColor(0, 0xaa, 0, 0xaa) : QColor(0, 0x44, 0xff, 0x77));
        painter.setPen(QColor(mod->isSel ? 0xffffff : 0xdddddd));
        if(boxWin->fdHasOutline->isChecked()) painter.drawRect(QRectF(0.5, 0.5, mod->width()-1, mod->height()-1));
        int off = 0;
        if(boxWin->fdHasPos->isChecked()) painter.drawText(2, off+=15, QString::number(mod->mX)+", "+QString::number(mod->mY));
        if(boxWin->fdHasSize->isChecked()) painter.drawText(2, off+=15, QString::number(mod->mW)+" x "+QString::number(mod->mH));
        if(! mod->name.isEmpty()) painter.drawText(2, off+15, mod->name);
        painter.translate(-pos);
    }
    QPen pen(0xff8800);
    pen.setCapStyle(Qt::FlatCap);
    pen.setDashPattern(QVector<qreal>{4, 4});
    painter.setPen(pen);
    painter.drawRect(QRectF(boxRect.x()-0.5, boxRect.y()-0.5, boxRect.width()+1, boxRect.height()+1));

    if(boxWin->fdHasConn->isChecked()) {
        QBrush brush(0x0088ff);
        painter.setPen(QPen(brush, 3));
        QPainterPath path;
        for(auto btn : boxWin->grpGrp->buttons()) {
            auto mods = btn->property("mods").toList();
            if(mods.isEmpty()) continue;
            QPointF last = ((ModuleUnit*) mods[0].value<void*>())->geometry().center();
            for(int mm=1; mm<mods.size(); mm++) {
                QPointF pos = ((ModuleUnit*) mods[mm].value<void*>())->geometry().center();
                painter.drawLine(last, pos);
                auto deg = 180 / 3.1415926535 * atan((pos.y()-last.y()) / (pos.x()-last.x()));
                if(pos.x()<last.x()) deg += 180;
                last = pos;
                painter.translate(pos);
                painter.rotate(deg);
                path.clear();
                path.lineTo(-30,6);
                path.lineTo(-30,-6);
                path.closeSubpath();
                painter.fillPath(path, brush);
                painter.rotate(-deg);
                painter.translate(-pos);
            }
        }
    }

    if(rect.isNull()) return;
    painter.setPen(0x00aa00);
    painter.drawRect(rect);
}


ModuleUnit::ModuleUnit(int x, int y, int w, int h, const JObj &module, const QString &name, QWidget *parent) : QWidget{parent}, mX{x}, mY{y}, mW{w}, mH{h}, mModule{module}, name{name} {
    box = (BoxPanel*)parent;
    setGeometry(x*box->rate, y*box->rate, w*box->rate, h*box->rate);
}

void ModuleUnit::drawed() {
    auto btn = box->boxWin->grpGrp->checkedButton();
    if(btn==0) return;
    auto mods = btn->property("mods").toList();
    if(! mods.isEmpty() && ((ExpertWin*)box->boxWin->parentWidget())->m_iRcvCardType==enum_zrf) {
        auto last = (ModuleUnit*) mods.last().value<void*>();
        if(last->mY!=mY || last->mX+last->mW!=mX) {
            QMessageBox::warning(this, "注意", "模组要紧跟着前一个");
            return;
        }
    }
    mods.append(QVariant::fromValue((void*)this));
    btn->setProperty("mods", mods);
    name = "G"+QString::number(box->boxWin->grpGrp->id(btn)+1)+"-"+QString::number(mods.size());
    for(auto child : box->children()) ((ModuleUnit*)child)->isSel = false;
    isSel = true;
    box->update();
}
void ModuleUnit::mousePressEvent(QMouseEvent *event) {
    QWidget::mousePressEvent(event);
    if(event->button() != Qt::LeftButton) return;
    event->accept();
    if(box->isDrawing) return;
    if(event->modifiers() & Qt::ControlModifier) return;
    setCursor(Qt::SizeAllCursor);
    auto glbPos = event->globalPos();
    box->grpRect = geometry();
    if(isSel) {
        box->sels.clear();
        ModuleUnit* mod;
        for(auto child : box->children()) if(child!=this && (mod = (ModuleUnit*)child)->isSel) {
            mod->mPressRel = mod->pos() - pos();
            box->grpRect |= mod->geometry();
            box->sels.emplace_back(mod);
        }
    }
    mPressRel = pos() - box->grpRect.topLeft();
    box->grpRect.translate(-glbPos);
}

void ModuleUnit::mouseReleaseEvent(QMouseEvent *event) {
    QWidget::mouseReleaseEvent(event);
    if(Qt::LeftButton != event->button()) return;
    event->accept();
    if(box->isDrawing) {
        if(name.isEmpty()) drawed();
        return;
    }
    if(event->modifiers() & Qt::ControlModifier) {
        isSel = true;
        update();
        return;
    }
    unsetCursor();
    mPressRel.setX(INT_MIN);
    if(! isSel) {
        isSel = true;
        update();
        for(auto item : box->children()) if(item!=this && ((ModuleUnit*)item)->isSel) {
            ((ModuleUnit*)item)->isSel = false;
            ((ModuleUnit*)item)->update();
        }
    }
    for(auto sel : box->sels) sel->mPressRel.setX(INT_MIN);
    box->sels.clear();
}
#define SnapSpace 12
void ModuleUnit::mouseMoveEvent(QMouseEvent *e) {
    if(! (e->buttons() & Qt::LeftButton)) return;
    if(box->isDrawing) {
        if(name.isEmpty()) drawed();
        else {
            auto mod = (ModuleUnit*) box->childAt(x()+e->x(), y()+e->y());
            if(mod && mod!=this && mod->name.isEmpty()) mod->drawed();
        }
        return;
    }
    if(mPressRel.x()==INT_MIN) return;
    ModuleUnit* mod;
    if(! isSel) {
        isSel = true;
        for(auto child : box->children()) if(child!=this && (mod = (ModuleUnit*)child)->isSel) mod->isSel = false;
    }
    auto grpRect = box->grpRect;
    grpRect.translate(e->globalPos());
    auto dstHor = qMax(0, grpRect.x());
    auto dstVer = qMax(0, grpRect.y());
    if(box->boxWin->fdSnap->isChecked() && (dstHor || dstVer)) {
        bool needH = dstHor, needV = dstVer;
        auto nearRect = grpRect.marginsAdded({24,24,24,24});
        for(auto child : box->children()) if(! (mod = (ModuleUnit*)child)->isSel && mod->geometry().intersects(nearRect)) {//左右
            if(needH) {
                if(abs(dstHor - mod->x()) < SnapSpace) {
                    dstHor = mod->x();
                    needH = false;
                    goto vvv;
                }
                auto eleRight = mod->x() + mod->width();
                if(abs(dstHor - eleRight) < SnapSpace) {
                    dstHor = eleRight;
                    needH = false;
                    goto vvv;
                }
                auto right = dstHor + grpRect.width();
                if(abs(right - mod->x()) < SnapSpace && mod->x() - grpRect.width() >= 0) {
                    dstHor = mod->x() - grpRect.width();
                    needH = false;
                    goto vvv;
                }
                if(abs(right - eleRight) < SnapSpace && eleRight - grpRect.width() >= 0) {
                    dstHor = eleRight - grpRect.width();
                    needH = false;
                    goto vvv;
                }
            }
            vvv:
            if(needV) {
                if(abs(dstVer-mod->y()) < SnapSpace) {
                    dstVer = mod->y();
                    needV = false;
                    goto eee;
                }
                auto eleBtm = mod->y() + mod->height();
                if(abs(dstVer - eleBtm) < SnapSpace) {
                    dstVer = eleBtm;
                    needV = false;
                    goto eee;
                }
                auto btm = dstVer + grpRect.height();
                if(abs(btm - mod->y()) < SnapSpace && mod->y() - grpRect.height() >= 0) {
                    dstVer = mod->y() - grpRect.height();
                    needV = false;
                    goto eee;
                }
                if(abs(btm - eleBtm) < SnapSpace && eleBtm - grpRect.height() >= 0) {
                    dstVer = eleBtm - grpRect.height();
                    needV = false;
                    goto eee;
                }
            }
            eee:
            if(! needH && ! needV) break;
        }
    }
    move(mPressRel.x() + dstHor, mPressRel.y() + dstVer);
    mX = qRound(x() / box->rate);
    mY = qRound(y() / box->rate);
    for(auto sel : box->sels) {
        if(sel->mPressRel.x()==INT_MIN) continue;
        sel->move(sel->mPressRel.x() + x(), sel->mPressRel.y() + y());
        sel->mX = qRound(sel->x() / box->rate);
        sel->mY = qRound(sel->y() / box->rate);
    }
    box->fitSize();
}