class RequestsOperations(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.initUI()
        self.__ops = []

    def initUI(self):
        self.layout = QFormLayout()
        self.layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        self.layout.setLabelAlignment(Qt.AlignLeft)
        self.layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)

        twoListsOfSets = QWidget()
        twoListsOfSets.setLayout(QHBoxLayout())
        twoListsOfSets.layout().setContentsMargins(5, 10, 5, 5)
        twoListsOfSets.layout().setSpacing(0)

        effect = QGraphicsDropShadowEffect()
        effect.setBlurRadius(10)
        effect.setColor(QColor(0, 0, 0, 160))
        effect.setOffset(0.0)

        self.requestList = QListView()
        self.requestList.setSpacing(3)
        self.requestList.setAutoFillBackground(True)
        self.requestList.setGraphicsEffect(effect)
        self.requestList.setFrameStyle(QFrame.NoFrame)
        self.requestList.viewport().setAutoFillBackground(False)
        self.requestList.setFlow(QListView.LeftToRight)
        self.requestList.setWrapping(True)
        self.requestList.setResizeMode(QListView.Adjust)
        self.requestList.setUniformItemSizes(True)
        self.requestsModel = QStandardItemModel()
        self.requestList.setModel(self.requestsModel)

        effect = QGraphicsDropShadowEffect()
        effect.setBlurRadius(10)
        effect.setColor(QColor(0, 0, 0, 160))
        effect.setOffset(0.0)

        self.requestList2 = QListView()
        self.requestList2.setSpacing(3)
        self.requestList2.setAutoFillBackground(True)
        self.requestList2.setGraphicsEffect(effect)
        self.requestList2.setFrameStyle(QFrame.NoFrame)
        self.requestList2.viewport().setAutoFillBackground(False)
        self.requestList2.setFlow(QListView.LeftToRight)
        self.requestList2.setWrapping(True)
        self.requestList2.setResizeMode(QListView.Adjust)
        self.requestList2.setUniformItemSizes(True)
        self.requestsModel2 = QStandardItemModel()
        self.requestList2.setModel(self.requestsModel2)

        twoListsOfSets.layout().addWidget(self.requestList)
        twoListsOfSets.layout().addWidget(self.requestList2)

        self.layout.addRow("SETS", twoListsOfSets)
        self.layout.addRow(HorizontalLine(self))

        self.operationSelection = QGroupBox()
        self.operationSelection.setFlat(True)
        self.operationSelection.setLayout(QVBoxLayout())

        self.buttonIntersection = QRadioButton("Intersection")
        self.operationSelection.layout().addWidget(self.buttonIntersection)
        self.buttonIntersection.clicked.connect(
            self.__disableSecondRequestList)
        self.buttonIntersection.click()

        self.buttonUnion = QRadioButton("Union")
        self.operationSelection.layout().addWidget(self.buttonUnion)
        self.buttonUnion.clicked.connect(self.__disableSecondRequestList)

        self.buttonDiff = QRadioButton("Difference")
        self.operationSelection.layout().addWidget(self.buttonDiff)
        self.buttonDiff.clicked.connect(self.__enableSecondRequestList)

        self.layout.addRow("OPERATION", self.operationSelection)

        self.buttonApplyWidget = QWidget()
        self.buttonApplyWidget.setSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Preferred)
        self.buttonApplyLayout = QHBoxLayout()
        self.buttonApplyLayout.setContentsMargins(0, 0, 0, 0)
        self.buttonApplyWidget.setLayout(self.buttonApplyLayout)
        self.buttonApply = QPushButton("Apply")
        self.buttonApply.clicked.connect(self.__applyOp)
        self.operationSelection.layout().addWidget(self.buttonApply)
        self.buttonApplyLayout.addWidget(self.buttonApply,
                                         alignment=Qt.AlignRight)
        self.layout.addRow("", self.buttonApplyWidget)
        self.layout.addRow(HorizontalLine(self))

        self.layout.addRow("RESULTS", None)

        self.resultingSets = QTableView()
        self.resultingSets.horizontalHeader().setSectionResizeMode(
            QHeaderView.Stretch)
        self.resultingSets.verticalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)
        self.resultingSets.setModel(OperationsTableModel())
        self.layout.addRow(self.resultingSets)
        self.layout.addRow(HorizontalLine(self))

        self.outputSetSelection = QComboBox()
        self.outputSetSelection.setSizePolicy(QSizePolicy.MinimumExpanding,
                                              QSizePolicy.Fixed)
        self.layout.addRow("OUTPUT SET", self.outputSetSelection)

        self.setLayout(self.layout)

    def outputSet(self):
        return self.outputSetSelection.currentText()

    def setOutputSet(self, outputSetName):
        self.outputSetSelection.setCurrentText(outputSetName)

    @property
    def ops(self):
        return copy.deepcopy(self.__ops)

    def __applyOp(self):
        includedSets = [
            self.requestsModel.item(i).text()
            for i in range(self.requestsModel.rowCount())
            if self.requestsModel.item(i).data(Qt.CheckStateRole) == QVariant(
                Qt.Checked)
        ]

        if self.buttonUnion.isChecked():
            if len(includedSets) > 1:
                opName = SetNameManagement.getUniqueSetName()
                self.addOp(OverpassUnion(opName), includedSets)
                logging.info("Union created.")
            else:
                logging.error("The union must have at least two sets.")
        elif self.buttonIntersection.isChecked():
            if len(includedSets) > 1:
                opName = SetNameManagement.getUniqueSetName()
                self.addOp(OverpassIntersection(opName), includedSets)
                logging.info("Intersection created.")
            else:
                logging.error("The intersection must have at least two sets.")
        elif self.buttonDiff.isChecked():
            excludedSets = [
                self.requestsModel2.item(i).text()
                for i in range(self.requestsModel2.rowCount())
                if self.requestsModel2.item(i).data(Qt.CheckStateRole) ==
                QVariant(Qt.Checked)
            ]

            if len(includedSets) == 1 and len(excludedSets) > 0:
                opName = SetNameManagement.getUniqueSetName()
                self.addOp(OverpassDiff(includedSets[0], opName), excludedSets)
                logging.info("Difference created.")
            else:
                logging.error(
                    "The difference must have only one set selected in the first list and at least one in the other."
                )
        logging.debug("LINE")

    def addOp(self, op, sets=None):
        SetNameManagement.assign(op.name)
        self.__ops.append(op)
        if sets is not None:
            op.addSets(sets)
        self.resultingSets.model().addOp(op.name, op)
        self.addRequest(op.name)
        self.cleanRequestList()

    def __enableSecondRequestList(self):
        self.requestList2.show()

    def __disableSecondRequestList(self):
        self.requestList2.hide()

    def addRequest(self, name):
        self.requestsModel.beginInsertRows(QModelIndex(),
                                           self.requestsModel.rowCount(),
                                           self.requestsModel.rowCount())
        item = QStandardItem(name)
        item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        item.setData(QVariant(Qt.Unchecked), Qt.CheckStateRole)
        self.requestsModel.appendRow(item)
        self.requestsModel.endInsertRows()

        self.requestsModel2.beginInsertRows(QModelIndex(),
                                            self.requestsModel2.rowCount(),
                                            self.requestsModel2.rowCount())
        item = QStandardItem(name)
        item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
        item.setData(QVariant(Qt.Unchecked), Qt.CheckStateRole)
        self.requestsModel2.appendRow(item)
        self.requestsModel2.endInsertRows()

        self.outputSetSelection.addItem(name)

    def removeSetAndDependencies(self, setName):
        removeList = [setName]

        for set in removeList:
            logging.info("Removing set '{}'.".format(set))
            removeList.extend(
                [i for i in self.__removeSet(set) if i not in removeList])
        logging.debug("LINE")

    def __removeSet(self, setName):
        dependencies = []
        for op in self.__ops:
            op.removeSet(setName)
            if not op.isValid():
                dependencies.append(op.name)

        for i in range(self.requestsModel.rowCount()):
            if self.requestsModel.item(i).text() == setName:
                self.requestsModel.beginRemoveRows(QModelIndex(), i, i)
                self.requestsModel.removeRow(i)
                self.requestsModel.endInsertRows()

                self.requestsModel2.beginRemoveRows(QModelIndex(), i, i)
                self.requestsModel2.removeRow(i)
                self.requestsModel2.endInsertRows()

                self.outputSetSelection.removeItem(i)
                break

        for op in self.__ops:
            if op.name == setName:
                self.resultingSets.model().removeOp(setName)
                self.__ops.remove(op)
                break

        SetNameManagement.releaseName(setName)

        return dependencies

    def reset(self):
        while len(self.ops) > 0:
            self.removeSetAndDependencies(self.ops[0].name)

    def cleanRequestList(self):
        for i in range(self.requestsModel.rowCount()):
            self.requestsModel.item(i).setData(QVariant(Qt.Unchecked),
                                               Qt.CheckStateRole)
            self.requestsModel2.item(i).setData(QVariant(Qt.Unchecked),
                                                Qt.CheckStateRole)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Backspace and self.resultingSets.hasFocus():

            advice = "Are you sure?\nAll sets containing this one will be deleted if they are no longer valid"
            reply = QMessageBox.question(self, "Remove request operation",
                                         advice)

            if reply == QMessageBox.Yes:
                select = self.resultingSets.selectionModel()
                while len(select.selectedRows()) > 0:
                    self.removeSetAndDependencies(
                        self.resultingSets.model().getOpByIndex(
                            select.selectedRows()[0].row()))

        event.accept()
class DisambiguationWidget(QWidget):
    def __init__(self, getRequestFunction, setFiltersFunction, parent=None):
        super().__init__(parent)

        self.getRequestFunction = getRequestFunction
        self.setFiltersFunction = setFiltersFunction

        # LAYOUT

        self.layout = QFormLayout()
        self.layout.setContentsMargins(10, 10, 10, 10)

        self.layout.setFieldGrowthPolicy(QFormLayout.ExpandingFieldsGrow)
        self.layout.setLabelAlignment(Qt.AlignLeft)
        self.layout.setFormAlignment(Qt.AlignLeft | Qt.AlignTop)

        # TYPE

        self.onlyDisconnectedCB = QCheckBox()
        self.onlyDisconnectedCB.setText("Only disconnected ways")

        self.columnSelection = QListView()
        self.columnSelection.setSpacing(3)
        self.columnSelection.setAutoFillBackground(True)
        self.columnSelection.setFrameStyle(QFrame.NoFrame)
        self.columnSelection.viewport().setAutoFillBackground(False)
        self.columnSelection.setFlow(QListView.LeftToRight)
        self.columnSelection.setWrapping(True)
        self.columnSelection.setResizeMode(QListView.Adjust)
        self.columnSelectionModel = QStandardItemModel()
        self.columnSelection.setModel(self.columnSelectionModel)

        self.applyButton = QPushButton("Apply")
        self.applyButton.clicked.connect(self.showTable)

        self.tableView = QTableView()
        self.tableView.doubleClicked.connect(
            lambda signal: self.setFiltersFunction(
                self.setSelection.currentText(),
                self.tableView.model().getDictDataFromCell(signal)))

        horizontalHeader = self.tableView.horizontalHeader()
        horizontalHeader.setSectionResizeMode(QHeaderView.ResizeToContents)
        horizontalHeader.setStretchLastSection(True)

        verticalHeader = self.tableView.verticalHeader()
        verticalHeader.sectionDoubleClicked.connect(
            lambda i: self.setFiltersFunction(
                self.setSelection.currentText(),
                self.tableView.model().getDictData(i)))

        self.tableView.setMinimumHeight(300)

        self.tableButtons = QWidget()
        tableButtonsLayout = QHBoxLayout()
        tableButtonsLayout.setAlignment(Qt.AlignRight)
        self.tableButtons.setLayout(tableButtonsLayout)
        tableButtonsLayout.setSpacing(0)
        tableButtonsLayout.setContentsMargins(0, 0, 0, 0)

        buttonMore = IconButton(
            QIcon(os.path.join(picturesDir, "showMore.png")),
            self.tableButtons.windowHandle(), self.tableButtons.height())
        buttonMore.setToolTip("Show more")
        buttonMore.setFlat(True)
        buttonMore.clicked.connect(self.showMore)

        tableButtonsLayout.addWidget(buttonMore)

        buttonLess = IconButton(
            QIcon(os.path.join(picturesDir, "showLess.png")),
            self.tableButtons.windowHandle(), self.tableButtons.height())
        buttonLess.setToolTip("Show less")
        buttonLess.setFlat(True)
        buttonLess.clicked.connect(self.showLess)

        tableButtonsLayout.addWidget(buttonLess)

        self.setSelection = QComboBox()
        self.setSelection.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Maximum)

        self.layout.addRow("SET", self.setSelection)
        self.layout.addRow("TYPE", self.onlyDisconnectedCB)
        self.layout.addRow("KEYS", self.columnSelection)
        self.layout.addRow(self.applyButton)
        self.layout.addRow(self.tableView)
        self.layout.addRow(self.tableButtons)

        self.setLayout(self.layout)

    def showMore(self):
        if self.tableView.model() is not None:
            self.tableView.model().showMore()

    def showLess(self):
        if self.tableView.model() is not None:
            self.tableView.model().showLess()

    def addFilterFromCell(self, signal):
        key = self.tableView.model().headerData(signal.column(), Qt.Horizontal,
                                                Qt.DisplayRole)
        value = self.tableView.model().itemData(signal).get(0)
        return self.setFiltersFunction(
            self.setSelection.currentText(),
            ([OverpassFilter(key, TagComparison.EQUAL, value, False, True)
              ], []))

    def showTable(self):
        request = self.getRequestFunction(self.setSelection.currentText())
        if request is not None:
            query = OverpassQuery(request.name)
            query.addRequest(request)

            try:
                writeXMLResponse(query.getQL(), tableDir)
            except OverpassRequestException as e:
                logging.error(str(e))
                return
            except OSError:
                logging.error(
                    "There was a problem creating the file with the request response."
                )
                return
            except RuntimeError as e:
                logging.error(str(e))
                return
            except Exception:
                logging.error(traceback.format_exc())
                return

            jsonResponse = ox.overpass_json_from_file(tableDir)

            if len(jsonResponse["elements"]) == 0:
                logging.warning("There are no elements to show in the table.")
                logging.debug("LINE")
            else:
                self.disconnectedWaysTable = DisconnectedWaysTable(
                    jsonResponse)
                self.similarWaysTable = SimilarWaysTable(jsonResponse)
                self.showHideOnlyDisconnected()

                self.columnSelectionModel.clear()
                for key in self.similarWaysTable.getAllColumns():
                    self.columnSelectionModel.beginInsertRows(
                        QModelIndex(), self.columnSelectionModel.rowCount(),
                        self.columnSelectionModel.rowCount())
                    item = QStandardItem(key)
                    self.columnSelectionModel.itemChanged.connect(
                        self.updateColumns)
                    item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
                    item.setData(
                        QVariant(Qt.Checked if key in self.similarWaysTable.
                                 getSelectedColumns() else Qt.Unchecked),
                        Qt.CheckStateRole)
                    self.columnSelectionModel.appendRow(item)
                    self.columnSelectionModel.endInsertRows()
                self.disconnectedWaysTable.updateColumns(
                    self.similarWaysTable.getSelectedColumns())

                self.onlyDisconnectedCB.stateChanged.connect(
                    self.showHideOnlyDisconnected)

                logging.info("Showing table.")
                logging.debug("LINE")
        else:
            logging.warning(
                "There is no requests. It is not possible to show the table.")

    def updateColumns(self):
        self.disconnectedWaysTable.updateColumns(self.getSelectedKeys())
        self.similarWaysTable.updateColumns(self.getSelectedKeys())

    def getSelectedKeys(self):
        return [
            self.columnSelectionModel.item(i).text()
            for i in range(self.columnSelectionModel.rowCount())
            if self.columnSelectionModel.item(i).data(Qt.CheckStateRole) ==
            QVariant(Qt.Checked)
        ]

    def getSelectedRowNetworkx(self):
        if self.tableView.model() is None:
            logging.warning("The table has not been created yet.")
            logging.debug("LINE")
        else:
            indexes = self.tableView.selectionModel().selectedRows()
            return self.tableView.model().getRowJson(indexes)

    def getHtmlFromSelectedRow(self):
        selectedRows = self.getSelectedRowNetworkx()
        if selectedRows:
            return buildHTMLWithNetworkx(selectedRows)
        else:
            raise RuntimeError("No row is selected")

    def showHideOnlyDisconnected(self):
        if self.onlyDisconnectedCB.isChecked():
            self.tableView.setModel(self.disconnectedWaysTable)
        else:
            self.tableView.setModel(self.similarWaysTable)

    def showTableSelection(self):
        try:
            self.changePage(
                buildHTMLWithNetworkx(self.getSelectedRowNetworkx()))
        except (OverpassRequestException, OsmnxException) as e:
            logging.error(str(e))
            logging.warning(
                "Before open NETEDIT you must run a query with the row filters applied."
            )
        except ox.EmptyOverpassResponse:
            logging.error("There are no elements with the given row.")
        except OSError:
            logging.error(
                "There was a problem creating the file with the row selection."
            )
        except Exception:
            logging.error(traceback.format_exc())
        logging.debug("LINE")

    def addSet(self, setName):
        self.applyButton.setEnabled(True)
        self.setSelection.addItem(setName)

    def removeSet(self, setName):
        for i in range(self.setSelection.count()):
            if self.setSelection.itemText(i) == setName:
                self.setSelection.removeItem(i)
                break