/**
 * Copyright (c) 2014 Takari, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package io.takari.maven.plugins.compile.jdt;

import java.io.File;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Named;

import io.takari.maven.plugins.compile.jdt.classpath.DependencyClasspathEntry;
import io.takari.maven.plugins.compile.jdt.classpath.PathNormalizer;

@Named
public class ClasspathEntryCache {

  public static interface Factory {
    DependencyClasspathEntry newClasspathEntry(Path location);
  }

  private static final Map<Path, CacheEntry> CACHE = new HashMap<>();

  private static final Map<Path, CacheEntry> SOURCEPATH_CACHE = new HashMap<>();

  public DependencyClasspathEntry get(Path location, CacheMode cacheMode, Factory factory) {
    return get(CACHE, location, cacheMode, factory);
  }

  public DependencyClasspathEntry getSourcepathEntry(Path location, CacheMode cacheMode, Factory factory) {
    return get(SOURCEPATH_CACHE, location, cacheMode, factory);
  }

  private static DependencyClasspathEntry get(Map<Path, CacheEntry> cache, Path location, CacheMode cacheMode, Factory factory) {
    location = PathNormalizer.getCanonicalPath(location);
    synchronized (cache) {
      CacheEntry entry = cache.get(location);
      CacheEntry newEntry = revalidate(location, entry, cacheMode, factory);
      if (entry != newEntry) {
        cache.put(location, entry = newEntry);
      }
      return entry.getCpe();
    }
  }

  private static CacheEntry revalidate(Path location, CacheEntry entry, CacheMode cacheMode, Factory factory) {
    // don't reload by default
    long length = -1;
    long lastModified = -1;

    if (cacheMode == CacheMode.PESSIMISTIC) {
      File file = location.toFile();
      if (file.isDirectory()) {
        // always reload dir entries
        length = 0;
        lastModified = System.currentTimeMillis();
      } else {
        // check if file has changed
        length = file.length();
        lastModified = file.lastModified();
      }
    }

    if (entry == null || entry.getSize() != length || entry.getTimestamp() != lastModified) {
      entry = new CacheEntry(factory.newClasspathEntry(location), lastModified, length);
    }

    return entry;
  }

  /**
   * @noreference this method is public for test purposes only
   */
  public static void flush() {
    synchronized (CACHE) {
      CACHE.clear();
    }
    synchronized (SOURCEPATH_CACHE) {
      SOURCEPATH_CACHE.clear();
    }
  }

  public static class CacheEntry {
    private final DependencyClasspathEntry cpe;
    private final long timestamp;
    private final long size;

    public CacheEntry(DependencyClasspathEntry cpe, long timestamp, long size) {
      this.cpe = cpe;
      this.timestamp = timestamp;
      this.size = size;
    }

    public DependencyClasspathEntry getCpe() {
      return cpe;
    }

    public long getSize() {
      return size;
    }

    public long getTimestamp() {
      return timestamp;
    }
  }

  public enum CacheMode {
    DEFAULT, PESSIMISTIC;
  }
}

@@ -1930,24 +1930,38 @@
 SIMISTIC) %7B%0A
+      try %7B%0A  
       File f
@@ -1987,24 +1987,26 @@
 le();%0A      
+  
 if (file.isD
@@ -2015,24 +2015,26 @@
 ectory()) %7B%0A
+  
         // a
@@ -2058,32 +2058,34 @@
 entries%0A        
+  
 length = 0;%0A    
@@ -2080,16 +2080,18 @@
 th = 0;%0A
+  
         
@@ -2139,16 +2139,18 @@
 ;%0A      
+ 
+ 
 %7D else %7B
@@ -2146,24 +2146,26 @@
    %7D else %7B%0A
+  
         // c
@@ -2189,32 +2189,34 @@
 changed%0A        
+  
 length = file.le
@@ -2223,32 +2223,34 @@
 ngth();%0A        
+ 
+ 
 lastModified = f
@@ -2265,24 +2265,171 @@
 Modified();%0A
+        %7D%0A      %7D catch (UnsupportedOperationException e) %7B%0A        // JRT Path doesn't support toFile --%3E JDK not expected change so don't reload%0A
       %7D%0A    

Fix UnsupportedOperationException thrown by JrtPath.toFile