/*
 * Copyright 2013 Cloudera 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 org.kitesdk.cli.commands;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.util.List;
import org.apache.avro.Schema;
import org.kitesdk.data.spi.Compatibility;
import org.kitesdk.data.spi.SchemaUtil;
import org.slf4j.Logger;

@Parameters(commandDescription = "Show the schema for a Dataset")
public class SchemaCommand extends BaseDatasetCommand {

  @Parameter(description = "<dataset name>")
  List<String> datasets;

  @edu.umd.cs.findbugs.annotations.SuppressWarnings(
      value="UWF_NULL_FIELD",
      justification = "Field set by JCommander")
  @Parameter(names={"-o", "--output"}, description="Save schema avsc to path")
  String outputPath = null;

  @Parameter(names="--minimize",
      description="Minimize schema file size by eliminating white space")
  boolean minimize=false;

  @Parameter(names="--merge",
      description="Merge schemas into a single output schema")
  boolean merge=false;

  public SchemaCommand(Logger console) {
    super(console);
  }

  @Override
  @edu.umd.cs.findbugs.annotations.SuppressWarnings(
      value={"NP_GUARANTEED_DEREF", "NP_NULL_ON_SOME_PATH"},
      justification="Null case checked by precondition")
  public int run() throws IOException {
    Preconditions.checkArgument(
        datasets != null && !datasets.isEmpty(),
        "Missing dataset name");
    if (merge || datasets.size() == 1) {
      Schema mergedSchema = null;
      for (String uriOrPath : datasets) {
        mergedSchema = merge(mergedSchema, schema(uriOrPath));
      }

      Preconditions.checkNotNull(mergedSchema, "No valid schema found");

      output(mergedSchema.toString(!minimize), console, outputPath);

    } else {
      Preconditions.checkArgument(outputPath == null,
          "Cannot output multiple schemas to one file");
      for (String name : datasets) {
        console.info("Dataset \"{}\" schema: {}",
            name, schema(name).toString(!minimize));
      }
    }
    return 0;
  }

  @Override
  public List<String> getExamples() {
    return Lists.newArrayList(
        "# Print the schema for dataset \"users\" to standard out:",
        "users",
        "# Print the schema for a dataset URI to standard out:",
        "dataset:hbase:zk1,zk2/users",
        "# Save the schema for dataset \"users\" to user.avsc:",
        "users -o user.avsc"
    );
  }

  private Schema schema(String uriOrPath) throws IOException {
    if (!isDatasetOrViewUri(uriOrPath) &&
        !Compatibility.isCompatibleName(uriOrPath)) {
      return new Schema.Parser().parse(open(uriOrPath));
    } else {
      return load(uriOrPath, Object.class)
          .getDataset().getDescriptor().getSchema();
    }
  }

  private static Schema merge(Schema left, Schema right) {
    if (left == null) {
      return right;
    } else if (right == null) {
      return left;
    } else {
      return SchemaUtil.merge(left, right);
    }
  }
}