/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * 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 io.fabric8.crd.generator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
import io.fabric8.crd.generator.v1.CustomResourceHandler;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

public class CRDGenerator {

  private final Resources resources = new Resources();
  private final CustomResourceHandler v1Handler = new CustomResourceHandler(resources);
  private final io.fabric8.crd.generator.v1beta1.CustomResourceHandler v1beta1Handler = new io.fabric8.crd.generator.v1beta1.CustomResourceHandler(
    resources);
  private CRDOutput output;

  private static final ObjectMapper YAML_MAPPER = new ObjectMapper(
    new YAMLFactory()
      .enable(Feature.MINIMIZE_QUOTES)
      .enable(Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS)
      .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
  );

  static {
    YAML_MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true);
    YAML_MAPPER.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
    YAML_MAPPER.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
  }

  public CRDGenerator() {
  }

  public CRDGenerator inOutputDir(File outputDir) {
    output = new DirCRDOutput(outputDir);
    return this;
  }

  public CRDGenerator withOutput(CRDOutput output) {
    this.output = output;
    return this;
  }
  
  public CRDGenerator customResources(CustomResourceInfo... infos) {
    for (CustomResourceInfo info : infos) {
      System.out.println(
        "Generating '"
          + info.crdName()
          + "' version '"
          + info.version()
          + "' with "
          + info.crClassName()
          + " (spec: "
          + info.specClassName()
          + " / status: "
          + info.statusClassName()
          + ")");

      v1Handler.handle(info);
      v1beta1Handler.handle(info);
    }
    return this;
  }

  public void generate() {
    final KubernetesList list = resources.generate();
    list.getItems().forEach(crd -> {
      final String version = ApiVersionUtil.trimVersion(crd.getApiVersion());
      try {
        try (final OutputStream outputStream = output.outputFor(crd.getMetadata().getName() + "-" + version + ".yml")){
          YAML_MAPPER.writeValue(outputStream, crd);
          System.out.println("Created " + output.crdURI());
        }
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    });
  }

  public interface CRDOutput extends Closeable {
    OutputStream outputFor(String crdFileName) throws IOException;
    URI crdURI();
  }

  static class DirCRDOutput implements CRDOutput {
    private final File dir;
    private OutputStream output;
    private URI uri;

    public DirCRDOutput(File dir) {
      if(!dir.isDirectory() || !dir.canWrite() || !dir.exists()) {
        throw new IllegalArgumentException(dir + " must exist, be a writeable output directory");
      }
      this.dir = dir;
    }

    @Override
    public OutputStream outputFor(String crdFileName) throws IOException {
      final File file = new File(dir, crdFileName);
      uri = file.toURI();
      output = new FileOutputStream(file);
      return output;
    }

    @Override
    public URI crdURI() {
      return uri;
    }

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

@@ -1330,26 +1330,8 @@
 rces
- = new Resources()
 ;%0A  
@@ -1379,47 +1379,8 @@
 dler
- = new CustomResourceHandler(resources)
 ;%0A  
@@ -1466,113 +1466,68 @@
 dler
- = new io.fabric8.crd.generator.v1beta1.CustomResourceHandler(%0A    resources);%0A  private CRDOutput output
+;%0A  private CRDOutput output;%0A  private boolean hasResources
 ;%0A%0A 
@@ -2045,16 +2045,195 @@
 tor() %7B%0A
+    resources = new Resources();%0A    v1Handler = new CustomResourceHandler(resources);%0A    v1beta1Handler = new io.fabric8.crd.generator.v1beta1.CustomResourceHandler(resources);%0A
   %7D%0A%0A  p
@@ -2557,24 +2557,51 @@
  : infos) %7B%0A
+      hasResources = true;%0A
       System
@@ -3022,24 +3022,229 @@
 enerate() %7B%0A
+    if (hasResources) %7B%0A      if (output == null) %7B%0A        System.out.println(%22No output option was selected either using 'inOutputDir' or 'withOutput' methods. Skipping generation.%22);%0A      %7D else %7B%0A    
     final Ku
@@ -3281,24 +3281,28 @@
 generate();%0A
+    
     list.get
@@ -3328,24 +3328,28 @@
  -%3E %7B%0A      
+ 
+   
 final String
@@ -3418,14 +3418,22 @@
     
+   
+ 
 try %7B%0A
+    
     
@@ -3554,24 +3554,28 @@
 %7B%0A          
+    
 YAML_MAPPER.
@@ -3607,32 +3607,36 @@
 crd);%0A          
+   
+ 
 System.out.print
@@ -3673,26 +3673,34 @@
 ));%0A        
-%7D%0A
+    %7D%0A    
       %7D catc
@@ -3719,32 +3719,36 @@
 on e) %7B%0A        
+   
+ 
 throw new Runtim
@@ -3772,18 +3772,161 @@
     
-%7D%0A
+    %7D%0A  
     
+  
 %7D);%0A
+      %7D%0A    %7D else %7B%0A      System.out.println(%22No resources were registered with the 'customResources' method to be generated%22);%0A    %7D%0A
   %7D%0A

validate generator parameters before generation