/*
 * Copyright 2018 - 2019 Volker Berlin (i-net software)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.inetsoftware.jwebassembly.module;

import java.util.HashMap;
import java.util.HashSet;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import de.inetsoftware.classparser.MethodInfo;

/**
 * Manage the required function/methods
 * 
 * @author Volker Berlin
 */
public class FunctionManager {

    private HashSet<String>       writtenFunctions = new HashSet<>();

    private HashSet<FunctionName> toWriteLater     = new HashSet<>();

    private HashMap<FunctionName, MethodInfo> replacement = new HashMap<>();

    /**
     * Mark the a function as written to the wasm file.
     * 
     * @param name
     *            the function name
     */
    void writeFunction( FunctionName name ) {
        toWriteLater.remove( name );
        writtenFunctions.add( name.signatureName );
    }

    /**
     * Mark a function as used/called.
     * 
     * @param name
     *            the function name
     */
    void functionCall( FunctionName name ) {
        if( !writtenFunctions.contains( name.signatureName ) ) {
            toWriteLater.add( name );
        }
    }

    /**
     * Get the first FunctionName that is required but was not written.
     * 
     * @return the FunctionName or null
     */
    @Nullable
    FunctionName nextWriteLater() {
        if( toWriteLater.isEmpty() ) {
            return null;
        }
        return toWriteLater.iterator().next();
    }

    /**
     * if the given function is required but was not written.
     * 
     * @param name
     *            the function name
     * @return true, if the function on the to do list
     */
    boolean isToWrite( FunctionName name ) {
        return toWriteLater.contains( name );
    }

    /**
     * Add a replacement for a method
     * 
     * @param name
     *            the name of the method which should be replaced
     * @param method
     *            the new implementation
     */
    void addReplacement( FunctionName name, MethodInfo method ) {
        replacement.put( name, method );
    }

    /**
     * Check if there is a replacement method
     * 
     * @param name
     *            the name
     * @param method
     *            the current method
     * @return the method that should be write
     */
    @Nonnull
    MethodInfo replace( FunctionName name, MethodInfo method ) {
        MethodInfo newMethod = replacement.get( name );
        return newMethod != null ? newMethod : method;
    }
}

@@ -704,23 +704,25 @@
 va.util.
-HashSet
+Map.Entry
 ;%0A%0Aimpor
@@ -961,42 +961,57 @@
 Hash
-Set%3CString%3E       writtenFunctions
+Map%3CFunctionName, FunctionState%3E states          
  = n
@@ -1009,35 +1009,35 @@
       = new Hash
-Set
+Map
 %3C%3E();%0A%0A    priva
@@ -1035,151 +1035,403 @@
     
-private HashSet%3CFunctionName%3E toWriteLater     = new HashSet%3C%3E();%0A%0A    private HashMap%3CFunctionName, MethodInfo%3E replacement = new HashMap%3C%3E();
+/**%0A     * Get an existing state or create one.%0A     * %0A     * @param name%0A     *            the FunctionName%0A     * @return the state%0A     */%0A    @Nonnull%0A    private FunctionState getOrCreate( FunctionName name ) %7B%0A        FunctionState state = states.get( name );%0A        if( state == null ) %7B%0A            states.put( name, state = new FunctionState() );%0A        %7D%0A        return state;%0A    %7D
 %0A%0A  
@@ -1621,87 +1621,49 @@
     
-toWriteLater.remove( name );%0A        writtenFunctions.add( name.signatureName )
+getOrCreate( name ).state = State.Written
 ;%0A  
@@ -1842,101 +1842,129 @@
     
-if( !writtenFunctions.contains( name.signatureName ) ) %7B%0A            toWriteLater.add( name )
+FunctionState state = getOrCreate( name );%0A        if( state.state == State.None ) %7B%0A            state.state = State.Need
 ;%0A  
@@ -2176,63 +2176,134 @@
     
-if( toWriteLater.isEmpty() ) %7B%0A            return null;
+for( Entry%3CFunctionName, FunctionState%3E entry : states.entrySet() ) %7B%0A            if( entry.getValue().state == State.Need ) %7B
 %0A   
@@ -2299,34 +2299,32 @@
 eed ) %7B%0A        
-%7D%0A
         return t
@@ -2326,38 +2326,67 @@
 urn 
-toWriteLater.iterator().next()
+entry.getKey();%0A            %7D%0A        %7D%0A        return null
 ;%0A  
@@ -2650,37 +2650,47 @@
 urn 
-toWriteLater.contains( name )
+getOrCreate( name ).state == State.Need
 ;%0A  
@@ -2981,39 +2981,43 @@
     
-replacement.put
+getOrCreate
 ( name
-,
  
+).
 method 
-)
+= method
 ;%0A  
@@ -3355,31 +3355,27 @@
 d = 
-replacement.get
+getOrCreate
 ( name )
 ;%0A  
@@ -3362,32 +3362,39 @@
 OrCreate( name )
+.method
 ;%0A        return
@@ -3439,11 +3439,258 @@
 d;%0A    %7D
+%0A%0A    /**%0A     * State of a function/method%0A     */%0A    private static class FunctionState %7B%0A        private State    state = State.None;%0A%0A        private MethodInfo method;%0A    %7D%0A%0A    private static enum State %7B%0A        None, Need, Written;%0A    %7D
 %0A%7D%0A

Use a FunctionState in the FunctionManager for later improvements