text: /* Copyright 2013-2020 Homegear GmbH
   *
   * Homegear is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as
   * published by the Free Software Foundation, either version 3 of the
   * License, or (at your option) any later version.
   *
   * Homegear 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 Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with Homegear.  If not, see
   * <http://www.gnu.org/licenses/>.
   *
   * In addition, as a special exception, the copyright holders give
   * permission to link the code of portions of this program with the
   * OpenSSL library under certain conditions as described in each
   * individual source file, and distribute linked combinations
   * including the two.
   * You must obey the GNU Lesser General Public License in all respects
   * for all of the code used other than OpenSSL.  If you modify
   * file(s) with this exception, you may extend this exception to your
   * version of the file(s), but you are not obligated to do so.  If you
   * do not wish to do so, delete this exception statement from your
   * version.  If you delete this exception statement from all source
   * files in the program, then also delete it here.
  */
  
  #include "UiController.h"
  #include "../GD/GD.h"
  
  namespace Homegear {
  
  UiController::UiController() {
    _rpcDecoder = std::unique_ptr<BaseLib::Rpc::RpcDecoder>(new BaseLib::Rpc::RpcDecoder(GD::bl.get(), false, false));
    _descriptions = std::unique_ptr<BaseLib::DeviceDescription::UiElements>(new BaseLib::DeviceDescription::UiElements(GD::bl.get()));
  }
  
  UiController::~UiController() = default;
  
  void UiController::load() {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      _uiElements.clear();
      _uiElementsByRoom.clear();
      _uiElementsByCategory.clear();
      auto rows = GD::bl->db->getUiElements();
      for (auto &row : *rows) {
        auto uiElement = std::make_shared<UiElement>();
        uiElement->databaseId = (uint64_t)row.second.at(0)->intValue;
        uiElement->elementId = row.second.at(1)->textValue;
        uiElement->data = _rpcDecoder->decodeResponse(*row.second.at(2)->binaryValue);
        uiElement->metadata = row.second.at(3)->binaryValue->empty() ? std::make_shared<BaseLib::Variable>() : _rpcDecoder->decodeResponse(*row.second.at(3)->binaryValue);
  
        if (uiElement->databaseId == 0 || uiElement->elementId.empty()) continue;
  
        addDataInfo(uiElement, uiElement->data);
  
        _uiElements.emplace(uiElement->databaseId, uiElement);
        _uiElementsByRoom[uiElement->roomId].emplace(uiElement);
        for (auto categoryId : uiElement->categoryIds) {
          _uiElementsByCategory[categoryId].emplace(uiElement);
        }
      }
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
  }
  
  BaseLib::PVariable UiController::addUiElement(BaseLib::PRpcClientInfo clientInfo, const std::string &elementId, const BaseLib::PVariable &data, const BaseLib::PVariable &metadata) {
    try {
      if (elementId.empty()) return BaseLib::Variable::createError(-1, "elementId is empty.");
      if (data->type != BaseLib::VariableType::tStruct) return BaseLib::Variable::createError(-1, "data is not of type Struct.");
  
      BaseLib::PVariable dataCopy = data;
      BaseLib::PVariable dynamicMetadata = metadata;
      if (!dynamicMetadata || dynamicMetadata->structValue->empty()) {
        auto metadataIterator = dataCopy->structValue->find("dynamicMetadata");
        if (metadataIterator != dataCopy->structValue->end()) {
          dynamicMetadata = metadataIterator->second;
          dataCopy->structValue->erase("dynamicMetadata");
        }
      }
  
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      auto databaseId = GD::bl->db->addUiElement(elementId, dataCopy, dynamicMetadata);
  
      if (databaseId == 0) return BaseLib::Variable::createError(-1, "Error adding element to database.");
  
      auto uiElement = std::make_shared<UiElement>();
      uiElement->databaseId = databaseId;
      uiElement->elementId = elementId;
      uiElement->data = dataCopy;
      uiElement->metadata = dynamicMetadata;
  
      addDataInfo(uiElement, dataCopy);
  
      _uiElements.emplace(uiElement->databaseId, uiElement);
      _uiElementsByRoom[uiElement->roomId].emplace(uiElement);
      for (auto categoryId : uiElement->categoryIds) {
        _uiElementsByCategory[categoryId].emplace(uiElement);
      }
  
      requestUiRefresh(clientInfo, "");
  
      return std::make_shared<BaseLib::Variable>(databaseId);
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::findRoleVariables(const BaseLib::PRpcClientInfo &clientInfo, const BaseLib::PVariable &uiInfo, const BaseLib::PVariable &variable, uint64_t roomId, BaseLib::PVariable &inputPeers, BaseLib::PVariable &outputPeers) {
    try {
      std::list<std::list<uint64_t>> roleIdsIn;
      std::list<std::list<std::pair<uint64_t, BaseLib::PVariable>>> roleIdsOut;
  
      { //Get required role Ids
        auto roleIdsInIterator = uiInfo->structValue->find("roleIdsIn");
        auto roleIdsOutIterator = uiInfo->structValue->find("roleIdsOut");
  
        if (roleIdsInIterator != uiInfo->structValue->end()) {
          for (auto &roleIdOuter : *roleIdsInIterator->second->arrayValue) {
            std::list<uint64_t> roleIds;
            for (auto &roleId : *roleIdOuter->arrayValue) {
              roleIds.push_back(roleId->integerValue64);
            }
            roleIdsIn.emplace_back(std::move(roleIds));
          }
        }
  
        if (roleIdsOutIterator != uiInfo->structValue->end()) {
          for (auto &roleIdOuter : *roleIdsOutIterator->second->arrayValue) {
            std::list<std::pair<uint64_t, BaseLib::PVariable>> roleIds;
            for (auto &roleId : *roleIdOuter->arrayValue) {
              if (roleId->type == BaseLib::VariableType::tStruct) {
                auto roleIdIterator = roleId->structValue->find("id");
                auto valueIterator = roleId->structValue->find("value");
                if (roleIdIterator != roleId->structValue->end()) {
                  auto value = (valueIterator == roleId->structValue->end() ? BaseLib::PVariable() : valueIterator->second);
                  roleIds.emplace_back(roleIdIterator->second->integerValue64, value);
                }
              } else roleIds.emplace_back(roleId->integerValue64, BaseLib::PVariable());
            }
            roleIdsOut.emplace_back(std::move(roleIds));
          }
        }
      }
  
      { //Get nearest variables with the required roles
        inputPeers->arrayValue->reserve(roleIdsIn.size());
        outputPeers->arrayValue->reserve(roleIdsOut.size());
  
        for (auto &roleIdOuter : roleIdsIn) {
          auto outerArray = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
          outerArray->arrayValue->reserve(roleIdOuter.size());
          for (auto roleId : roleIdOuter) {
            BaseLib::PVariable requestParameters(new BaseLib::Variable(BaseLib::VariableType::tArray));
            requestParameters->arrayValue->reserve(2);
            requestParameters->arrayValue->push_back(std::make_shared<BaseLib::Variable>(roleId));
            requestParameters->arrayValue->push_back(variable->arrayValue->at(0));
            std::string methodName = "getVariablesInRole";
            auto variables = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, requestParameters);
            if (variables->errorStruct) return BaseLib::Variable::createError(-1, "Error getting variables for roles required by UI element.");
  
            size_t roleCount = 0;
  
            auto channelIterator = variables->structValue->find(std::to_string(variable->arrayValue->at(1)->integerValue64));
            if (channelIterator != variables->structValue->end() && !channelIterator->second->structValue->empty()) {
              auto variableIterator = channelIterator->second->structValue->find(variable->arrayValue->at(2)->stringValue);
              if (variableIterator != channelIterator->second->structValue->end()) {
                bool wrongDirection = false;
                auto directionIterator = variableIterator->second->structValue->find("direction");
                if (directionIterator != variableIterator->second->structValue->end()) {
                  if (directionIterator->second->integerValue64 == 1) {
                    if (GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Required role (" + std::to_string(roleId) + ") " + variableIterator->first + " does not have required direction (input). Simple UI element creation is not possible.");
                    wrongDirection = true;
                  }
                }
  
                if (!wrongDirection) {
                  roleCount++;
  
                  //Role in variable
                  auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                  roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                  roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator->first)));
                  roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator->first));
                  outerArray->arrayValue->emplace_back(std::move(roleVariable));
                }
              }
  
              if (roleCount == 0) {
                BaseLib::PVariable autoRoleVariable;
  
                //Variable not found, look for alternative in channel
                for (auto &variableIterator2 : *channelIterator->second->structValue) {
                  bool wrongDirection = false;
                  auto directionIterator = variableIterator2.second->structValue->find("direction");
                  if (directionIterator != variableIterator2.second->structValue->end()) {
                    if (directionIterator->second->integerValue64 == 1) {
                      if (GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Required role (" + std::to_string(roleId) + ") " + variableIterator2.first + " does not have required direction (input). Simple UI element creation is not possible.");
                      wrongDirection = true;
                    }
                  }
  
                  if (!wrongDirection) {
                    if (variableIterator2.first.find(".RV.") != std::string::npos) {
                      autoRoleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                      autoRoleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                      autoRoleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator->first)));
                      autoRoleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator2.first));
                    } else {
                      roleCount++;
  
                      //Role in channel
                      auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                      roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                      roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator->first)));
                      roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator2.first));
                      outerArray->arrayValue->emplace_back(std::move(roleVariable));
                    }
                  }
                }
  
                if (roleCount == 0 && autoRoleVariable) {
                  if (GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Using automatically generated variable to fill role " + std::to_string(roleId));
                  outerArray->arrayValue->emplace_back(std::move(autoRoleVariable));
                }
              }
  
              if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role (" + std::to_string(roleId) + ") exists multiple times in channel. Simple UI element creation is not possible.");
            }
  
            if (roleCount == 0) {
              //Role not in channel
              for (auto &channelIterator2 : *variables->structValue) {
                for (auto &variableIterator : *channelIterator2.second->structValue) {
                  bool wrongDirection = false;
                  auto directionIterator = variableIterator.second->structValue->find("direction");
                  if (directionIterator != variableIterator.second->structValue->end()) {
                    if (directionIterator->second->integerValue64 == 1) {
                      if (GD::bl->debugLevel >= 5)
                        GD::out.printDebug(
                            "Debug: Required role (" + std::to_string(roleId) + ") " + variableIterator.first + " does not have required direction (input) in channel " + channelIterator2.first + ". Simple UI element creation is not possible.");
                      wrongDirection = true;
                    }
                  }
  
                  if (!wrongDirection) {
                    roleCount++;
  
                    auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                    roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                    roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator2.first)));
                    roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator.first));
                    outerArray->arrayValue->emplace_back(std::move(roleVariable));
                  }
                }
              }
  
              if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role exists multiple times in device. Simple UI element creation is not possible.");
  
              if (roleCount == 0) {
                //Role not in device, try to find role in room
                requestParameters->arrayValue->resize(1);
                requestParameters->arrayValue->at(0) = std::make_shared<BaseLib::Variable>(roomId);
                std::string methodName2 = "getRolesInRoom";
                auto variables2 = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName2, requestParameters);
                if (variables2->errorStruct) return BaseLib::Variable::createError(-1, "Error getting roles in room.");
  
                for (auto &deviceIterator : *variables2->structValue) {
                  for (auto &channelIterator2 : *deviceIterator.second->structValue) {
                    for (auto &variableIterator : *channelIterator2.second->structValue) {
                      bool wrongDirection = false;
                      auto directionIterator = variableIterator.second->structValue->find("direction");
                      if (directionIterator != variableIterator.second->structValue->end()) {
                        if (directionIterator->second->integerValue64 == 1) {
                          if (GD::bl->debugLevel >= 5)
                            GD::out.printDebug("Debug: Required role (" + std::to_string(roleId) + ") " + variableIterator.first + " does not have required direction (input) in channel " + channelIterator2.first + " of device " + deviceIterator.first
                                                   + ". Simple UI element creation is not possible.");
                          wrongDirection = true;
                        }
                      }
  
                      if (!wrongDirection) {
                        roleCount++;
  
                        auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                        roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                        roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator2.first)));
                        roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator.first));
                        outerArray->arrayValue->emplace_back(std::move(roleVariable));
                      }
                    }
                  }
                }
  
                if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role exists multiple times in room. Simple UI element creation is not possible.");
              }
  
              if (roleCount == 0) {
                return BaseLib::Variable::createError(-1, "Required role \"" + std::to_string(roleId) + "\" not found in device or room. Simple UI element creation is not possible.");
              }
            }
          }
          inputPeers->arrayValue->emplace_back(std::move(outerArray));
        }
  
        for (const auto &roleIdOuter : roleIdsOut) {
          auto outerArray = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
          outerArray->arrayValue->reserve(roleIdOuter.size());
          for (auto roleId : roleIdOuter) {
            BaseLib::PVariable requestParameters(new BaseLib::Variable(BaseLib::VariableType::tArray));
            requestParameters->arrayValue->reserve(2);
            requestParameters->arrayValue->push_back(std::make_shared<BaseLib::Variable>(roleId.first));
            requestParameters->arrayValue->push_back(variable->arrayValue->at(0));
            std::string methodName = "getVariablesInRole";
            auto variables = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, requestParameters);
            if (variables->errorStruct) return BaseLib::Variable::createError(-1, "Error getting variables for roles required by UI element.");
  
            size_t roleCount = 0;
  
            auto channelIterator = variables->structValue->find(std::to_string(variable->arrayValue->at(1)->integerValue64));
            if (channelIterator != variables->structValue->end() && !channelIterator->second->structValue->empty()) {
              auto variableIterator = channelIterator->second->structValue->find(variable->arrayValue->at(2)->stringValue);
              if (variableIterator != channelIterator->second->structValue->end()) {
                bool wrongDirection = false;
                auto directionIterator = variableIterator->second->structValue->find("direction");
                if (directionIterator != variableIterator->second->structValue->end()) {
                  if (directionIterator->second->integerValue64 == 0) {
                    if (GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Required role (" + std::to_string(roleId.first) + ") " + variableIterator->first + " does not have required direction (output). Simple UI element creation is not possible.");
                    wrongDirection = true;
                  }
                }
  
                if (!wrongDirection) {
                  roleCount++;
  
                  //Role in channel
                  auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                  roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                  roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator->first)));
                  roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator->first));
                  if (roleId.second) roleVariable->structValue->emplace("value", roleId.second);
                  outerArray->arrayValue->emplace_back(std::move(roleVariable));
                }
              }
  
              if (roleCount == 0) {
                for (auto &variableIterator2 : *channelIterator->second->structValue) {
                  bool wrongDirection = false;
                  auto directionIterator = variableIterator2.second->structValue->find("direction");
                  if (directionIterator != variableIterator2.second->structValue->end()) {
                    if (directionIterator->second->integerValue64 == 0) {
                      if (GD::bl->debugLevel >= 5) GD::out.printDebug("Debug: Required role (" + std::to_string(roleId.first) + ") " + variableIterator2.first + " does not have required direction (output). Simple UI element creation is not possible.");
                      wrongDirection = true;
                    }
                  }
  
                  if (!wrongDirection) {
                    roleCount++;
  
                    //Role in channel
                    auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                    roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                    roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator->first)));
                    roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator2.first));
                    if (roleId.second) roleVariable->structValue->emplace("value", roleId.second);
                    outerArray->arrayValue->emplace_back(std::move(roleVariable));
                  }
                }
              }
  
              if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role (" + std::to_string(roleId.first) + ") exists multiple times in channel. Simple UI element creation is not possible.");
            }
  
            if (roleCount == 0) {
              //Role not in channel
              for (auto &channelIterator2 : *variables->structValue) {
                for (auto &variableIterator : *channelIterator2.second->structValue) {
                  bool wrongDirection = false;
                  auto directionIterator = variableIterator.second->structValue->find("direction");
                  if (directionIterator != variableIterator.second->structValue->end()) {
                    if (directionIterator->second->integerValue64 == 0) {
                      if (GD::bl->debugLevel >= 5)
                        GD::out.printDebug(
                            "Debug: Required role (" + std::to_string(roleId.first) + ") " + variableIterator.first + " does not have required direction (output) in channel " + channelIterator2.first + ". Simple UI element creation is not possible.");
                      wrongDirection = true;
                    }
                  }
  
                  if (!wrongDirection) {
                    roleCount++;
  
                    auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                    roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                    roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator2.first)));
                    roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator.first));
                    if (roleId.second) roleVariable->structValue->emplace("value", roleId.second);
                    outerArray->arrayValue->emplace_back(std::move(roleVariable));
                  }
                }
              }
  
              if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role exists multiple times in device. Simple UI element creation is not possible.");
  
              if (roleCount == 0) {
                //Role not in device, try to find role in room
                requestParameters->arrayValue->resize(1);
                requestParameters->arrayValue->at(0) = std::make_shared<BaseLib::Variable>(roomId);
                std::string methodName2 = "getRolesInRoom";
                auto variables2 = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName2, requestParameters);
                if (variables2->errorStruct) return BaseLib::Variable::createError(-1, "Error getting roles in room.");
  
                for (auto &deviceIterator : *variables2->structValue) {
                  for (auto &channelIterator2 : *deviceIterator.second->structValue) {
                    for (auto &variableIterator : *channelIterator2.second->structValue) {
                      bool wrongDirection = false;
                      auto directionIterator = variableIterator.second->structValue->find("direction");
                      if (directionIterator != variableIterator.second->structValue->end()) {
                        if (directionIterator->second->integerValue64 == 0) {
                          if (GD::bl->debugLevel >= 5)
                            GD::out.printDebug(
                                "Debug: Required role (" + std::to_string(roleId.first) + ") " + variableIterator.first + " does not have required direction (output) in channel " + channelIterator2.first + " of device " + deviceIterator.first
                                    + ". Simple UI element creation is not possible.");
                          wrongDirection = true;
                        }
                      }
  
                      if (!wrongDirection) {
                        roleCount++;
  
                        auto roleVariable = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
                        roleVariable->structValue->emplace("peer", variable->arrayValue->at(0));
                        roleVariable->structValue->emplace("channel", std::make_shared<BaseLib::Variable>(BaseLib::Math::getNumber(channelIterator2.first)));
                        roleVariable->structValue->emplace("name", std::make_shared<BaseLib::Variable>(variableIterator.first));
                        if (roleId.second) roleVariable->structValue->emplace("value", roleId.second);
                        outerArray->arrayValue->emplace_back(std::move(roleVariable));
                      }
                    }
                  }
                }
  
                if (roleCount > 1) return BaseLib::Variable::createError(-1, "Required role exists multiple times in room. Simple UI element creation is not possible.");
              }
  
              if (roleCount == 0) {
                return BaseLib::Variable::createError(-1, "Required role \"" + std::to_string(roleId.first) + "\" not found in device or room. Simple UI element creation is not possible.");
              }
            }
          }
          outputPeers->arrayValue->emplace_back(std::move(outerArray));
        }
      }
  
      return std::make_shared<BaseLib::Variable>();
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::addUiElementSimple(const BaseLib::PRpcClientInfo &clientInfo, const std::string &label, const BaseLib::PVariable &variable, bool dryRun) {
    try {
      BaseLib::PVariable dryrunStruct;
      if (dryRun) {
        dryrunStruct = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
        dryrunStruct->structValue->emplace("visualizable", std::make_shared<BaseLib::Variable>(false));
      }
  
      if (variable->arrayValue->size() < 3) {
        if (dryRun) {
          dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("No variables were passed."));
          return dryrunStruct;
        } else return BaseLib::Variable::createError(-1, "No variables were passed.");
      }
  
      std::set<uint64_t> roleIds;
      uint64_t roomId = 0;
  
      { //Get role ID and room ID
        BaseLib::PVariable requestParameters(new BaseLib::Variable(BaseLib::VariableType::tArray));
        requestParameters->arrayValue->push_back(variable->arrayValue->at(0));
        requestParameters->arrayValue->push_back(variable->arrayValue->at(1));
        requestParameters->arrayValue->push_back(variable->arrayValue->at(2));
        BaseLib::PVariable fields(new BaseLib::Variable(BaseLib::VariableType::tArray));
        fields->arrayValue->reserve(2);
        fields->arrayValue->push_back(std::make_shared<BaseLib::Variable>("ROOM"));
        fields->arrayValue->push_back(std::make_shared<BaseLib::Variable>("ROLES"));
        requestParameters->arrayValue->push_back(fields);
        std::string methodName = "getVariableDescription";
        auto variableDescription = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, requestParameters);
        if (variableDescription->errorStruct) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Error getting variable description. Are you authorized to access the variable?"));
            return dryrunStruct;
          } else return BaseLib::Variable::createError(-1, "Error getting variable description. Are you authorized to access the variable?");
        }
        auto rolesIterator = variableDescription->structValue->find("ROLES");
        if (rolesIterator == variableDescription->structValue->end() || rolesIterator->second->arrayValue->empty()) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Variable has no roles."));
            return dryrunStruct;
          } else return BaseLib::Variable::createError(-1, "Variable has no roles.");
        }
        for (auto &role : *rolesIterator->second->arrayValue) {
          auto idIterator = role->structValue->find("id");
          auto levelIterator = role->structValue->find("level");
          if (levelIterator != role->structValue->end() && levelIterator->second->integerValue64 != 2) continue;
          if (idIterator != role->structValue->end() && idIterator->second->integerValue64 != 0) roleIds.emplace(idIterator->second->integerValue64);
        }
        auto roomIterator = variableDescription->structValue->find("ROOM");
        if (roomIterator != variableDescription->structValue->end() && roomIterator->second->integerValue64 > 0) {
          roomId = roomIterator->second->integerValue64;
        } else if (variable->arrayValue->at(0)->integerValue64 != 0) //Device variable?
        {
          fields->arrayValue->resize(1);
          requestParameters->arrayValue->at(2) = fields;
          requestParameters->arrayValue->resize(3);
          methodName = "getDeviceDescription";
          auto deviceDescription = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, requestParameters);
          if (deviceDescription->errorStruct) {
            if (dryRun) {
              dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Error getting device description. Are you authorized to access the device?"));
              return dryrunStruct;
            } else return BaseLib::Variable::createError(-1, "Error getting device description. Are you authorized to access the device?");
          }
          roomIterator = deviceDescription->structValue->find("ROOM");
          if (roomIterator != deviceDescription->structValue->end() && roomIterator->second->integerValue64 > 0) {
            roomId = roomIterator->second->integerValue64;
          } else {
            requestParameters->arrayValue->at(1) = std::make_shared<BaseLib::Variable>(-1);
            deviceDescription = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, requestParameters);
            if (deviceDescription->errorStruct) {
              if (dryRun) {
                dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Error getting device description. Are you authorized to access the device?"));
                return dryrunStruct;
              } else return BaseLib::Variable::createError(-1, "Error getting device description. Are you authorized to access the device?");
            }
            roomIterator = deviceDescription->structValue->find("ROOM");
            if (roomIterator != deviceDescription->structValue->end()) {
              roomId = roomIterator->second->integerValue64;
            }
          }
        }
      }
  
      if (roomId == 0) {
        if (dryRun) {
          dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Variable, channel and device have no room assigned."));
          return dryrunStruct;
        } else return BaseLib::Variable::createError(-1, "Variable, channel and device have no room assigned.");
      }
  
      BaseLib::PVariable roleMetadata;
      BaseLib::PVariable uiInfo;
      uint64_t uiRole = 0;
      for (auto roleId : roleIds) {
        roleMetadata = GD::bl->db->getRoleMetadata(roleId);
        auto uiRefIterator = roleMetadata->structValue->find("uiRef");
        if (uiRefIterator != roleMetadata->structValue->end() && uiRefIterator->second->integerValue64 != 0) {
          roleId = uiRefIterator->second->integerValue64;
          roleMetadata = GD::bl->db->getRoleMetadata(roleId);
        }
        auto uiIterator = roleMetadata->structValue->find("ui");
        if (uiIterator == roleMetadata->structValue->end()) continue;
        auto simpleCreationIterator = uiIterator->second->structValue->find("simpleCreationInfo");
        if (simpleCreationIterator == uiIterator->second->structValue->end()) continue;
        if (uiRole == 0) uiRole = roleId;
        if (!uiInfo) uiInfo = simpleCreationIterator->second;
        else if (uiRole != roleId) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Variable has more than one role with UI definition."));
            return dryrunStruct;
          } else return BaseLib::Variable::createError(-1, "Variable has more than one role with UI definition. UI element creation is not possible.");
        }
      }
      if (!uiInfo) {
        if (dryRun) {
          dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Role has no UI definition."));
          return dryrunStruct;
        } else return BaseLib::Variable::createError(-1, "Role has no UI definition.");
      }
  
      auto inputPeers = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
      auto outputPeers = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
  
      if (uiInfo->type == BaseLib::VariableType::tArray) {
        bool found = false;
        for (auto &definition : *uiInfo->arrayValue) {
          auto result = findRoleVariables(clientInfo, definition, variable, roomId, inputPeers, outputPeers);
          if (!result->errorStruct) {
            found = true;
            uiInfo = definition;
            break;
          } else GD::out.printInfo("Info: No match for UI definition. Reason: " + result->structValue->at("faultString")->stringValue);
        }
        if (!found) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Could not find a matching variable set."));
            return dryrunStruct;
          } else return BaseLib::Variable::createError(-1, "Could not find a matching variable set.");
        }
      } else if (uiInfo->type == BaseLib::VariableType::tStruct) {
        auto result = findRoleVariables(clientInfo, uiInfo, variable, roomId, inputPeers, outputPeers);
        if (result->errorStruct) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>(result->structValue->at("faultString")->stringValue));
            return dryrunStruct;
          } else return result;
        }
      } else {
        if (dryRun) {
          dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("UI definition has unknown type."));
          return dryrunStruct;
        } else return BaseLib::Variable::createError(-1, "UI definition has unknown type.");
      }
  
      std::string elementId;
  
      { //Get element ID
        auto elementIdIterator = uiInfo->structValue->find("element");
        if (elementIdIterator == uiInfo->structValue->end() || elementIdIterator->second->stringValue.empty()) {
          if (dryRun) {
            dryrunStruct->structValue->emplace("reason", std::make_shared<BaseLib::Variable>("Role is missing key \"element\" containing the UI element ID."));
            return dryrunStruct;
          } else return BaseLib::Variable::createError(-1, "Role is missing key \"element\" containing the UI element ID.");
        }
        elementId = elementIdIterator->second->stringValue;
      }
  
      BaseLib::PVariable metadata;
      if (!dryRun) { //Get metadata
        auto metadataIterator = uiInfo->structValue->find("metadata");
        if (metadataIterator != uiInfo->structValue->end() && metadataIterator->second->type == BaseLib::VariableType::tStruct) {
          metadata = metadataIterator->second;
        } else metadata = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
  
        auto addUiElementData = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct);
        { //Create data struct
          addUiElementData->structValue->emplace("inputPeers", inputPeers);
          addUiElementData->structValue->emplace("outputPeers", outputPeers);
          addUiElementData->structValue->emplace("label", std::make_shared<BaseLib::Variable>(label));
          addUiElementData->structValue->emplace("room", std::make_shared<BaseLib::Variable>(roomId));
          addUiElementData->structValue->emplace("metadata", metadata);
        }
  
        return addUiElement(clientInfo, elementId, addUiElementData, std::make_shared<BaseLib::Variable>());
      } else {
        dryrunStruct->structValue->at("visualizable")->booleanValue = true;
        auto uiElementsWithVariable = getUiElementsWithVariable(variable->arrayValue->at(0)->integerValue64, variable->arrayValue->at(1)->integerValue, variable->arrayValue->at(2)->stringValue);
        dryrunStruct->structValue->emplace("visualized", std::make_shared<BaseLib::Variable>(!uiElementsWithVariable.empty()));
        if (!uiElementsWithVariable.empty()) {
          auto uiElementsWithVariableArray = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
          uiElementsWithVariableArray->arrayValue->reserve(uiElementsWithVariable.size());
          for (auto &element : uiElementsWithVariable) {
            uiElementsWithVariableArray->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>(element));
          }
          dryrunStruct->structValue->emplace("uiElements", uiElementsWithVariableArray);
        }
        return dryrunStruct;
      }
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  void UiController::addDataInfo(UiController::PUiElement &uiElement, const BaseLib::PVariable &data) {
    try {
      uiElement->peerInfo->inputPeers.clear();
      uiElement->peerInfo->outputPeers.clear();
      uiElement->roomId = 0;
      uiElement->categoryIds.clear();
  
      auto dataIterator = data->structValue->find("label");
      if (dataIterator != data->structValue->end()) uiElement->label = dataIterator->second->stringValue;
  
      dataIterator = data->structValue->find("inputPeers");
      if (dataIterator != data->structValue->end()) {
        uiElement->peerInfo->inputPeers.reserve(dataIterator->second->arrayValue->size());
        for (auto &outerArray : *dataIterator->second->arrayValue) {
          std::vector<BaseLib::DeviceDescription::UiElements::PUiVariableInfo> peers;
          peers.reserve(outerArray->arrayValue->size());
          for (auto &peerElement : *outerArray->arrayValue) {
            auto variableInfo = std::make_shared<BaseLib::DeviceDescription::UiElements::UiVariableInfo>();
  
            if (peerElement->type == BaseLib::VariableType::tStruct) {
              auto peerIdIterator = peerElement->structValue->find("peer");
              if (peerIdIterator == peerElement->structValue->end()) continue;
  
              variableInfo->peerId = (uint64_t)peerIdIterator->second->integerValue64;
  
              auto channelIterator = peerElement->structValue->find("channel");
              if (channelIterator != peerElement->structValue->end()) variableInfo->channel = channelIterator->second->integerValue;
  
              auto nameIterator = peerElement->structValue->find("name");
              if (nameIterator != peerElement->structValue->end()) variableInfo->name = nameIterator->second->stringValue;
  
              auto minimumValueIterator = peerElement->structValue->find("minimum");
              if (minimumValueIterator != peerElement->structValue->end()) variableInfo->minimumValue = minimumValueIterator->second;
  
              auto maximumValueIterator = peerElement->structValue->find("maximum");
              if (maximumValueIterator != peerElement->structValue->end()) variableInfo->maximumValue = maximumValueIterator->second;
  
              auto minimumValueScaledIterator = peerElement->structValue->find("minimumScaled");
              if (minimumValueScaledIterator != peerElement->structValue->end()) variableInfo->minimumValueScaled = minimumValueScaledIterator->second;
  
              auto maximumValueScaledIterator = peerElement->structValue->find("maximumScaled");
              if (maximumValueScaledIterator != peerElement->structValue->end()) variableInfo->maximumValueScaled = maximumValueScaledIterator->second;
            } else if (peerElement->type == BaseLib::VariableType::tInteger || peerElement->type == BaseLib::VariableType::tInteger64) {
              variableInfo->peerId = (uint64_t)peerElement->integerValue64;
            } else continue;
  
            peers.push_back(variableInfo);
          }
          uiElement->peerInfo->inputPeers.emplace_back(std::move(peers));
        }
      }
  
      dataIterator = data->structValue->find("outputPeers");
      if (dataIterator != data->structValue->end()) {
        uiElement->peerInfo->outputPeers.reserve(dataIterator->second->arrayValue->size());
        for (auto &outerArray : *dataIterator->second->arrayValue) {
          std::vector<BaseLib::DeviceDescription::UiElements::PUiVariableInfo> peers;
          peers.reserve(outerArray->arrayValue->size());
          for (auto &peerElement : *outerArray->arrayValue) {
            auto variableInfo = std::make_shared<BaseLib::DeviceDescription::UiElements::UiVariableInfo>();
  
            if (peerElement->type == BaseLib::VariableType::tStruct) {
              auto peerIdIterator = peerElement->structValue->find("peer");
              if (peerIdIterator == peerElement->structValue->end()) continue;
  
              variableInfo->peerId = (uint64_t)peerIdIterator->second->integerValue64;
  
              auto channelIterator = peerElement->structValue->find("channel");
              if (channelIterator != peerElement->structValue->end()) variableInfo->channel = channelIterator->second->integerValue;
  
              auto nameIterator = peerElement->structValue->find("name");
              if (nameIterator != peerElement->structValue->end()) variableInfo->name = nameIterator->second->stringValue;
  
              auto valueIterator = peerElement->structValue->find("value");
              if (valueIterator != peerElement->structValue->end()) variableInfo->value = valueIterator->second;
  
              auto minimumValueIterator = peerElement->structValue->find("minimum");
              if (minimumValueIterator != peerElement->structValue->end()) variableInfo->minimumValue = minimumValueIterator->second;
  
              auto maximumValueIterator = peerElement->structValue->find("maximum");
              if (maximumValueIterator != peerElement->structValue->end()) variableInfo->maximumValue = maximumValueIterator->second;
            } else if (peerElement->type == BaseLib::VariableType::tInteger || peerElement->type == BaseLib::VariableType::tInteger64) {
              variableInfo->peerId = (uint64_t)peerElement->integerValue64;
            } else continue;
  
            peers.push_back(variableInfo);
          }
          uiElement->peerInfo->outputPeers.emplace_back(std::move(peers));
        }
      }
  
      dataIterator = data->structValue->find("room");
      if (dataIterator != data->structValue->end()) uiElement->roomId = (uint64_t)dataIterator->second->integerValue64;
  
      dataIterator = data->structValue->find("categories");
      if (dataIterator != data->structValue->end()) {
        for (auto &categoryElement : *dataIterator->second->arrayValue) {
          uiElement->categoryIds.emplace((uint64_t)categoryElement->integerValue64);
        }
      }
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
  }
  
  void UiController::addVariableInfo(const BaseLib::PRpcClientInfo &clientInfo, const PUiElement &uiElement, BaseLib::PArray &variables, bool addValue) {
    try {
      for (auto &variable : *variables) {
        auto peerIdIterator = variable->structValue->find("peer");
        if (peerIdIterator == variable->structValue->end()) continue;
        auto channelIterator = variable->structValue->find("channel");
        if (channelIterator == variable->structValue->end()) continue;
        auto nameIterator = variable->structValue->find("name");
        if (nameIterator == variable->structValue->end()) continue;
  
        std::string methodName = "getValue";
        auto parameters = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
        parameters->arrayValue->reserve(4);
        parameters->arrayValue->emplace_back(peerIdIterator->second);
        parameters->arrayValue->emplace_back(channelIterator->second);
        parameters->arrayValue->emplace_back(nameIterator->second);
  
        if (addValue) {
          auto value = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, parameters);
          if (!value->errorStruct) variable->structValue->emplace("value", value);
        }
  
        auto fields = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
        fields->arrayValue->reserve(5);
        fields->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>("TYPE"));
        fields->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>("MIN"));
        fields->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>("MAX"));
        fields->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>("ROLES"));
        parameters->arrayValue->emplace_back(fields);
  
        methodName = "getVariableDescription";
        auto description = GD::rpcServers.begin()->second->callMethod(clientInfo, methodName, parameters);
        if (description->errorStruct) {
          GD::out.printWarning("Warning: Could not get variable description for UI element " + uiElement->elementId + " with ID " + std::to_string(uiElement->databaseId) + ": " + description->structValue->at("faultString")->stringValue
                                   + ". Please set the log level to 4 to see the failed RPC call.");
          continue;
        }
  
        auto typeIterator = description->structValue->find("TYPE");
        if (typeIterator != description->structValue->end()) variable->structValue->emplace("type", std::make_shared<BaseLib::Variable>(BaseLib::HelperFunctions::toLower(typeIterator->second->stringValue)));
  
        auto minimumValueIterator = variable->structValue->find("minimumValue");
        auto maximumValueIterator = variable->structValue->find("maximumValue");
        if (minimumValueIterator == variable->structValue->end() || maximumValueIterator == variable->structValue->end()) {
          if (minimumValueIterator == variable->structValue->end()) {
            auto minimumValueIterator2 = description->structValue->find("MIN");
            if (minimumValueIterator2 != description->structValue->end()) {
              variable->structValue->emplace("minimumValue", minimumValueIterator2->second);
            }
          }
  
          if (maximumValueIterator == variable->structValue->end()) {
            auto maximumValueIterator2 = description->structValue->find("MAX");
            if (maximumValueIterator2 != description->structValue->end()) {
              variable->structValue->emplace("maximumValue", maximumValueIterator2->second);
            }
          }
        }
  
        auto rolesIterator = description->structValue->find("ROLES");
        if (rolesIterator != description->structValue->end()) {
          variable->structValue->emplace("roles", rolesIterator->second);
        }
      }
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
  }
  
  BaseLib::PVariable UiController::getAllUiElements(const BaseLib::PRpcClientInfo &clientInfo, const std::string &language) {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
  
      bool checkAcls = clientInfo->acls->variablesRoomsCategoriesRolesDevicesReadSet();
  
      auto uiElements = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
      uiElements->arrayValue->reserve(_uiElements.size());
  
      auto clickCountsData = User::getData(clientInfo->user, "ui.clickCounts", "");
      std::multimap<uint64_t, PUiElement> uiElementsByClickCount;
      std::unordered_set<uint64_t> addedElementIds;
      for (auto &uiElement : *clickCountsData->structValue) {
        auto uiElementId = BaseLib::Math::getUnsignedNumber64(uiElement.first);
        auto uiElementsIterator = _uiElements.find(uiElementId);
        if (uiElementsIterator == _uiElements.end()) {
          User::deleteData(clientInfo->user, "ui.clickCounts", uiElement.first);
          continue;
        }
        uiElementsByClickCount.emplace(uiElement.second->integerValue64, uiElementsIterator->second);
        addedElementIds.emplace(uiElementId);
      }
  
      for (auto &uiElement : _uiElements) {
        if (addedElementIds.find(uiElement.first) != addedElementIds.end()) continue;
  
        uiElementsByClickCount.emplace(0, uiElement.second);
      }
  
      for (auto uiElementIterator = uiElementsByClickCount.rbegin(); uiElementIterator != uiElementsByClickCount.rend(); uiElementIterator++) {
        auto &uiElement = uiElementIterator->second;
        auto languageIterator = uiElement->rpcElement.find(language);
        if (languageIterator == uiElement->rpcElement.end()) {
          auto rpcElement = _descriptions->getUiElement(language, uiElement->elementId, uiElement->peerInfo);
          if (!rpcElement) continue;
          uiElement->rpcElement.emplace(language, rpcElement);
          languageIterator = uiElement->rpcElement.find(language);
          if (languageIterator == uiElement->rpcElement.end()) continue;
        }
  
        if (checkAcls) {
          if (!clientInfo->acls->checkRoomReadAccess(uiElement->roomId)) continue;
          for (auto categoryId : uiElement->categoryIds) {
            if (!clientInfo->acls->checkCategoryReadAccess(categoryId)) continue;
          }
          if (!checkElementAccess(clientInfo, uiElement, languageIterator->second)) continue;
        }
  
        auto elementInfo = languageIterator->second->getElementInfo();
        elementInfo->structValue->emplace("databaseId", std::make_shared<BaseLib::Variable>(uiElement->databaseId));
        elementInfo->structValue->emplace("clickCount", std::make_shared<BaseLib::Variable>(uiElementIterator->first));
        elementInfo->structValue->emplace("label", std::make_shared<BaseLib::Variable>(uiElement->label));
        elementInfo->structValue->emplace("room", std::make_shared<BaseLib::Variable>(uiElement->roomId));
        auto categories = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
        categories->arrayValue->reserve(uiElement->categoryIds.size());
        for (auto categoryId : uiElement->categoryIds) {
          categories->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>(categoryId));
        }
        elementInfo->structValue->emplace("categories", categories);
  
        //{{{ value
        //Simple
        auto variableInputsIterator = elementInfo->structValue->find("variableInputs");
        if (variableInputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableInputsIterator = control->structValue->find("variableInputs");
              if (variableInputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
            }
          }
        }
  
        //Simple
        auto variableOutputsIterator = elementInfo->structValue->find("variableOutputs");
        if (variableOutputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableOutputsIterator = control->structValue->find("variableOutputs");
              if (variableOutputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
            }
          }
        }
        //}}}
  
        auto dataIterator = uiElement->data->structValue->find("metadata");
        if (dataIterator != uiElement->data->structValue->end()) {
          auto metadataIterator = elementInfo->structValue->find("metadata");
          if (metadataIterator == elementInfo->structValue->end()) metadataIterator = elementInfo->structValue->emplace("metadata", std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct)).first;
  
          for (auto &metadataElement : *dataIterator->second->structValue) {
            metadataIterator->second->structValue->emplace(metadataElement);
          }
        }
  
        elementInfo->structValue->emplace("dynamicMetadata", uiElement->metadata);
  
        uiElements->arrayValue->emplace_back(elementInfo);
      }
  
      return uiElements;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::getAvailableUiElements(const BaseLib::PRpcClientInfo &clientInfo, const std::string &language) {
    try {
      return _descriptions->getUiElements(language);
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::getUiElement(const BaseLib::PRpcClientInfo &clientInfo, uint64_t databaseId, const std::string &language) {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      auto elementIterator = _uiElements.find(databaseId);
      if (elementIterator == _uiElements.end()) return BaseLib::Variable::createError(-2, "Unknown element.");
  
      bool checkAcls = clientInfo->acls->variablesRoomsCategoriesRolesDevicesReadSet();
  
      auto &uiElement = elementIterator->second;
  
      auto languageIterator = uiElement->rpcElement.find(language);
      if (languageIterator == uiElement->rpcElement.end()) {
        auto rpcElement = _descriptions->getUiElement(language, uiElement->elementId, uiElement->peerInfo);
        if (!rpcElement) return BaseLib::Variable::createError(-32500, "Unknown application error.");
        uiElement->rpcElement.emplace(language, rpcElement);
        languageIterator = uiElement->rpcElement.find(language);
        if (languageIterator == uiElement->rpcElement.end()) return BaseLib::Variable::createError(-32500, "Unknown application error.");
      }
  
      if (checkAcls) {
        if (!clientInfo->acls->checkRoomReadAccess(uiElement->roomId)) return BaseLib::Variable::createError(-32603, "Unauthorized.");
        for (auto categoryId : uiElement->categoryIds) {
          if (!clientInfo->acls->checkCategoryReadAccess(categoryId)) return BaseLib::Variable::createError(-32603, "Unauthorized.");
        }
        if (!checkElementAccess(clientInfo, uiElement, languageIterator->second)) return BaseLib::Variable::createError(-32603, "Unauthorized.");
      }
  
      auto elementInfo = languageIterator->second->getElementInfo();
      elementInfo->structValue->emplace("databaseId", std::make_shared<BaseLib::Variable>(uiElement->databaseId));
      elementInfo->structValue->emplace("label", std::make_shared<BaseLib::Variable>(uiElement->label));
      elementInfo->structValue->emplace("room", std::make_shared<BaseLib::Variable>(uiElement->roomId));
      auto categories = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
      categories->arrayValue->reserve(uiElement->categoryIds.size());
      for (auto categoryId : uiElement->categoryIds) {
        categories->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>(categoryId));
      }
      elementInfo->structValue->emplace("categories", categories);
  
      //{{{ value
      //Simple
      auto variableInputsIterator = elementInfo->structValue->find("variableInputs");
      if (variableInputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
      else //Complex
      {
        auto controlsIterator = elementInfo->structValue->find("controls");
        if (controlsIterator != elementInfo->structValue->end()) {
          for (auto &control : *controlsIterator->second->arrayValue) {
            variableInputsIterator = control->structValue->find("variableInputs");
            if (variableInputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
          }
        }
      }
  
      //Simple
      auto variableOutputsIterator = elementInfo->structValue->find("variableOutputs");
      if (variableOutputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
      else //Complex
      {
        auto controlsIterator = elementInfo->structValue->find("controls");
        if (controlsIterator != elementInfo->structValue->end()) {
          for (auto &control : *controlsIterator->second->arrayValue) {
            variableOutputsIterator = control->structValue->find("variableOutputs");
            if (variableOutputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
          }
        }
      }
      //}}}
  
      auto dataIterator = uiElement->data->structValue->find("metadata");
      if (dataIterator != uiElement->data->structValue->end()) {
        auto metadataIterator = elementInfo->structValue->find("metadata");
        if (metadataIterator == elementInfo->structValue->end()) metadataIterator = elementInfo->structValue->emplace("metadata", std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tStruct)).first;
  
        for (auto &metadataElement : *dataIterator->second->structValue) {
          metadataIterator->second->structValue->emplace(metadataElement);
        }
      }
  
      elementInfo->structValue->emplace("dynamicMetadata", uiElement->metadata);
  
      return elementInfo;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::getUiElementMetadata(const BaseLib::PRpcClientInfo &clientInfo, uint64_t databaseId) {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      auto elementIterator = _uiElements.find(databaseId);
      if (elementIterator == _uiElements.end()) return BaseLib::Variable::createError(-2, "Unknown element.");
  
      return elementIterator->second->metadata;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::getUiElementsInRoom(const BaseLib::PRpcClientInfo &clientInfo, uint64_t roomId, const std::string &language) {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
  
      if (clientInfo->acls->roomsReadSet() && !clientInfo->acls->checkRoomReadAccess(roomId)) return BaseLib::Variable::createError(-32603, "Unauthorized.");
      bool checkAcls = clientInfo->acls->variablesRoomsCategoriesRolesDevicesReadSet();
  
      auto roomIterator = _uiElementsByRoom.find(roomId);
      if (roomIterator == _uiElementsByRoom.end()) return BaseLib::Variable::createError(-1, "Unknown room.");
  
      auto uiElements = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
      uiElements->arrayValue->reserve(roomIterator->second.size());
  
      for (auto &uiElement : roomIterator->second) {
        auto languageIterator = uiElement->rpcElement.find(language);
        if (languageIterator == uiElement->rpcElement.end()) {
          auto rpcElement = _descriptions->getUiElement(language, uiElement->elementId, uiElement->peerInfo);
          if (!rpcElement) continue;
          uiElement->rpcElement.emplace(language, rpcElement);
          languageIterator = uiElement->rpcElement.find(language);
          if (languageIterator == uiElement->rpcElement.end()) continue;
        }
  
        if (checkAcls && !checkElementAccess(clientInfo, uiElement, languageIterator->second)) continue;
  
        auto elementInfo = languageIterator->second->getElementInfo();
        elementInfo->structValue->emplace("databaseId", std::make_shared<BaseLib::Variable>(uiElement->databaseId));
        elementInfo->structValue->emplace("label", std::make_shared<BaseLib::Variable>(uiElement->label));
        elementInfo->structValue->emplace("room", std::make_shared<BaseLib::Variable>(uiElement->roomId));
        auto categories = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
        categories->arrayValue->reserve(uiElement->categoryIds.size());
        for (auto categoryId : uiElement->categoryIds) {
          categories->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>(categoryId));
        }
        elementInfo->structValue->emplace("categories", categories);
  
        //{{{ value
        //Simple
        auto variableInputsIterator = elementInfo->structValue->find("variableInputs");
        if (variableInputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableInputsIterator = control->structValue->find("variableInputs");
              if (variableInputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
            }
          }
        }
  
        //Simple
        auto variableOutputsIterator = elementInfo->structValue->find("variableOutputs");
        if (variableOutputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableOutputsIterator = control->structValue->find("variableOutputs");
              if (variableOutputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
            }
          }
        }
        //}}}
  
        uiElements->arrayValue->emplace_back(elementInfo);
      }
  
      return uiElements;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::getUiElementsInCategory(const BaseLib::PRpcClientInfo &clientInfo, uint64_t categoryId, const std::string &language) {
    try {
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
  
      if (clientInfo->acls->categoriesReadSet() && !clientInfo->acls->checkCategoryReadAccess(categoryId)) return BaseLib::Variable::createError(-32603, "Unauthorized.");
      bool checkAcls = clientInfo->acls->variablesRoomsCategoriesRolesDevicesReadSet();
  
      auto categoryIterator = _uiElementsByCategory.find(categoryId);
      if (categoryIterator == _uiElementsByCategory.end()) return BaseLib::Variable::createError(-1, "Unknown category.");
  
      auto uiElements = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
      uiElements->arrayValue->reserve(categoryIterator->second.size());
  
      for (auto &uiElement : categoryIterator->second) {
        auto languageIterator = uiElement->rpcElement.find(language);
        if (languageIterator == uiElement->rpcElement.end()) {
          auto rpcElement = _descriptions->getUiElement(language, uiElement->elementId, uiElement->peerInfo);
          if (!rpcElement) continue;
          uiElement->rpcElement.emplace(language, rpcElement);
          languageIterator = uiElement->rpcElement.find(language);
          if (languageIterator == uiElement->rpcElement.end()) continue;
        }
  
        if (checkAcls && !checkElementAccess(clientInfo, uiElement, languageIterator->second)) continue;
  
        auto elementInfo = languageIterator->second->getElementInfo();
        elementInfo->structValue->emplace("databaseId", std::make_shared<BaseLib::Variable>(uiElement->databaseId));
        elementInfo->structValue->emplace("label", std::make_shared<BaseLib::Variable>(uiElement->label));
        elementInfo->structValue->emplace("room", std::make_shared<BaseLib::Variable>(uiElement->roomId));
        auto categories = std::make_shared<BaseLib::Variable>(BaseLib::VariableType::tArray);
        categories->arrayValue->reserve(uiElement->categoryIds.size());
        for (auto categoryId : uiElement->categoryIds) {
          categories->arrayValue->emplace_back(std::make_shared<BaseLib::Variable>(categoryId));
        }
        elementInfo->structValue->emplace("categories", categories);
  
        //{{{ value
        //Simple
        auto variableInputsIterator = elementInfo->structValue->find("variableInputs");
        if (variableInputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableInputsIterator = control->structValue->find("variableInputs");
              if (variableInputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableInputsIterator->second->arrayValue, true);
            }
          }
        }
  
        //Simple
        auto variableOutputsIterator = elementInfo->structValue->find("variableOutputs");
        if (variableOutputsIterator != elementInfo->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
        else //Complex
        {
          auto controlsIterator = elementInfo->structValue->find("controls");
          if (controlsIterator != elementInfo->structValue->end()) {
            for (auto &control : *controlsIterator->second->arrayValue) {
              variableOutputsIterator = control->structValue->find("variableOutputs");
              if (variableOutputsIterator != control->structValue->end()) addVariableInfo(clientInfo, uiElement, variableOutputsIterator->second->arrayValue, false);
            }
          }
        }
        //}}}
  
        uiElements->arrayValue->emplace_back(elementInfo);
      }
  
      return uiElements;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  bool UiController::checkElementAccess(const BaseLib::PRpcClientInfo &clientInfo, const UiController::PUiElement &uiElement, const BaseLib::DeviceDescription::PHomegearUiElement &rpcElement) {
    try {
      auto families = GD::familyController->getFamilies();
  
      //variableInputs and variableOutputs are filled by UiElements::getUiElement
  
      for (auto &variableInput : rpcElement->variableInputs) {
        if (variableInput->peerId == 0) {
          auto systemVariable = GD::systemVariableController->getInternal(variableInput->name);
          if (!systemVariable) return false;
          clientInfo->acls->checkSystemVariableReadAccess(systemVariable);
        } else {
          for (auto &family : families) {
            auto central = family.second->getCentral();
            if (!central) continue;
  
            auto peer = central->getPeer((uint64_t)variableInput->peerId);
            if (!peer) continue;
  
            if (!clientInfo->acls->checkDeviceReadAccess(peer)) return false;
            if (!clientInfo->acls->checkVariableReadAccess(peer, variableInput->channel, variableInput->name)) return false;
          }
        }
      }
  
      for (auto &variableOutput : rpcElement->variableOutputs) {
        if (variableOutput->peerId == 0) {
          auto systemVariable = GD::systemVariableController->getInternal(variableOutput->name);
          if (!systemVariable) return false;
          clientInfo->acls->checkSystemVariableReadAccess(systemVariable);
        } else {
          for (auto &family : families) {
            auto central = family.second->getCentral();
            if (!central) continue;
  
            auto peer = central->getPeer((uint64_t)variableOutput->peerId);
            if (!peer) continue;
  
            if (!clientInfo->acls->checkDeviceWriteAccess(peer)) return false;
            if (!clientInfo->acls->checkVariableWriteAccess(peer, variableOutput->channel, variableOutput->name)) return false;
          }
        }
      }
  
      return true;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return false;
  }
  
  BaseLib::PVariable UiController::requestUiRefresh(const BaseLib::PRpcClientInfo &clientInfo, const std::string &id) {
    try {
      GD::rpcClient->broadcastRequestUiRefresh(id);
  
      return std::make_shared<BaseLib::Variable>();
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::removeUiElement(const BaseLib::PRpcClientInfo &clientInfo, uint64_t databaseId) {
    try {
      if (databaseId == 0) return BaseLib::Variable::createError(-1, "databaseId is invalid.");
  
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      auto elementIterator = _uiElements.find(databaseId);
      if (elementIterator == _uiElements.end()) return BaseLib::Variable::createError(-2, "Unknown element.");
  
      GD::bl->db->removeUiElement(databaseId);
  
      auto roomIterator = _uiElementsByRoom.find(elementIterator->second->roomId);
      if (roomIterator != _uiElementsByRoom.end()) {
        for (auto &roomUiElement : roomIterator->second) {
          if (roomUiElement.get() == elementIterator->second.get()) {
            roomIterator->second.erase(roomUiElement);
            break;
          }
        }
        if (roomIterator->second.empty()) _uiElementsByRoom.erase(roomIterator);
      }
  
      for (auto categoryId : elementIterator->second->categoryIds) {
        auto categoryIterator = _uiElementsByCategory.find(categoryId);
        if (categoryIterator != _uiElementsByCategory.end()) {
          for (auto &categoryUiElement : categoryIterator->second) {
            if (categoryUiElement.get() == elementIterator->second.get()) {
              categoryIterator->second.erase(categoryUiElement);
              break;
            }
          }
          if (categoryIterator->second.empty()) _uiElementsByCategory.erase(categoryIterator);
        }
      }
  
      _uiElements.erase(elementIterator);
  
      requestUiRefresh(clientInfo, "");
  
      return std::make_shared<BaseLib::Variable>();
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  BaseLib::PVariable UiController::setUiElementMetadata(const BaseLib::PRpcClientInfo &clientInfo, uint64_t databaseId, const BaseLib::PVariable &metadata) {
    try {
      {
        std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
        auto elementIterator = _uiElements.find(databaseId);
        if (elementIterator == _uiElements.end()) return BaseLib::Variable::createError(-2, "Unknown element.");
  
        elementIterator->second->metadata = metadata;
      }
  
      GD::bl->db->setUiElementMetadata(databaseId, metadata);
      return std::make_shared<BaseLib::Variable>();
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return BaseLib::Variable::createError(-32500, "Unknown application error.");
  }
  
  std::unordered_set<uint64_t> UiController::getUiElementsWithVariable(uint64_t peerId, int32_t channel, const std::string &variableName) {
    try {
      std::unordered_set<uint64_t> result;
  
      std::lock_guard<std::mutex> uiElementsGuard(_uiElementsMutex);
      for (auto &uiElement : _uiElements) {
        bool found = false;
        for (auto &inputPeers : uiElement.second->peerInfo->inputPeers) {
          for (auto &inputPeer : inputPeers) {
            if (inputPeer->peerId == peerId && inputPeer->channel == channel && inputPeer->name == variableName) {
              result.emplace(uiElement.first);
              found = true;
              break;
            }
          }
        }
        if (found) continue;
  
        for (auto &outputPeers : uiElement.second->peerInfo->outputPeers) {
          for (auto &outputPeer : outputPeers) {
            if (outputPeer->peerId == peerId && outputPeer->channel == channel && outputPeer->name == variableName) {
              result.emplace(uiElement.first);
              break;
            }
          }
        }
      }
  
      return result;
    }
    catch (const std::exception &ex) {
      GD::out.printEx(__FILE__, __LINE__, __PRETTY_FUNCTION__, ex.what());
    }
    return std::unordered_set<uint64_t>();
  }
  
  }
  