// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.javac;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.tools.JavaFileManager;
import javax.tools.SimpleJavaFileObject;
import java.io.*;
import java.net.URI;
import java.nio.CharBuffer;

/**
 * @author Eugene Zhuravlev
 * Date: 16-Oct-18
 */
public abstract class JpsFileObject extends SimpleJavaFileObject {
  private static final Kind[] ourAvailableKinds = Kind.values();
  private final JavaFileManager.Location myLocation;

  public JpsFileObject(URI uri, Kind kind, @Nullable JavaFileManager.Location location) {
    super(uri, kind);
    myLocation = location;
  }

  @Nullable
  public JavaFileManager.Location getLocation() {
    return myLocation;
  }

  protected static Kind findKind(String name) {
    for (Kind kind : ourAvailableKinds) {
      if (kind != Kind.OTHER && name.regionMatches(true, name.length() - kind.extension.length(), kind.extension, 0, kind.extension.length())) {
        return kind;
      }
    }
    return Kind.OTHER;
  }

  /**
   * @param path
   * @param caseSensitiveFS
   * @return null means the file manager should delegate to base implementation
   */
  @Nullable
  protected abstract String inferBinaryName(Iterable<? extends File> path, boolean caseSensitiveFS);

  @Override
  public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
    final CharSequence content = getCharContent(ignoreEncodingErrors);
    if (content == null) {
      throw new UnsupportedOperationException();
    }
    if (content instanceof CharBuffer) {
      final CharBuffer buffer = (CharBuffer)content;
      if (buffer.hasArray()) {
        return new CharArrayReader(buffer.array(), buffer.arrayOffset(), buffer.length());
      }
    }
    return new StringReader(content.toString());
  }

  @SuppressWarnings("Duplicates")
  @NotNull
  protected static CharSequence loadCharContent(@NotNull File file, @Nullable String encoding) throws IOException {
    // FileUtil.loadText clones char array if length mismatch
    Closeable toClose = null;
    try {
      FileInputStream fileStream = new FileInputStream(file);
      toClose = fileStream;
      final int size = (int)fileStream.getChannel().size();
      InputStream stream = size > 0? new BufferedInputStream(fileStream, Math.min(size, 512 * 1024 /* 512 kb*/)) : new BufferedInputStream(fileStream);
      toClose = stream;
      final Reader reader = encoding == null ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding);
      toClose = reader;
      // channel allows to avoid extra call to get file size because fd is reused, see Files.readAllBytes
      char[] chars = new char[size];
      int count = 0;
      while (count < chars.length) {
        int n = reader.read(chars, count, chars.length - count);
        if (n <= 0) {
          break;
        }
        count += n;
      }
      return CharBuffer.wrap(chars, 0, count);
    }
    finally {
      if (toClose != null) {
        toClose.close();
      }
    }
  }

  @Override
  public int hashCode() {
    return toUri().hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    // todo: check if this is fast enough to rely on URI.equals() here
    return this == obj || (obj instanceof JpsFileObject && toUri().equals(((JpsFileObject)obj).toUri()));
  }
}

@@ -2177,50 +2177,8 @@
 tch%0A
-    Closeable toClose = null;%0A    try %7B%0A  
     
@@ -2193,21 +2193,17 @@
 tStream 
-fileS
+s
 tream = 
@@ -2237,267 +2237,13 @@
     
-  toClose = fileStream;%0A      final int size = (int)fileStream.getChannel().size();%0A      InputStream stream = size %3E 0? new BufferedInputStream(fileStream, Math.min(size, 512 * 1024 /* 512 kb*/)) : new BufferedInputStream(fileStream);%0A      toClose = stream;
+try %7B
 %0A   
@@ -2370,25 +2370,15 @@
    t
-oClose = reader;%0A
+ry %7B%0A  
     
@@ -2485,16 +2485,18 @@
 s%0A      
+  
 char%5B%5D c
@@ -2515,15 +2515,44 @@
 har%5B
+(int)stream.getChannel().
 size
+()
 %5D;%0A
+  
     
@@ -2574,16 +2574,18 @@
 ;%0A      
+  
 while (c
@@ -2603,24 +2603,26 @@
 s.length) %7B%0A
+  
         int 
@@ -2678,24 +2678,26 @@
 t);%0A        
+  
 if (n %3C= 0) 
@@ -2694,24 +2694,26 @@
  (n %3C= 0) %7B%0A
+  
           br
@@ -2721,26 +2721,30 @@
 ak;%0A        
-%7D%0A
+  %7D%0A  
         coun
@@ -2757,24 +2757,26 @@
 ;%0A      
+  
 %7D%0A      
 return C
@@ -2763,24 +2763,26 @@
     %7D%0A      
+  
 return CharB
@@ -2810,26 +2810,30 @@
 count);%0A    
-%7D%0A
+  %7D%0A  
     finally 
@@ -2844,28 +2844,51 @@
     
-if (toClose != null)
+  reader.close();%0A      %7D%0A    %7D%0A    finally
  %7B%0A 
@@ -2896,17 +2896,14 @@
     
-  toClose
+stream
 .clo
@@ -2904,32 +2904,24 @@
 am.close();%0A
-      %7D%0A
     %7D%0A  %7D%0A%0A 

revert buffering as superfluous