package org.cqframework.cql.cql2elm;

import org.cqframework.cql.cql2elm.model.TranslatedLibrary;
import org.hl7.elm.r1.VersionedIdentifier;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

public class LibraryManager {
    private static final Map<String, TranslatedLibrary> LIBRARIES = new HashMap<String, TranslatedLibrary>();
    private static final Stack<String> TRANSLATION_STACK = new Stack<String>();

    public static TranslatedLibrary resolveLibrary(VersionedIdentifier libraryIdentifier, List<CqlTranslatorException> errors) {
        if (libraryIdentifier == null) {
            throw new IllegalArgumentException("libraryIdentifier is null.");
        }

        if (libraryIdentifier.getId() == null || libraryIdentifier.getId().equals("")) {
            throw new IllegalArgumentException("libraryIdentifier Id is null");
        }

        TranslatedLibrary library = LIBRARIES.get(libraryIdentifier.getId());
        if (library == null) {
            library = translateLibrary(libraryIdentifier, errors);
        }

        if (libraryIdentifier.getVersion() != null && !libraryIdentifier.getVersion().equals(library.getIdentifier().getVersion())) {
            throw new IllegalArgumentException(String.format("Could not resolve reference to library %s, version %s because version %s is already loaded.",
                    libraryIdentifier.getId(), libraryIdentifier.getVersion(), library.getIdentifier().getVersion()));
        }

        return library;
    }

    private static TranslatedLibrary translateLibrary(VersionedIdentifier libraryIdentifier, List<CqlTranslatorException> errors) {
        InputStream librarySource = LibrarySourceLoader.getLibrarySource(libraryIdentifier);
        if (librarySource == null) {
            throw new IllegalArgumentException(String.format("Could not load source for library %s, version %s.",
                    libraryIdentifier.getId(), libraryIdentifier.getVersion()));
        }

        try {
            CqlTranslator translator = CqlTranslator.fromStream(librarySource);
            if (errors != null) {
                errors.addAll(translator.getErrors());
            }

            return translator.getTranslatedLibrary();
        } catch (IOException e) {
            throw new IllegalArgumentException(String.format("Errors occurred translating library %s, version %s.",
                    libraryIdentifier.getId(), libraryIdentifier.getVersion()), e);
        }
    }

    public static void beginTranslation(String libraryName) {
        if (libraryName == null || libraryName.equals("")) {
            throw new IllegalArgumentException("libraryName is null.");
        }

        if (TRANSLATION_STACK.contains(libraryName)) {
            throw new IllegalArgumentException(String.format("Circular library reference %s.", libraryName));
        }

        TRANSLATION_STACK.push(libraryName);
    }

    public static void endTranslation(String libraryName) {
        if (libraryName == null || libraryName.equals("")) {
            throw new IllegalArgumentException("libraryName is null.");
        }

        String currentLibraryName = TRANSLATION_STACK.pop();
        if (!libraryName.equals(currentLibraryName)) {
            throw new IllegalArgumentException(String.format("Translation stack imbalance for library %s.", libraryName));
        }
    }
}

@@ -1037,41 +1037,17 @@
     
-if (library == null) %7B%0A  
+%0A
         
   li
@@ -1046,18 +1046,20 @@
     
-  
+if (
 library 
 = tr
@@ -1058,54 +1058,16 @@
 ary 
+!
 = 
-translateLibrary(libraryIdentifier, errors);
+null 
 %0A   
@@ -1067,35 +1067,32 @@
 ll %0A        
-%7D%0A%0A
         
 if (libraryI
@@ -1071,36 +1071,35 @@
                 
-if (
+&& 
 libraryIdentifie
@@ -1121,16 +1121,33 @@
 != null 
+%0A                
 && !libr
@@ -1497,32 +1497,179 @@
 n()));%0A        %7D
+ else %7B%0A            library = translateLibrary(libraryIdentifier, errors);%0A            LIBRARIES.put(libraryIdentifier.getId(), library);%0A        %7D
 %0A%0A        return

Fix an oversight where parsed libraries were never being cached