package org.ambraproject.wombat.service;

import com.google.common.io.Closer;
import org.ambraproject.wombat.config.site.Site;
import org.ambraproject.wombat.config.theme.Theme;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.Templates;
import javax.xml.transform.stream.StreamSource;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class SiteTransformerFactory {

  private static TransformerFactory newTransformerFactory() {
    // This implementation is required for XSLT features, so just hard-code it here
    // Preferred over TransformerFactory.newInstance because Java system properties can burn in hell
    return new net.sf.saxon.TransformerFactoryImpl();
  }

  private final String rootPath;
  private final String templateFilename;
  private final Map<Site, Templates> transformTemplateCache = Collections.synchronizedMap(new HashMap<>());

  public SiteTransformerFactory(String rootPath, String templateFilename) {
    this.rootPath = Objects.requireNonNull(rootPath);
    this.templateFilename = Objects.requireNonNull(templateFilename);
  }

  public Transformer build(Site site) throws IOException {
    Templates templates = transformTemplateCache.get(site);
    if (templates == null) {
      TransformerFactory factory = newTransformerFactory();
      Theme theme = site.getTheme();
      try (ThemeUriResolver resolver = new ThemeUriResolver(theme);
           InputStream transformFile = theme.getStaticResource(rootPath + templateFilename)) {
        factory.setURIResolver(resolver);
        templates = factory.newTemplates(new StreamSource(transformFile));
      } catch (TransformerConfigurationException e) {
        throw new RuntimeException(e);
      }
      transformTemplateCache.put(site, templates);
    }
    try {
      return templates.newTransformer();
    } catch (TransformerConfigurationException e){
      throw new RuntimeException(e);
    }
  }


  private class ThemeUriResolver implements URIResolver, Closeable {

    /*
     * Any stream opened while providing a Source gets stored here,
     * then is closed when the outer ThemeUriResolver object is closed.
     */
    private final Closer closer = Closer.create();

    private final Theme theme;

    private ThemeUriResolver(Theme theme) {
      this.theme = Objects.requireNonNull(theme);
    }

    @Override
    public Source resolve(String href, String base) throws TransformerException {
      InputStream resourceStream;
      try {
        resourceStream = closer.register(theme.getStaticResource(rootPath + href));
      } catch (IOException e) {
        throw new TransformerException(e);
      }
      return new StreamSource(resourceStream);
    }

    @Override
    public void close() throws IOException {
      closer.close();
    }
  }

}

@@ -630,27 +630,19 @@
 va.util.
-Collections
+Map
 ;%0Aimport
@@ -652,23 +652,23 @@
 va.util.
-HashMap
+Objects
 ;%0Aimport
@@ -682,37 +682,36 @@
 til.
-Map;%0Aimport java.util.Objects
+concurrent.ConcurrentHashMap
 ;%0A%0Ap
@@ -1194,40 +1194,22 @@
 e = 
-Collections.synchronizedMap(new 
+new Concurrent
 Hash
@@ -1215,17 +1215,16 @@
 hMap%3C%3E()
-)
 ;%0A%0A  pub
@@ -1460,35 +1460,16 @@
 te site)
- throws IOException
  %7B%0A    T
@@ -1516,50 +1516,251 @@
 che.
-get(site);%0A    if (templates == null
+computeIfAbsent(site, this::createTemplate);%0A    try %7B%0A      return templates.newTransformer();%0A    %7D catch (TransformerConfigurationException e)%7B%0A      throw new RuntimeException(e);%0A    %7D%0A  %7D%0A%0A  private Templates createTemplate(Site site
 ) %7B%0A
-  
     
@@ -1809,26 +1809,24 @@
 rFactory();%0A
-  
     Theme th
@@ -1848,26 +1848,24 @@
 heme();%0A    
-  
 try (ThemeUr
@@ -1910,26 +1910,24 @@
 ver(theme);%0A
-  
          Inp
@@ -2013,18 +2013,16 @@
 %7B%0A      
-  
 factory.
@@ -2057,21 +2057,14 @@
     
-  templates =
+return
  fac
@@ -2111,34 +2111,32 @@
 formFile));%0A    
-  
 %7D catch (Transfo
@@ -2166,217 +2166,25 @@
 ion 
-e) %7B%0A        throw new RuntimeException(e);%0A      %7D%0A      transformTemplateCache.put(site, templates);%0A    %7D%0A    try %7B%0A      return templates.newTransformer();%0A    %7D catch (TransformerConfigurationException e)
+%7C IOException e) 
 %7B%0A  
@@ -2229,17 +2229,16 @@
  %7D%0A  %7D%0A%0A
-%0A
   privat

break out map value into its own method, use ConcurrentHashMap for better performance