/*
 * Copyright 2013 Sébastien Lesaint
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fr.phan.damapping.processor.sourcegenerator.writer;

import fr.phan.damapping.processor.model.DAName;
import fr.phan.damapping.processor.model.DAType;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;

import static com.google.common.collect.FluentIterable.from;

/**
 * DAFileWriter -
 *
 * @author Sébastien Lesaint
 */
public class DAFileWriter implements DAWriter {
  private final BufferedWriter bw;
  private DAName packageName;

  public DAFileWriter(BufferedWriter bw) {
    this.bw = bw;
  }

  public DAFileWriter appendPackage(@Nonnull DAName packageName) throws IOException {
    this.packageName = Preconditions.checkNotNull(packageName, "PackageName can not be null");
    if (packageName != null && packageName.length() > 0) {
      bw.append("package ").append(packageName).append(";");
      bw.newLine();
      bw.newLine();
    }
    return this;
  }

  public DAFileWriter appendImports(Collection<DAName> mapperImports) throws IOException {
    if (mapperImports.isEmpty()) {
      return this;
    }

    List<DAName> imports = filterAndSortImports(mapperImports, packageName);
    if (imports.isEmpty()) {
      return this;
    }

    for (DAName name : imports) {
      bw.append("import ").append(name).append(";");
      bw.newLine();
    }
    bw.newLine();
    return this;
  }

  private List<DAName> filterAndSortImports(Collection<DAName> mapperImports, @Nullable DAName packageName) {
    Predicate<DAName> notDisplayedBase = Predicates.or(
        // defense against null values, of null/empty DAName.name
        InvalidDAName.INSTANCE,
        // imports from java itself
        JavaLangDANamePredicate.INSTANCE
    );
    Predicate<DAName> notDisplayed;
    if (packageName == null) {
      notDisplayed = notDisplayedBase;
    }
    else {
      notDisplayed = Predicates.or(
          notDisplayedBase,
          // imports in the same package as the generated class (ie. the package of the Mapper class)
          new PackagePredicate(packageName)
      );
    }

    List<DAName> res = Lists.newArrayList(
        from(mapperImports)
            .filter(
                Predicates.not(
                    notDisplayed
                )
            ).toImmutableSet() // using deprecated method because old version of Guava is bundled into IDEA 12
    );
    Collections.sort(res);
    return res;
  }

  public DAFileWriter appendWarningComment() throws IOException {
    bw.append("// GENERATED CODE, DO NOT MODIFY, THIS WILL BE OVERRIDE");
    bw.newLine();
    return this;
  }

  public DAClassWriter<DAFileWriter> newClass(DAType classType) throws IOException {
    return new DAClassWriter<DAFileWriter>(classType, bw, this, 0);
  }

  public DAInterfaceWriter<DAFileWriter> newInterface(String name) throws IOException {
    return new DAInterfaceWriter<DAFileWriter>(name, bw, this, 0);
  }

  public void end() throws IOException {
    bw.flush();
    bw.close();
  }

  private static enum InvalidDAName implements Predicate<DAName> {
    INSTANCE;

    @Override
    public boolean apply(@Nullable DAName daName) {
      return daName == null || daName.getName() == null || daName.getName().isEmpty();
    }
  }

  private static enum JavaLangDANamePredicate implements Predicate<DAName> {
    INSTANCE;

    @Override
    public boolean apply(@Nullable DAName qualifiedName) {
      return qualifiedName != null && qualifiedName.toString().startsWith("java.lang.");
    }
  }

  private static class PackagePredicate implements Predicate<DAName> {
    @Nonnull
    private final DAName packageName;

    public PackagePredicate(@Nonnull DAName packageName) {
      this.packageName = packageName;
    }

    @Override
    public boolean apply(DAName qualifiedName) {
      String name = qualifiedName.toString();
      int dotIndex = name.lastIndexOf(".");
      if (dotIndex > -1) {
        return name.substring(0, dotIndex).equals(packageName.toString());
      }
      return false;
    }
  }
}

@@ -655,16 +655,184 @@
 riter;%0A%0A
+import com.google.common.base.Preconditions;%0Aimport com.google.common.base.Predicate;%0Aimport com.google.common.base.Predicates;%0Aimport com.google.common.collect.Lists;%0A
 import f
@@ -922,16 +922,83 @@
 AType;%0A%0A
+import javax.annotation.Nonnull;%0Aimport javax.annotation.Nullable;%0A
 import j
@@ -1134,283 +1134,116 @@
 st;%0A
-import javax.annotation.Nonnull;%0Aimport javax.annotation.Nullable;%0Aimport com.google.common.base.Preconditions;%0Aimport com.google.common.base.Predicate;%0Aimport com.google.common.base.Predicates;%0Aimport com.google.common.collect.Lists;%0A%0Aimport static com.google.common.collect
+%0Aimport static com.google.common.collect.FluentIterable.from;%0Aimport static fr.phan.damapping.processor.util
 .Flu
@@ -1253,21 +1253,27 @@
 Iterable
-.from
+Proxy.toSet
 ;%0A%0A/**%0A 
@@ -2483,18 +2483,16 @@
 (%0A      
-  
 // defen
@@ -2541,18 +2541,16 @@
 me.name%0A
-  
       In
@@ -2573,26 +2573,24 @@
 ANCE,%0A      
-  
 // imports f
@@ -2605,18 +2605,16 @@
  itself%0A
-  
       Ja
@@ -2762,20 +2762,16 @@
 e;%0A    %7D
-%0A   
  else %7B%0A
@@ -2806,34 +2806,32 @@
 tes.or(%0A        
-  
 notDisplayedBase
@@ -2832,18 +2832,16 @@
 edBase,%0A
-  
         
@@ -2932,18 +2932,16 @@
  class)%0A
-  
         
@@ -3033,16 +3033,29 @@
 ayList(%0A
+      toSet(%0A
         
@@ -3084,18 +3084,16 @@
         
-  
 .filter(
@@ -3093,20 +3093,16 @@
 filter(%0A
-    
         
@@ -3135,22 +3135,16 @@
         
-      
 notDispl
@@ -3160,20 +3160,16 @@
         
-  
-  
 )%0A      
@@ -3176,108 +3176,17 @@
     
-  ).toImmutableSet() // using deprecated method because old version of Guava is bundled into IDEA 12
+)%0A      )
 %0A   

fix one last direct use of FluentIterable.toImmutableSet()