package com.jetbrains.python;

import com.intellij.lang.Language;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.openapi.vfs.VirtualFile;
import icons.PythonPsiApiIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author yole
 */
public class PythonFileType extends LanguageFileType {
  private static final Pattern ENCODING_PATTERN = Pattern.compile("coding[:=]\\s*([-\\w.]+)");

  public static PythonFileType INSTANCE = new PythonFileType();

  public PythonFileType() {
    this(new PythonLanguage());
  }

  public PythonFileType(Language language) {
    super(language);
  }

  @NotNull
  public String getName() {
    return "Python";
  }

  @NotNull
  public String getDescription() {
    return "Python files";
  }

  @NotNull
  public String getDefaultExtension() {
    return "py";
  }

  @NotNull
  public Icon getIcon() {
    return PythonPsiApiIcons.PythonFile;
  }

  @Override
  public String getCharset(@NotNull VirtualFile file, byte[] content) {
    if (CharsetToolkit.hasUTF8Bom(content)) {
      return CharsetToolkit.UTF8;
    }
    ByteBuffer bytes = ByteBuffer.wrap(content, 0, Math.min(256, content.length));
    String decoded = CharsetToolkit.UTF8_CHARSET.decode(bytes).toString();
    return getCharsetFromEncodingDeclaration(StringUtil.convertLineSeparators(decoded));
  }

  @Override
  public Charset extractCharsetFromFileContent(@Nullable Project project, @Nullable VirtualFile file, @NotNull String content) {
    final String charsetName = getCharsetFromEncodingDeclaration(content);
    if (charsetName == null) {
      return null;
    }
    try {
      return Charset.forName(charsetName);
    }
    catch (IllegalCharsetNameException e) {
      return null;
    }
    catch (UnsupportedCharsetException e) {
      return null;
    }
  }

  @Nullable
  public static String getCharsetFromEncodingDeclaration(String content) {
    if (!content.contains("coding")) {
      return null;
    }
    final List<String> lines = StringUtil.split(content, "\n");
    int count = 0;
    for (String line : lines) {
      final Matcher matcher = ENCODING_PATTERN.matcher(line);
      if (matcher.find()) {
        final String charset = matcher.group(1);
        return normalizeCharset(charset);
      }
      count++;
      if (count == 2) break;
    }
    return null;
  }

  @Nullable
  private static String normalizeCharset(String charset) {
    if (charset == null) {
      return null;
    }
    charset = charset.toLowerCase();
    if ("latin-1".equals(charset)) {
      return "iso-8859-1";
    }
    return charset;
  }
}

@@ -443,16 +443,104 @@
 wing.*;%0A
+import java.io.BufferedReader;%0Aimport java.io.IOException;%0Aimport java.io.StringReader;%0A
 import j
@@ -702,31 +702,8 @@
 on;%0A
-import java.util.List;%0A
 impo
@@ -2433,34 +2433,43 @@
 if (
-!
 content
-.contains(%22coding%22
+ == null %7C%7C content.isEmpty(
 )) %7B
@@ -2502,59 +2502,93 @@
     
-final List%3CString%3E lines = StringUtil.split
+try %7B%0A      final BufferedReader reader = new BufferedReader(new StringReader
 (content
 , %22%5C
@@ -2587,68 +2587,182 @@
 tent
-, %22%5Cn%22
+)
 );%0A    
-int count = 0;%0A    for (String line : lines) %7B%0A
+  try %7B%0A        for (int i = 0; i %3C 2; i++) %7B%0A          final String line = reader.readLine();%0A          if (line == null) %7B%0A            return null;%0A          %7D%0A    
     
@@ -2821,24 +2821,28 @@
 ine);%0A      
+    
 if (matcher.
@@ -2851,32 +2851,36 @@
 nd()) %7B%0A        
+   
+ 
 final String cha
@@ -2904,32 +2904,36 @@
 oup(1);%0A        
+    
 return normalize
@@ -2960,53 +2960,113 @@
     
+ 
-%7D%0A      count++;%0A      if (count == 2) break;
+   %7D%0A        %7D%0A      %7D finally %7B%0A        reader.close();%0A      %7D%0A    %7D%0A    catch (IOException ignored) %7B
 %0A   

Read content lines lazily