/*
 * Copyright 2015, 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_EXPORT_REDUCE_H_  // NOLINT
#define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_EXPORT_REDUCE_H_

#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallVector.h"

#include "slang_rs_context.h"
#include "slang_rs_exportable.h"
#include "slang_rs_export_type.h"

namespace clang {
  class FunctionDecl;
}  // namespace clang

namespace slang {

// Base class for reflecting control-side reduce
class RSExportReduce : public RSExportable {
 public:
  typedef llvm::SmallVectorImpl<const clang::ParmVarDecl*> InVec;
  typedef llvm::SmallVectorImpl<const RSExportType*> InTypeVec;

  typedef InVec::const_iterator InIter;
  typedef InTypeVec::const_iterator InTypeIter;

 private:
  // pragma location (for error reporting)
  clang::SourceLocation mLocation;

  // reduction kernel name
  std::string mNameReduce;

  // constituent function names
  std::string mNameInitializer;
  std::string mNameAccumulator;
  std::string mNameCombiner;
  std::string mNameOutConverter;
  std::string mNameHalter;

  // constituent function identity
  enum FnIdent {
    FN_IDENT_INITIALIZER,
    FN_IDENT_ACCUMULATOR,
    FN_IDENT_COMBINER,
    FN_IDENT_OUT_CONVERTER,
    FN_IDENT_HALTER
  };
  static const char *getKey(FnIdent Kind);

  // signature information for accumulator function
  unsigned int mAccumulatorSignatureMetadata;

  // size of accumulator data type (compType), in bytes
  unsigned int mAccumulatorTypeSize;

  // input information for accumulator function
  static const int kAccumulatorInsSmallSize = 4;
  llvm::SmallVector<const clang::ParmVarDecl*, kAccumulatorInsSmallSize> mAccumulatorIns;
  llvm::SmallVector<const RSExportType*, kAccumulatorInsSmallSize> mAccumulatorInTypes;

  // result information
  RSExportType *mResultType;

  RSExportReduce(RSContext *Context,
                 const clang::SourceLocation Location,
                 const llvm::StringRef &NameReduce,
                 const llvm::StringRef &NameInitializer,
                 const llvm::StringRef &NameAccumulator,
                 const llvm::StringRef &NameCombiner,
                 const llvm::StringRef &NameOutConverter,
                 const llvm::StringRef &NameHalter)
    : RSExportable(Context, RSExportable::EX_REDUCE),
      mLocation(Location),
      mNameReduce(NameReduce),
      mNameInitializer(NameInitializer),
      mNameAccumulator(NameAccumulator),
      mNameCombiner(NameCombiner),
      mNameOutConverter(NameOutConverter),
      mNameHalter(NameHalter),
      mAccumulatorSignatureMetadata(0),
      mAccumulatorTypeSize(0),
      mResultType(nullptr) {
  }

  RSExportReduce(const RSExportReduce &) = delete;
  void operator=(const RSExportReduce &) = delete;

  struct StateOfAnalyzeTranslationUnit;

  static void notOk(StateOfAnalyzeTranslationUnit &S, FnIdent Kind);

  static void checkPointeeConstQualified(StateOfAnalyzeTranslationUnit &S,
                                         FnIdent Kind, const llvm::StringRef &Name,
                                         const clang::ParmVarDecl *Param, bool ExpectedQualification);

  static void checkVoidReturn(StateOfAnalyzeTranslationUnit &S, FnIdent Kind, clang::FunctionDecl *Fn);

  clang::FunctionDecl *lookupFunction(StateOfAnalyzeTranslationUnit &S,
                                      const char *Kind, const llvm::StringRef &Name);

  void analyzeInitializer(StateOfAnalyzeTranslationUnit &S);
  void analyzeAccumulator(StateOfAnalyzeTranslationUnit &S);
  void analyzeCombiner(StateOfAnalyzeTranslationUnit &S);
  void analyzeOutConverter(StateOfAnalyzeTranslationUnit &S);
  void analyzeHalter(StateOfAnalyzeTranslationUnit &S);
  void analyzeResultType(StateOfAnalyzeTranslationUnit &S);

 public:

  static const char KeyReduce[];
  static const char KeyInitializer[];
  static const char KeyAccumulator[];
  static const char KeyCombiner[];
  static const char KeyOutConverter[];
  static const char KeyHalter[];

  static RSExportReduce *Create(RSContext *Context,
                                const clang::SourceLocation Location,
                                const llvm::StringRef &NameReduce,
                                const llvm::StringRef &NameInitializer,
                                const llvm::StringRef &NameAccumulator,
                                const llvm::StringRef &NameCombiner,
                                const llvm::StringRef &NameOutConverter,
                                const llvm::StringRef &NameHalter);

  const clang::SourceLocation &getLocation() const { return mLocation; }

  const std::string &getNameReduce() const { return mNameReduce; }
  const std::string &getNameInitializer() const { return mNameInitializer; }
  const std::string &getNameAccumulator() const { return mNameAccumulator; }
  const std::string &getNameCombiner() const { return mNameCombiner; }
  const std::string &getNameOutConverter() const { return mNameOutConverter; }
  const std::string &getNameHalter() const { return mNameHalter; }

  unsigned int getAccumulatorSignatureMetadata() const { return mAccumulatorSignatureMetadata; }

  unsigned int getAccumulatorTypeSize() const { return mAccumulatorTypeSize; }

  const InVec &getAccumulatorIns() const { return mAccumulatorIns; }
  const InTypeVec &getAccumulatorInTypes() const { return mAccumulatorInTypes; }

  const RSExportType *getResultType() const { return mResultType; }

  // Does one of this reduction's constituent function names match Candidate?
  bool matchName(const llvm::StringRef &Candidate) const;

  bool analyzeTranslationUnit();
};  // RSExportReduce

}  // namespace slang

#endif  // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_EXPORT_REDUCE_H_  NOLINT