/*
 * Copyright 2012, 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_RS_REFLECTION_CPP_H_  // NOLINT
#define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_REFLECTION_CPP_H_

#include "slang_rs_reflect_utils.h"

#include <set>
#include <string>

#define RS_EXPORT_VAR_PREFIX "mExportVar_"

namespace slang {

class RSReflectionCpp {
 public:
  RSReflectionCpp(const RSContext *Context, const std::string &OutputDirectory,
                  const std::string &RSSourceFileName,
                  const std::string &BitCodeFileName);
  virtual ~RSReflectionCpp();

  bool reflect();

 private:
  struct Argument {
    std::string Type;
    std::string Name;
    std::string DefaultValue;
    Argument(std::string Type, std::string Name, std::string DefaultValue = "")
      : Type(Type), Name(Name), DefaultValue(DefaultValue) {}
  };
  typedef std::vector<Argument> ArgumentList;

  // Information coming from the compiler about the code we're reflecting.
  const RSContext *mRSContext;

  // Path to the *.rs file for which we're generating C++ code.
  std::string mRSSourceFilePath;
  // Path to the file that contains the byte code generated from the *.rs file.
  std::string mBitCodeFilePath;
  // The directory where we'll generate the C++ files.
  std::string mOutputDirectory;
  // A cleaned up version of the *.rs file name that can be used in generating
  // C++ identifiers.
  std::string mCleanedRSFileName;
  // The name of the generated C++ class.
  std::string mClassName;

  // TODO document
  unsigned int mNextExportVarSlot;
  unsigned int mNextExportFuncSlot;
  unsigned int mNextExportForEachSlot;

  // Generated RS Elements for type-checking code.
  std::set<std::string> mTypesToCheck;

  inline void clear() {
    mNextExportVarSlot = 0;
    mNextExportFuncSlot = 0;
    mNextExportForEachSlot = 0;
    mTypesToCheck.clear();
  }

  // The file we are currently generating, either the header or the
  // implementation file.
  GeneratedFile mOut;

  void genInitValue(const clang::APValue &Val, bool asBool = false);
  static const char *getVectorAccessor(unsigned index);

  inline unsigned int getNextExportVarSlot() { return mNextExportVarSlot++; }

  inline unsigned int getNextExportFuncSlot() { return mNextExportFuncSlot++; }

  inline unsigned int getNextExportForEachSlot() {
    return mNextExportForEachSlot++;
  }

  bool writeHeaderFile();
  bool writeImplementationFile();

  // Write out signatures both in the header and implementation.
  void makeFunctionSignature(bool isDefinition, const RSExportFunc *ef);

  bool genEncodedBitCode();
  void genFieldsToStoreExportVariableValues();
  void genTypeInstancesUsedInForEach();
  void genFieldsForAllocationTypeVerification();

  // Write out the code for the getters and setters.
  void genExportVariablesGetterAndSetter();

  // Write out the code for the declaration of the kernel entry points.
  void genForEachDeclarations();
  void genExportFunctionDeclarations();

  // Write out code for the definitions of the kernel entry points.
  void genExportForEachBodies();
  void genExportFunctionBodies();

  bool startScriptHeader();

  // Write out code for an export variable initialization.
  void genInitExportVariable(const RSExportType *ET, const std::string &VarName,
                             const clang::APValue &Val);
  void genZeroInitExportVariable(const std::string &VarName);
  void genInitBoolExportVariable(const std::string &VarName,
                                 const clang::APValue &Val);
  void genInitPrimitiveExportVariable(const std::string &VarName,
                                      const clang::APValue &Val);

  // Produce an argument string of the form "T1 t, T2 u, T3 v".
  void genArguments(const ArgumentList &Args, int Offset);

  void genPointerTypeExportVariable(const RSExportVar *EV);
  void genMatrixTypeExportVariable(const RSExportVar *EV);
  void genRecordTypeExportVariable(const RSExportVar *EV);

  void genGetterAndSetter(const RSExportPrimitiveType *EPT, const RSExportVar* EV);
  void genGetterAndSetter(const RSExportVectorType *EVT, const RSExportVar* EV);
  void genGetterAndSetter(const RSExportConstantArrayType *AT, const RSExportVar* EV);
  void genGetterAndSetter(const RSExportRecordType *ERT, const RSExportVar *EV);

  // Write out a local FieldPacker (if necessary).
  bool genCreateFieldPacker(const RSExportType *T, const char *FieldPackerName);

  // Populate (write) the FieldPacker with add() operations.
  void genPackVarOfType(const RSExportType *ET, const char *VarName,
                        const char *FieldPackerName);

  // Generate a runtime type check for VarName.
  void genTypeCheck(const RSExportType *ET, const char *VarName);

  // Generate a type instance for a given type.
  void genTypeInstanceFromPointer(const RSExportType *ET);
  void genTypeInstance(const RSExportType *ET);

}; // class RSReflectionCpp

} // namespace slang

#endif // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_REFLECTION_CPP_H_  NOLINT