content: package com.aizuda.snailjob.server.web.service.impl;
  
  import cn.hutool.core.collection.CollUtil;
  import cn.hutool.core.date.LocalDateTimeUtil;
  import cn.hutool.core.lang.Assert;
  import cn.hutool.core.util.ObjUtil;
  import cn.hutool.core.util.StrUtil;
  import com.aizuda.snailjob.common.core.constant.SystemConstants;
  import com.aizuda.snailjob.common.core.enums.NodeTypeEnum;
  import com.aizuda.snailjob.common.core.enums.ServerStatusEnum;
  import com.aizuda.snailjob.common.core.model.Result;
  import com.aizuda.snailjob.common.core.util.JsonUtil;
  import com.aizuda.snailjob.common.core.util.NetUtil;
  import com.aizuda.snailjob.common.core.util.StreamUtils;
  import com.aizuda.snailjob.common.log.SnailJobLog;
  import com.aizuda.snailjob.server.common.dto.DistributeInstance;
  import com.aizuda.snailjob.server.common.dto.ServerNodeExtAttrs;
  import com.aizuda.snailjob.server.common.dto.UpdateClientInfoDTO;
  import com.aizuda.snailjob.server.common.enums.DashboardLineEnum;
  import com.aizuda.snailjob.server.common.enums.SyetemTaskTypeEnum;
  import com.aizuda.snailjob.server.common.enums.SystemModeEnum;
  import com.aizuda.snailjob.server.common.exception.SnailJobServerException;
  import com.aizuda.snailjob.server.common.register.ServerRegister;
  import com.aizuda.snailjob.server.common.register.UpdateClientRegister;
  import com.aizuda.snailjob.server.web.model.base.PageResult;
  import com.aizuda.snailjob.server.web.model.enums.DateTypeEnum;
  import com.aizuda.snailjob.server.web.model.request.*;
  import com.aizuda.snailjob.server.web.model.response.DashboardCardResponseVO;
  import com.aizuda.snailjob.server.web.model.response.DashboardLineResponseVO;
  import com.aizuda.snailjob.server.web.model.response.DashboardRetryLineResponseVO;
  import com.aizuda.snailjob.server.web.model.response.DashboardRetryLineResponseVO.Task;
  import com.aizuda.snailjob.server.web.model.response.ServerNodeResponseVO;
  import com.aizuda.snailjob.server.web.service.DashboardService;
  import com.aizuda.snailjob.server.web.service.convert.*;
  import com.aizuda.snailjob.server.web.util.UserSessionUtils;
  import com.aizuda.snailjob.template.datasource.persistence.dataobject.ActivePodQuantityResponseDO;
  import com.aizuda.snailjob.template.datasource.persistence.dataobject.DashboardCardResponseDO;
  import com.aizuda.snailjob.template.datasource.persistence.dataobject.DashboardLineResponseDO;
  import com.aizuda.snailjob.template.datasource.persistence.dataobject.DashboardRetryLineResponseDO;
  import com.aizuda.snailjob.template.datasource.persistence.mapper.JobSummaryMapper;
  import com.aizuda.snailjob.template.datasource.persistence.mapper.RetrySummaryMapper;
  import com.aizuda.snailjob.template.datasource.persistence.mapper.ServerNodeMapper;
  import com.aizuda.snailjob.template.datasource.persistence.po.*;
  import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  import com.baomidou.mybatisplus.core.metadata.IPage;
  import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
  import com.google.common.collect.Lists;
  import lombok.RequiredArgsConstructor;
  import lombok.extern.slf4j.Slf4j;
  import org.springframework.boot.autoconfigure.web.ServerProperties;
  import org.springframework.stereotype.Service;
  import org.springframework.web.client.RestTemplate;
  
  import java.time.LocalDate;
  import java.time.LocalDateTime;
  import java.time.LocalTime;
  import java.util.*;
  import java.util.stream.Collectors;
  
  /**
   * @author: opensnail
   * @date : 2022-04-22 20:19
   * @since 1.0.0
   */
  @Service
  @Slf4j
  @RequiredArgsConstructor
  public class DashboardServiceImpl implements DashboardService {
  
      private static final String DASHBOARD_CONSUMER_BUCKET = "/dashboard/consumer/bucket";
  
      private final ServerNodeMapper serverNodeMapper;
      private final RestTemplate restTemplate;
      private final JobSummaryMapper jobSummaryMapper;
      private final RetrySummaryMapper retrySummaryMapper;
      private final ServerProperties serverProperties;
      private final UpdateClientRegister updateClientRegister;
  
      @Override
      public DashboardCardResponseVO taskRetryJob() {
  
          // 查询登录用户权限
          UserSessionVO userSessionVO = UserSessionUtils.currentUserSession();
          String namespaceId = userSessionVO.getNamespaceId();
          List<String> groupNames = userSessionVO.isUser() ? userSessionVO.getGroupNames() : new ArrayList<>();
          DashboardCardResponseVO responseVO = new DashboardCardResponseVO();
  
          // 重试任务
          DashboardCardResponseDO.RetryTask retryTaskDO = retrySummaryMapper.selectRetryTask(
                  new LambdaQueryWrapper<RetrySummary>()
                          .eq(RetrySummary::getNamespaceId, namespaceId)
                          .in(CollUtil.isNotEmpty(groupNames), RetrySummary::getGroupName, groupNames));
          DashboardCardResponseVO.RetryTask retryTaskVO = RetrySummaryResponseVOConverter.INSTANCE.convert(retryTaskDO);
          responseVO.setRetryTask(retryTaskVO);
  
          // 定时任务
          DashboardCardResponseDO.JobTask jobTaskDO = jobSummaryMapper.selectJobTask(
                  new LambdaQueryWrapper<JobSummary>()
                          .eq(JobSummary::getSystemTaskType, SyetemTaskTypeEnum.JOB.getType())
                          .eq(JobSummary::getNamespaceId, namespaceId)
                          .in(CollUtil.isNotEmpty(groupNames), JobSummary::getGroupName, groupNames));
          DashboardCardResponseVO.JobTask jobTaskVO = JobSummaryResponseVOConverter.INSTANCE.convert(jobTaskDO);
          responseVO.setJobTask(jobTaskVO);
  
          // 工作流任务
          DashboardCardResponseDO.JobTask workFlowTaskDO = jobSummaryMapper.selectJobTask(
                  new LambdaQueryWrapper<JobSummary>()
                          .eq(JobSummary::getSystemTaskType, SyetemTaskTypeEnum.WORKFLOW.getType())
                          .eq(JobSummary::getNamespaceId, namespaceId)
                          .in(CollUtil.isNotEmpty(groupNames), JobSummary::getGroupName, groupNames));
          DashboardCardResponseVO.WorkFlowTask workFlowTaskVO = JobSummaryResponseVOConverter.INSTANCE.convertToWorkFlowTask(workFlowTaskDO);
          responseVO.setWorkFlowTask(workFlowTaskVO);
  
          // 重试任务柱状图
          HashMap<LocalDateTime, DashboardCardResponseVO.RetryTaskBar> retryTaskBarMap = new HashMap<>();
          for (int i = 0; i < 7; i++) {
              DashboardCardResponseVO.RetryTaskBar retryTaskBar = new DashboardCardResponseVO.RetryTaskBar().setX(LocalDateTime.of(LocalDate.now(), LocalTime.MIN).plusDays(-i).toLocalDate().toString()).setTaskTotal(0L);
              retryTaskBarMap.put(LocalDateTimeUtil.beginOfDay(LocalDateTime.now().minusDays(i)), retryTaskBar);
          }
          List<DashboardCardResponseDO.RetryTask> retryTaskList = retrySummaryMapper.selectRetryTaskBarList(
                  new LambdaQueryWrapper<RetrySummary>()
                          .eq(RetrySummary::getNamespaceId, namespaceId)
                          .in(CollUtil.isNotEmpty(groupNames), RetrySummary::getGroupName, groupNames)
                          .orderByDesc(RetrySummary::getId));
          Map<LocalDateTime, LongSummaryStatistics> summaryStatisticsMap = retryTaskList.stream()
                  .collect(Collectors.groupingBy(DashboardCardResponseDO.RetryTask::getTriggerAt,
                          Collectors.summarizingLong(i -> i.getMaxCountNum() + i.getRunningNum() + i.getSuspendNum() + i.getFinishNum())));
          for (Map.Entry<LocalDateTime, LongSummaryStatistics> map : summaryStatisticsMap.entrySet()) {
              if (retryTaskBarMap.containsKey(LocalDateTime.of(map.getKey().toLocalDate(), LocalTime.MIN))) {
                  DashboardCardResponseVO.RetryTaskBar retryTaskBar = retryTaskBarMap.get(LocalDateTimeUtil.beginOfDay(map.getKey()));
                  retryTaskBar.setX(map.getKey().toLocalDate().toString()).setTaskTotal(map.getValue().getSum());
              }
          }
          responseVO.setRetryTaskBarList(new ArrayList<>(retryTaskBarMap.values()));
  
          // 在线Pods
          List<ActivePodQuantityResponseDO> activePodQuantityDO = serverNodeMapper.selectActivePodCount(
                  new LambdaQueryWrapper<ServerNode>()
                          .in(ServerNode::getNamespaceId, Lists.newArrayList(userSessionVO.getNamespaceId(), ServerRegister.NAMESPACE_ID))
                          .groupBy(ServerNode::getNodeType));
          Map<Integer, Long> map = StreamUtils.toMap(activePodQuantityDO,
                  ActivePodQuantityResponseDO::getNodeType, ActivePodQuantityResponseDO::getTotal);
          Long clientTotal = map.getOrDefault(NodeTypeEnum.CLIENT.getType(), 0L);
          Long serverTotal = map.getOrDefault(NodeTypeEnum.SERVER.getType(), 0L);
          responseVO.getOnLineService().setServerTotal(serverTotal);
          responseVO.getOnLineService().setClientTotal(clientTotal);
          responseVO.getOnLineService().setTotal(clientTotal + serverTotal);
  
          return responseVO;
      }
  
      @Override
      public DashboardRetryLineResponseVO retryLineList(LineQueryVO queryVO) {
          // 查询登录用户权限
          UserSessionVO userSessionVO = UserSessionUtils.currentUserSession();
          String namespaceId = userSessionVO.getNamespaceId();
          List<String> groupNames = userSessionVO.isUser() ? userSessionVO.getGroupNames() : new ArrayList<>();
          DashboardRetryLineResponseVO responseVO = new DashboardRetryLineResponseVO();
  
          // 重试任务列表
          Page<Object> pager = new Page<>(queryVO.getPage(), queryVO.getSize());
          LambdaQueryWrapper<RetrySceneConfig> wrapper = new LambdaQueryWrapper<RetrySceneConfig>()
                  .eq(RetrySceneConfig::getNamespaceId, namespaceId)
                  .in(CollUtil.isNotEmpty(groupNames), RetrySceneConfig::getGroupName, groupNames);
  
          // 针对 Group By 分页自定义countStatement
          pager.setSearchCount(false);
          pager.setTotal(retrySummaryMapper.selectRetryTaskListCount(wrapper));
  
          IPage<DashboardRetryLineResponseDO.Task> page = retrySummaryMapper.selectRetryTaskList(wrapper, pager);
          List<DashboardRetryLineResponseVO.Task> taskList = JobSummaryResponseVOConverter.INSTANCE.convertList(page.getRecords());
          PageResult<List<Task>> pageResult = new PageResult<>(
                  new PageDTO(pager.getCurrent(), pager.getSize(), pager.getTotal()),
                  taskList);
          responseVO.setTaskList(pageResult);
  
          // 折线图
          DateTypeEnum dateTypeEnum = DateTypeEnum.valueOf(queryVO.getType());
          LocalDateTime startDateTime = dateTypeEnum.getStartTime().apply(
                  ObjUtil.isNotNull(queryVO.getStartDt()) ? queryVO.getStartDt() : LocalDateTime.now());
          LocalDateTime endDateTime = dateTypeEnum.getEndTime().apply(
                  ObjUtil.isNotNull(queryVO.getEndDt()) ? queryVO.getEndDt() : LocalDateTime.now());
          List<DashboardLineResponseDO> dashboardRetryLinkeResponseDOList = retrySummaryMapper.selectRetryLineList(
                  DashboardLineEnum.dateFormat(queryVO.getType()),
                  new LambdaQueryWrapper<RetrySummary>()
                          .in(CollUtil.isNotEmpty(groupNames), RetrySummary::getGroupName, groupNames)
                          .eq(StrUtil.isNotBlank(queryVO.getGroupName()), RetrySummary::getGroupName, queryVO.getGroupName())
                          .eq(RetrySummary::getNamespaceId, namespaceId)
                          .between(RetrySummary::getTriggerAt, startDateTime, endDateTime));
          List<DashboardLineResponseVO> dashboardLineResponseVOList = DispatchQuantityResponseVOConverter.INSTANCE.convertList(dashboardRetryLinkeResponseDOList);
          dateTypeEnum.getConsumer().accept(dashboardLineResponseVOList);
          dashboardLineResponseVOList.sort(Comparator.comparing(DashboardLineResponseVO::getCreateDt));
          responseVO.setDashboardLineResponseDOList(dashboardLineResponseVOList);
  
          // 排行榜
          List<DashboardRetryLineResponseDO.Rank> rankList = retrySummaryMapper.selectDashboardRankList(
                  new LambdaQueryWrapper<RetrySummary>()
                          .in(CollUtil.isNotEmpty(groupNames), RetrySummary::getGroupName, groupNames)
                          .eq(StrUtil.isNotBlank(queryVO.getGroupName()), RetrySummary::getGroupName, groupNames)
                          .eq(RetrySummary::getNamespaceId, namespaceId)
                          .ge(RetrySummary::getTriggerAt, startDateTime)
                          .le(RetrySummary::getTriggerAt, endDateTime)
                          .groupBy(RetrySummary::getNamespaceId, RetrySummary::getGroupName, RetrySummary::getSceneName));
          List<DashboardRetryLineResponseVO.Rank> ranks = SceneQuantityRankResponseVOConverter.INSTANCE.convertList(rankList);
          responseVO.setRankList(ranks);
          return responseVO;
      }
  
      @Override
      public DashboardRetryLineResponseVO jobLineList(JobLineQueryVO queryVO) {
          // 查询登录用户权限
          UserSessionVO userSessionVO = UserSessionUtils.currentUserSession();
          String namespaceId = userSessionVO.getNamespaceId();
          List<String> groupNames = userSessionVO.isUser() ? userSessionVO.getGroupNames() : new ArrayList<>();
          DashboardRetryLineResponseVO responseVO = new DashboardRetryLineResponseVO();
  
          // 重试任务列表
          Page<Object> pager = new Page<>(queryVO.getPage(), queryVO.getSize());
  
          // 任务类型
          Integer systemTaskType = SystemModeEnum.JOB.name().equals(queryVO.getMode()) ? SyetemTaskTypeEnum.JOB.getType() : SyetemTaskTypeEnum.WORKFLOW.getType();
          LambdaQueryWrapper<Job> wrapper = new LambdaQueryWrapper<Job>()
                  .eq(Job::getDeleted, 0)
                  .eq(Job::getNamespaceId, namespaceId)
                  .in(CollUtil.isNotEmpty(groupNames), Job::getGroupName, groupNames);
  
          // 针对 Group By 分页自定义 countStatement
          pager.setSearchCount(false);
          pager.setTotal(SystemModeEnum.JOB.name().equals(queryVO.getMode()) ?
                  jobSummaryMapper.selectJobTaskListCount(wrapper) :
                  jobSummaryMapper.selectWorkflowTaskListCount(wrapper));
  
          IPage<DashboardRetryLineResponseDO.Task> taskIPage = SystemModeEnum.JOB.name().equals(queryVO.getMode()) ?
                  jobSummaryMapper.selectJobTaskList(wrapper, pager) : jobSummaryMapper.selectWorkflowTaskList(wrapper, pager);
          List<DashboardRetryLineResponseVO.Task> taskList = JobSummaryResponseVOConverter.INSTANCE.convertList(taskIPage.getRecords());
          PageResult<List<DashboardRetryLineResponseVO.Task>> pageResult = new PageResult<>(
                  new PageDTO(pager.getCurrent(), pager.getSize(), pager.getTotal()),
                  taskList);
          responseVO.setTaskList(pageResult);
  
          // 折线图
          DateTypeEnum dateTypeEnum = DateTypeEnum.valueOf(queryVO.getType());
          LocalDateTime startDateTime = dateTypeEnum.getStartTime().apply(
                  ObjUtil.isNotNull(queryVO.getStartDt()) ? queryVO.getStartDt() : LocalDateTime.now());
          LocalDateTime endDateTime = dateTypeEnum.getEndTime().apply(
                  ObjUtil.isNotNull(queryVO.getEndDt()) ? queryVO.getEndDt() : LocalDateTime.now());
          List<DashboardLineResponseDO> dashboardLineResponseDOList = jobSummaryMapper.selectJobLineList(
                  DashboardLineEnum.dateFormat(queryVO.getType()),
                  new LambdaQueryWrapper<JobSummary>()
                          .in(CollUtil.isNotEmpty(groupNames), JobSummary::getGroupName, groupNames)
                          .eq(StrUtil.isNotBlank(queryVO.getGroupName()), JobSummary::getGroupName, queryVO.getGroupName())
                          .eq(JobSummary::getSystemTaskType, systemTaskType)
                          .eq(JobSummary::getNamespaceId, namespaceId)
                          .between(JobSummary::getTriggerAt, startDateTime, endDateTime));
          List<DashboardLineResponseVO> dashboardLineResponseVOList = DispatchQuantityResponseVOConverter.INSTANCE.convertList(dashboardLineResponseDOList);
          dateTypeEnum.getConsumer().accept(dashboardLineResponseVOList);
          dashboardLineResponseVOList.sort(Comparator.comparing(DashboardLineResponseVO::getCreateDt));
          responseVO.setDashboardLineResponseDOList(dashboardLineResponseVOList);
  
          // 排行榜
          List<DashboardRetryLineResponseDO.Rank> rankList = jobSummaryMapper.selectDashboardRankList(
                  systemTaskType,
                  new LambdaQueryWrapper<JobSummary>()
                          .in(CollUtil.isNotEmpty(groupNames), JobSummary::getGroupName, groupNames)
                          .eq(StrUtil.isNotBlank(queryVO.getGroupName()), JobSummary::getGroupName, queryVO.getGroupName())
                          .ge(JobSummary::getTriggerAt, startDateTime).le(JobSummary::getTriggerAt, endDateTime)
                          .eq(JobSummary::getSystemTaskType, systemTaskType)
                          .eq(JobSummary::getNamespaceId, namespaceId)
                          .groupBy(JobSummary::getNamespaceId, JobSummary::getGroupName, JobSummary::getBusinessId));
          List<DashboardRetryLineResponseVO.Rank> ranks = SceneQuantityRankResponseVOConverter.INSTANCE.convertList(rankList);
          responseVO.setRankList(ranks);
          return responseVO;
      }
  
      @Override
      public PageResult<List<ServerNodeResponseVO>> pods(ServerNodeQueryVO queryVO) {
          PageDTO<ServerNode> pageDTO = new PageDTO<>(queryVO.getPage(), queryVO.getSize());
  
          LambdaQueryWrapper<ServerNode> serverNodeLambdaQueryWrapper = new LambdaQueryWrapper<ServerNode>()
                  .in(ServerNode::getNamespaceId, Lists.newArrayList(
                          UserSessionUtils.currentUserSession().getNamespaceId(), ServerRegister.NAMESPACE_ID))
                  .eq(StrUtil.isNotBlank(queryVO.getGroupName()), ServerNode::getGroupName, queryVO.getGroupName())
                  .ge(ServerNode::getExpireAt, LocalDateTime.now().minusSeconds(ServerRegister.DELAY_TIME + (ServerRegister.DELAY_TIME / 3)))
                  .orderByDesc(ServerNode::getNodeType);
          PageDTO<ServerNode> serverNodePageDTO = serverNodeMapper.selectPage(pageDTO, serverNodeLambdaQueryWrapper);
          List<ServerNodeResponseVO> responseVOList = ServerNodeResponseVOConverter.INSTANCE.convertList(serverNodePageDTO.getRecords());
  
          for (final ServerNodeResponseVO serverNodeResponseVO : responseVOList) {
              if (NodeTypeEnum.CLIENT.getType().equals(serverNodeResponseVO.getNodeType())) {
                  continue;
              }
  
              // 若是本地节点则直接从缓存中取
              if (ServerRegister.CURRENT_CID.equals(serverNodeResponseVO.getHostId())) {
                  serverNodeResponseVO.setConsumerBuckets(DistributeInstance.INSTANCE.getConsumerBucket());
                  continue;
              }
              if (StringUtils.isBlank(serverNodeResponseVO.getExtAttrs())) {
                  continue;
              }
              ServerNodeExtAttrs serverNodeExtAttrs = JsonUtil.parseObject(serverNodeResponseVO.getExtAttrs(), ServerNodeExtAttrs.class);
              try {
                  // 从远程节点取
                  String url = NetUtil.getUrl(serverNodeResponseVO.getHostIp(), serverNodeExtAttrs.getWebPort(), serverProperties.getServlet().getContextPath());
                  Result<List<Integer>> result = restTemplate.getForObject(url.concat(DASHBOARD_CONSUMER_BUCKET), Result.class);
                  List<Integer> data = result.getData();
                  if (CollUtil.isNotEmpty(data)) {
                      serverNodeResponseVO.setConsumerBuckets(data.stream()
                              .sorted(Integer::compareTo)
                              .collect(Collectors.toCollection(LinkedHashSet::new)));
                  }
              } catch (Exception e) {
                  SnailJobLog.LOCAL.error("Failed to retrieve consumer group for node [{}:{}].", serverNodeResponseVO.getHostIp(), serverNodeExtAttrs.getWebPort());
              }
          }
          return new PageResult<>(serverNodePageDTO, responseVOList);
      }
  
      @Override
      public Boolean updatePodsStatus(ServerNodeStatusUpdateRequestVO updateRequestVO) {
          ServerNode serverNode = serverNodeMapper.selectById(updateRequestVO.getId());
          Assert.notNull(serverNode, () -> new SnailJobServerException("ServerNode not found."));
          if (serverNode.getNodeType().equals(NodeTypeEnum.SERVER.getType())) {
              return false;
          }
  
          String labels = serverNode.getLabels();
          Map<String, String> map = JsonUtil.parseConcurrentHashMap(labels);
          ServerStatusEnum statusEnum = ServerStatusEnum.getByType(updateRequestVO.getServerNodeStatus());
          Assert.notNull(statusEnum, () -> new SnailJobServerException("ServerNodeStatus not existed"));
          map.put(SystemConstants.DEFAULT_LABEL.getKey(), statusEnum.getStatus());
          serverNode.setLabels(JsonUtil.toJsonString(map));
          Boolean updated = updateLabel(serverNode);
  
          Assert.isTrue(updated, () -> new SnailJobServerException("update labels error"));
          return updated;
      }
  
  
      @Override
      public Boolean updatePodsLabels(ServerNodeLabelsUpdateRequestVO updateRequestVO) {
          ServerNode serverNode = serverNodeMapper.selectById(updateRequestVO.getId());
          if (serverNode.getNodeType().equals(NodeTypeEnum.SERVER.getType())) {
              return false;
          }
  
          Map<String, String> toUpdateMap = JsonUtil.parseHashMap(updateRequestVO.getLabels());
  
          String labels = serverNode.getLabels();
          Map<String, String> dbMap;
          if(StrUtil.isNotBlank(labels)) {
              dbMap = JsonUtil.parseHashMap(labels);
              // 重新放入DEFAULT_LABEL
              toUpdateMap.put(SystemConstants.DEFAULT_LABEL.getKey(),
                      dbMap.get(SystemConstants.DEFAULT_LABEL.getKey()));
          }
  
          serverNode.setLabels(JsonUtil.toJsonString(toUpdateMap));
  
          return updateLabel(serverNode);
      }
  
      private Boolean updateLabel(ServerNode serverNode) {
          if (serverNodeMapper.updateById(serverNode) > 0) {
              UpdateClientInfoDTO clientInfoDTO = new UpdateClientInfoDTO();
              clientInfoDTO.setHostId(serverNode.getHostId());
              clientInfoDTO.setHostIp(serverNode.getHostIp());
              clientInfoDTO.setNamespaceId(serverNode.getNamespaceId());
              clientInfoDTO.setGroupName(serverNode.getGroupName());
              clientInfoDTO.setLabels(serverNode.getLabels());
              return updateClientRegister.updateClientInfo(clientInfoDTO);
          } else {
              return false;
          }
      }
  
  }
  