class HostLevelParamsHolder extends AgentHostDataHolder<HostLevelParamsUpdateEvent> {

  @Override
  protected HostLevelParamsUpdateEvent handleUpdate(HostLevelParamsUpdateEvent current, HostLevelParamsUpdateEvent update) {
    HostLevelParamsUpdateEvent result = null;
    boolean changed = false;
    Map<String, HostLevelParamsCluster> mergedClusters = new HashMap<>();
    if (MapUtils.isNotEmpty(update.getHostLevelParamsClusters())) {
      // put from current all clusters absent in update
      for (Map.Entry<String, HostLevelParamsCluster> hostLevelParamsClusterEntry : current.getHostLevelParamsClusters().entrySet()) {
        String clusterId = hostLevelParamsClusterEntry.getKey();
        if (!update.getHostLevelParamsClusters().containsKey(clusterId)) {
          mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
        }
      }
      // process clusters from update
      for (Map.Entry<String, HostLevelParamsCluster> hostLevelParamsClusterEntry : update.getHostLevelParamsClusters().entrySet()) {
        String clusterId = hostLevelParamsClusterEntry.getKey();
        if (current.getHostLevelParamsClusters().containsKey(clusterId)) {
          boolean clusterChanged = false;
          HostLevelParamsCluster updatedCluster = hostLevelParamsClusterEntry.getValue();
          HostLevelParamsCluster currentCluster = current.getHostLevelParamsClusters().get(clusterId);
          RecoveryConfig mergedRecoveryConfig;
          SortedMap<Long, CommandRepository> mergedRepositories;
          Map<String, BlueprintProvisioningState> mergedBlueprintProvisioningStates;
          SortedMap<String, Long> mergedComponentRepos;
          if (!currentCluster.getRecoveryConfig().equals(updatedCluster.getRecoveryConfig())) {
            mergedRecoveryConfig = updatedCluster.getRecoveryConfig();
            clusterChanged = true;
          } else {
            mergedRecoveryConfig = currentCluster.getRecoveryConfig();
          }
          if (!currentCluster.getHostRepositories().getRepositories()
              .equals(updatedCluster.getHostRepositories().getRepositories())) {
            mergedRepositories = updatedCluster.getHostRepositories().getRepositories();
            clusterChanged = true;
          } else {
            mergedRepositories = currentCluster.getHostRepositories().getRepositories();
          }
          if (!currentCluster.getBlueprintProvisioningState()
              .equals(updatedCluster.getBlueprintProvisioningState())) {
            mergedBlueprintProvisioningStates = updatedCluster.getBlueprintProvisioningState();
            clusterChanged = true;
          } else {
            mergedBlueprintProvisioningStates = currentCluster.getBlueprintProvisioningState();
          }
          if (!currentCluster.getHostRepositories().getComponentRepos()
              .equals(updatedCluster.getHostRepositories().getComponentRepos())) {
            mergedComponentRepos = updatedCluster.getHostRepositories().getComponentRepos();
            clusterChanged = true;
          } else {
            mergedComponentRepos = currentCluster.getHostRepositories().getComponentRepos();
          }
          if (clusterChanged) {
            HostLevelParamsCluster mergedCluster = new HostLevelParamsCluster(
                new HostRepositories(mergedRepositories, mergedComponentRepos),
                mergedRecoveryConfig,
                mergedBlueprintProvisioningStates);
            mergedClusters.put(clusterId, mergedCluster);
            changed = true;
          } else {
            mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
          }
        } else {
          mergedClusters.put(clusterId, hostLevelParamsClusterEntry.getValue());
          changed = true;
        }
      }
    }
    if (changed) {
      result = new HostLevelParamsUpdateEvent(current.getHostId(), mergedClusters);
    }
    return result;
  }

  @Inject public  HostLevelParamsHolder(AmbariEventPublisher ambariEventPublisher);

}