/*
 * Copyright 2010, The Android Open Source Project
 *
 * 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.
 */

#ifndef _FRAMEWORKS_COMPILE_SLANG_SLANG_REFLECT_UTILS_H_ // NOLINT
#define _FRAMEWORKS_COMPILE_SLANG_SLANG_REFLECT_UTILS_H_

#include <fstream>
#include <string>

namespace slang {

// BitCode storage type
enum BitCodeStorageType { BCST_APK_RESOURCE, BCST_JAVA_CODE, BCST_CPP_CODE };

class RSSlangReflectUtils {
public:
  // Encode a binary bitcode file into a Java source file.
  // rsFileName: the original .rs file name (with or without path).
  // bc32FileName: path of the 32-bit bitcode file
  // bc64FileName: path of the 64-bit bitcode file
  // reflectPath: where to output the generated Java file, no package name in
  // it.
  // packageName: the package of the output Java file.
  // verbose: whether or not to print out additional info about compilation.
  // bcStorage: where to emit bitcode to (resource file or embedded).
  struct BitCodeAccessorContext {
    const char *rsFileName;
    const char *bc32FileName;
    const char *bc64FileName;
    const char *reflectPath;
    const char *packageName;
    const std::string *licenseNote;
    bool verbose;
    BitCodeStorageType bcStorage;
  };

  // Return the stem of the file name, i.e., remove the dir and the extension.
  // Eg, foo.ext -> foo
  //     foo.bar.ext -> foo.bar
  //     ./path/foo.ext -> foo
  static std::string GetFileNameStem(const char *fileName);

  // Compute a Java source file path from a given prefixPath and its package.
  // Eg, given prefixPath=./foo/bar and packageName=com.x.y, then it returns
  // ./foo/bar/com/x/y
  static std::string ComputePackagedPath(const char *prefixPath,
                                         const char *packageName);

  // Compute Java class name from a .rs file name.
  // Any non-alnum, non-underscore characters will be discarded.
  // E.g. with rsFileName=./foo/bar/my-Renderscript_file.rs it returns
  // "myRenderscript_file".
  // rsFileName: the input .rs file name (with or without path).
  static std::string JavaClassNameFromRSFileName(const char *rsFileName);

  // Compute a bitcode file name (no extension) from a .rs file name.
  // Because the bitcode file name may be used as Resource ID in the generated
  // class (something like R.raw.<bitcode_filename>), Any non-alnum,
  // non-underscore character will be discarded.
  // The difference from JavaClassNameFromRSFileName() is that the result is
  // converted to lowercase.
  // E.g. with rsFileName=./foo/bar/my-Renderscript_file.rs it returns
  // "myrenderscript_file"
  // rsFileName: the input .rs file name (with or without path).
  static std::string BCFileNameFromRSFileName(const char *rsFileName);

  // Compute the bitcode-containing class name from a .rs filename.
  // Any non-alnum, non-underscore characters will be discarded.
  // E.g. with rsFileName=./foo/bar/my-Renderscript_file.rs it returns
  // "myRenderscript_fileBitCode".
  // rsFileName: the input .rs file name (with or without path).
  static std::string JavaBitcodeClassNameFromRSFileName(const char *rsFileName);

  // Generate the bit code accessor Java source file.
  static bool GenerateJavaBitCodeAccessor(const BitCodeAccessorContext &context);
};

// Joins two sections of a path, inserting a separator if needed.
// E.g. JoinPath("foo/bar", "baz/a.java") returns "foo/bar/baz/a.java",
// JoinPath("foo", "/bar/baz") returns "foo/bar/baz", and
// JoinPath("foo/", "/bar") returns "foo/bar".
std::string JoinPath(const std::string &path1, const std::string &path2);

/* Compute a safe root name from a .rs file name.  Any non-alphanumeric,
 * non-underscore characters will be discarded.
 * E.g. RootNameFromRSFileName("./foo/bar/my-Renderscript_file.rs") returns
 * "myRenderscript_file".
 */
std::string RootNameFromRSFileName(const std::string &rsFileName);

/* This class is used to generate one source file.  There will be one instance
 * for each generated file.
 */
class GeneratedFile : public std::ofstream {
public:
  /* Starts the file by:
   * - creating the parent directories (if needed),
   * - opening the stream,
   * - writing out the license,
   * - writing a message that this file has been auto-generated.
   * If optionalLicense is nullptr, a default license is used.
   */
  bool startFile(const std::string &outPath, const std::string &outFileName,
                 const std::string &sourceFileName,
                 const std::string *optionalLicense, bool isJava, bool verbose);
  void closeFile();

  void increaseIndent(); // Increases the new line indentation by 4.
  void decreaseIndent(); // Decreases the new line indentation by 4.
  void comment(const std::string& s); // Outputs a multiline comment.

  // Starts a control block.  This works both for Java and C++.
  void startBlock() {
    *this << " {\n";
    increaseIndent();
  }

  // Ends a control block.
  void endBlock(bool addSemicolon = false) {
    decreaseIndent();
    indent() << "}" << (addSemicolon ? ";" : "") << "\n\n";
  }

  /* Indents the line.  By returning *this, we can use like this:
   *  mOut.ident() << "a = b;\n";
   */
  std::ofstream &indent() {
    *this << mIndent;
    return *this;
  }

private:
  std::string mIndent; // The correct spacing at the beginning of each line.
};

} // namespace slang

#endif // _FRAMEWORKS_COMPILE_SLANG_SLANG_REFLECT_UTILS_H_  NOLINT