// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition;

import com.yahoo.config.application.api.FileRegistry;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * Constant values for ranking/model execution tied to a schema, or globally to an application package
 *
 * @author bratseth
 */
public class RankingConstants {

    private final FileRegistry fileRegistry;

    /** The schema this belongs to, or empty if it is global */
    private final Optional<Schema> owner;

    private final Map<String, RankingConstant> constants = new LinkedHashMap<>();

    public RankingConstants(FileRegistry fileRegistry, Optional<Schema> owner) {
        this.fileRegistry = fileRegistry;
        this.owner = owner;
    }

    public void add(RankingConstant constant) {
        constant.validate();
        constant.register(fileRegistry);
        String name = constant.getName();
        RankingConstant prev = constants.putIfAbsent(name, constant);
        if ( prev != null )
            throw new IllegalArgumentException("Constant '" + name + "' defined twice");
    }

    public void putIfAbsent(RankingConstant constant) {
        constant.validate();
        constant.register(fileRegistry);
        String name = constant.getName();
        constants.putIfAbsent(name, constant);
    }

    public void computeIfAbsent(String name, Function<? super String, ? extends RankingConstant> createConstant) {
        constants.computeIfAbsent(name, key -> {
            RankingConstant constant = createConstant.apply(key);
            constant.validate();
            constant.register(fileRegistry);
            return constant;
        });
    }

    /** Returns the ranking constant with the given name, or null if not present */
    public RankingConstant get(String name) {
        var constant = constants.get(name);
        if (constant != null) return constant;
        if (owner.isPresent() && owner.get().inherited().isPresent())
            return owner.get().inherited().get().rankingConstants().get(name);
        return null;
    }

    /** Returns a read-only map of the ranking constants in this indexed by name */
    public Map<String, RankingConstant> asMap() {
        // Shortcuts
        if (owner.isEmpty() || owner.get().inherited().isEmpty()) return Collections.unmodifiableMap(constants);
        if (constants.isEmpty()) return owner.get().inherited().get().rankingConstants().asMap();

        var allConstants = new LinkedHashMap<>(owner.get().inherited().get().rankingConstants().asMap());
        allConstants.putAll(constants);
        return Collections.unmodifiableMap(allConstants);
    }

}

@@ -810,24 +810,72 @@
 shMap%3C%3E();%0A%0A
+    private final Object mutex = new Object();%0A%0A
     public R
@@ -1060,32 +1060,67 @@
 ant constant) %7B%0A
+        synchronized (mutex) %7B%0A    
         constant
@@ -1124,32 +1124,36 @@
 ant.validate();%0A
+    
         constant
@@ -1177,32 +1177,36 @@
 istry);%0A        
+ 
+   
 String name = co
@@ -1215,32 +1215,36 @@
 tant.getName();%0A
+    
         RankingC
@@ -1309,13 +1309,16 @@
     
+   
+ 
 if (
- 
 prev
@@ -1329,11 +1329,14 @@
 null
- 
 )%0A
+    
     
@@ -1420,16 +1420,26 @@
 wice%22);%0A
+        %7D%0A
     %7D%0A%0A 
@@ -1485,32 +1485,67 @@
 ant constant) %7B%0A
+        synchronized (mutex) %7B%0A    
         constant
@@ -1549,32 +1549,36 @@
 ant.validate();%0A
+    
         constant
@@ -1602,32 +1602,36 @@
 istry);%0A        
+   
+ 
 String name = co
@@ -1640,32 +1640,36 @@
 tant.getName();%0A
+    
         constant
@@ -1695,24 +1695,34 @@
  constant);%0A
+        %7D%0A
     %7D%0A%0A    p
@@ -1823,32 +1823,67 @@
 eateConstant) %7B%0A
+        synchronized (mutex) %7B%0A    
         constant
@@ -1915,16 +1915,20 @@
 ey -%3E %7B%0A
+    
         
@@ -1989,32 +1989,36 @@
 y);%0A            
+    
 constant.validat
@@ -2026,32 +2026,36 @@
 ();%0A            
+ 
+   
 constant.registe
@@ -2079,24 +2079,28 @@
             
+    
 return const
@@ -2116,12 +2116,26 @@
     
+   
+ 
 %7D);%0A
+        %7D%0A
     
@@ -2263,24 +2263,59 @@
 ing name) %7B%0A
+        synchronized (mutex) %7B%0A    
         var 
@@ -2338,32 +2338,36 @@
 ants.get(name);%0A
+    
         if (cons
@@ -2397,32 +2397,36 @@
 nstant;%0A        
+    
 if (owner.isPres
@@ -2471,16 +2471,20 @@
 sent())%0A
+    
         
@@ -2558,24 +2558,28 @@
 e);%0A        
+ 
+   
 return null;
@@ -2579,16 +2579,26 @@
 n null;%0A
+        %7D%0A
     %7D%0A%0A 
@@ -2726,24 +2726,59 @@
 %3E asMap() %7B%0A
+        synchronized (mutex) %7B%0A    
         // S
@@ -2786,32 +2786,36 @@
 ortcuts%0A        
+ 
+   
 if (owner.isEmpt
@@ -2903,32 +2903,36 @@
 tants);%0A        
+    
 if (constants.is
@@ -3010,24 +3010,28 @@
 );%0A%0A        
+   
+ 
 var allConst
@@ -3124,16 +3124,20 @@
         
+    
 allConst
@@ -3152,32 +3152,36 @@
 All(constants);%0A
+    
         return C
@@ -3218,21 +3218,31 @@
 Constants);%0A
+        %7D%0A
     %7D%0A%0A%7D%0A

Synchronize access