/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.fs.loadGenerator;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.EnumSet;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.Options.CreateOpts;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

/**
 * This program reads the directory structure and file structure from
 * the input directory and creates the namespace in the file system
 * specified by the configuration in the specified root.
 * All the files are filled with 'a'.
 * 
 * The synopsis of the command is
 * java DataGenerator 
 *   -inDir <inDir>: input directory name where directory/file structures
 *                   are stored. Its default value is the current directory.
 *   -root <root>: the name of the root directory which the new namespace 
 *                 is going to be placed under. 
 *                 Its default value is "/testLoadSpace".
 */
public class DataGenerator extends Configured implements Tool {
  private File inDir = StructureGenerator.DEFAULT_STRUCTURE_DIRECTORY;
  private Path root = DEFAULT_ROOT;
  private FileContext fc;
  final static private long BLOCK_SIZE = 10;
  final static private String USAGE = "java DataGenerator " +
  		"-inDir <inDir> " +
  		"-root <root>";
  
  /** default name of the root where the test namespace will be placed under */
  final static Path DEFAULT_ROOT = new Path("/testLoadSpace");
  
  /** Main function.
   * It first parses the command line arguments.
   * It then reads the directory structure from the input directory 
   * structure file and creates directory structure in the file system
   * namespace. Afterwards it reads the file attributes and creates files 
   * in the file. All file content is filled with 'a'.
   */
  @Override
  public int run(String[] args) throws Exception {
    int exitCode = 0;
    exitCode = init(args);
    if (exitCode != 0) {
      return exitCode;
    }
    genDirStructure();
    genFiles();
    return exitCode;
  }

  /** Parse the command line arguments and initialize the data */
  private int init(String[] args) {
    try { // initialize file system handle
      fc = FileContext.getFileContext(getConf());
    } catch (IOException ioe) {
      System.err.println("Can not initialize the file system: " + 
          ioe.getLocalizedMessage());
      return -1;
    }

    for (int i = 0; i < args.length; i++) { // parse command line
      if (args[i].equals("-root")) {
        root = new Path(args[++i]);
      } else if (args[i].equals("-inDir")) {
        inDir = new File(args[++i]);
      } else {
        System.err.println(USAGE);
        ToolRunner.printGenericCommandUsage(System.err);
        System.exit(-1);
      }
    }
    return 0;
  }
  
  /** Read directory structure file under the input directory.
   * Create each directory under the specified root.
   * The directory names are relative to the specified root.
   */
  private void genDirStructure() throws IOException {
    BufferedReader in = new BufferedReader(
        new FileReader(new File(inDir, 
            StructureGenerator.DIR_STRUCTURE_FILE_NAME)));
    String line;
    while ((line=in.readLine()) != null) {
      fc.mkdir(new Path(root+line), FileContext.DEFAULT_PERM, true);
    }
  }

  /** Read file structure file under the input directory.
   * Create each file under the specified root.
   * The file names are relative to the root.
   */
  private void genFiles() throws IOException {
    BufferedReader in = new BufferedReader(
        new FileReader(new File(inDir, 
            StructureGenerator.FILE_STRUCTURE_FILE_NAME)));
    String line;
    while ((line=in.readLine()) != null) {
      String[] tokens = line.split(" ");
      if (tokens.length != 2) {
        throw new IOException("Expect at most 2 tokens per line: " + line);
      }
      String fileName = root+tokens[0];
      long fileSize = (long)(BLOCK_SIZE*Double.parseDouble(tokens[1]));
      genFile(new Path(fileName), fileSize);
    }
  }
  
  /** Create a file with the name <code>file</code> and 
   * a length of <code>fileSize</code>. The file is filled with character 'a'.
   */
  private void genFile(Path file, long fileSize) throws IOException {
    FSDataOutputStream out = fc.create(file,
        EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE),
        CreateOpts.createParent(), CreateOpts.bufferSize(4096),
        CreateOpts.repFac((short) 3));
    for(long i=0; i<fileSize; i++) {
      out.writeByte('a');
    }
    out.close();
  }
  
  /** Main program.
   * 
   * @param args Command line arguments
   * @throws Exception
   */
  public static void main(String[] args) throws Exception {
    int res = ToolRunner.run(new Configuration(),
        new DataGenerator(), args);
    System.exit(res);
  }
}
