/*
 * 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 BCC_RS_INFO_H
#define BCC_RS_INFO_H

#include <stdint.h>

#include <utility>

#include "bcc/Renderscript/RSScript.h"
#include "bcc/Support/Log.h"
#include "bcc/Support/Sha1Util.h"

#include <utils/String8.h>
#include <utils/Vector.h>

namespace bcc {

// Forward declarations
class FileBase;
class InputFile;
class OutputFile;
class Source;

namespace rsinfo {

/* RS info file magic */
#define RSINFO_MAGIC      "\0rsinfo\n"

/* RS info file version, encoded in 4 bytes of ASCII */
#define RSINFO_VERSION    "003\0"

struct __attribute__((packed)) ListHeader {
  // The offset from the beginning of the file of data
  uint32_t offset;
  // Number of item in the list
  uint32_t count;
  // Size of each item
  uint8_t itemSize;
};

/* RS info file header */
struct __attribute__((packed)) Header {
  // Magic versus version
  uint8_t magic[8];
  uint8_t version[4];

  uint8_t isThreadable;
  uint8_t hasDebugInformation;

  uint16_t headerSize;

  uint32_t strPoolSize;

  struct ListHeader dependencyTable;
  struct ListHeader pragmaList;
  struct ListHeader objectSlotList;
  struct ListHeader exportVarNameList;
  struct ListHeader exportFuncNameList;
  struct ListHeader exportForeachFuncList;
};

typedef uint32_t StringIndexTy;
// Use value -1 as an invalid string index marker. No need to declare with
// 'static' modifier since 'const' variable has internal linkage by default.
const StringIndexTy gInvalidStringIndex = static_cast<StringIndexTy>(-1);

struct __attribute__((packed)) DependencyTableItem {
  StringIndexTy id;
  // SHA-1 checksum is stored as a string in string pool (and has fixed-length
  // SHA1_DIGEST_LENGTH (=20) bytes)
  StringIndexTy sha1;
};

struct __attribute__((packed)) PragmaItem {
  // Pragma is a key-value pair.
  StringIndexTy key;
  StringIndexTy value;
};

struct __attribute__((packed)) ObjectSlotItem {
  uint32_t slot;
};

struct __attribute__((packed)) ExportVarNameItem {
  StringIndexTy name;
};

struct __attribute__((packed)) ExportFuncNameItem {
  StringIndexTy name;
};

struct __attribute__((packed)) ExportForeachFuncItem {
  StringIndexTy name;
  uint32_t signature;
};

// Return the human-readable name of the given rsinfo::*Item in the template
// parameter. This is for debugging and error message.
template<typename Item>
inline const char *GetItemTypeName();

template<>
inline const char *GetItemTypeName<DependencyTableItem>()
{ return "rs dependency info"; }

template<>
inline const char *GetItemTypeName<PragmaItem>()
{  return "rs pragma"; }

template<>
inline const char *GetItemTypeName<ObjectSlotItem>()
{  return "rs object slot"; }

template<>
inline const char *GetItemTypeName<ExportVarNameItem>()
{ return "rs export var"; }

template<>
inline const char *GetItemTypeName<ExportFuncNameItem>()
{  return "rs export func"; }

template<>
inline const char *GetItemTypeName<ExportForeachFuncItem>()
{ return "rs export foreach"; }

} // end namespace rsinfo

class RSInfo {
public:
  typedef android::Vector<std::pair<const char *,
                                    const uint8_t *> > DependencyTableTy;
  typedef android::Vector<std::pair<const char*, const char*> > PragmaListTy;
  typedef android::Vector<uint32_t> ObjectSlotListTy;
  typedef android::Vector<const char *> ExportVarNameListTy;
  typedef android::Vector<const char *> ExportFuncNameListTy;
  typedef android::Vector<std::pair<const char *,
                                    uint32_t> > ExportForeachFuncListTy;

public:
  // Calculate or load the SHA-1 information of the built-in dependencies.
  static void LoadBuiltInSHA1Information();

  // Return the path of the RS info file corresponded to the given output
  // executable file.
  static android::String8 GetPath(const FileBase &pFile);

  static const char LibBCCPath[];
  static const char LibRSPath[];
  static const char LibCLCorePath[];
#if defined(ARCH_ARM_HAVE_NEON)
  static const char LibCLCoreNEONPath[];
#endif

private:
  // SHA-1 of the built-in dependencies. Will be initialized in
  // LoadBuiltInSHA1Information().
  static const uint8_t *LibBCCSHA1;
  static const uint8_t *LibRSSHA1;
  static const uint8_t *LibCLCoreSHA1;
#if defined(ARCH_ARM_HAVE_NEON)
  static const uint8_t *LibCLCoreNEONSHA1;
#endif

  static bool CheckDependency(const RSInfo &pInfo,
                              const char *pInputFilename,
                              const DependencyTableTy &pDeps);
  static bool AddBuiltInDependencies(RSInfo &pInfo);

  rsinfo::Header mHeader;

  char *mStringPool;

  // In most of the time, there're 4 source dependencies stored (libbcc.so,
  // libRS.so, libclcore and the input bitcode itself.)
  DependencyTableTy mDependencyTable;
  PragmaListTy mPragmas;
  ObjectSlotListTy mObjectSlots;
  ExportVarNameListTy mExportVarNames;
  ExportFuncNameListTy mExportFuncNames;
  ExportForeachFuncListTy mExportForeachFuncs;

  // Initialize an empty RSInfo with its size of string pool is pStringPoolSize.
  RSInfo(size_t pStringPoolSize);

  // layout() assigns value of offset in each ListHeader (i.e., it decides where
  // data should go in the file.) It also updates fields other than offset to
  // reflect the current RSInfo object states to mHeader.
  bool layout(off_t initial_offset);

public:
  ~RSInfo();

  // Implemented in RSInfoExtractor.cpp.
  static RSInfo *ExtractFromSource(const Source &pSource,
                                   const DependencyTableTy &pDeps);

  // Implemented in RSInfoReader.cpp.
  static RSInfo *ReadFromFile(InputFile &pInput,
                              const DependencyTableTy &pDeps);

  // Implemneted in RSInfoWriter.cpp
  bool write(OutputFile &pOutput);

  void dump() const;

  // const getter
  inline bool isThreadable() const
  { return mHeader.isThreadable; }
  inline bool hasDebugInformation() const
  { return mHeader.hasDebugInformation; }
  inline const DependencyTableTy &getDependencyTable() const
  { return mDependencyTable; }
  inline const PragmaListTy &getPragmas() const
  { return mPragmas; }
  inline const ObjectSlotListTy &getObjectSlots() const
  { return mObjectSlots; }
  inline const ExportVarNameListTy &getExportVarNames() const
  { return mExportVarNames; }
  inline const ExportFuncNameListTy &getExportFuncNames() const
  { return mExportFuncNames; }
  inline const ExportForeachFuncListTy &getExportForeachFuncs() const
  { return mExportForeachFuncs; }

  const char *getStringFromPool(rsinfo::StringIndexTy pStrIdx) const;
  rsinfo::StringIndexTy getStringIdxInPool(const char *pStr) const;

  // setter
  inline void setThreadable(bool pThreadable = true)
  { mHeader.isThreadable = pThreadable; }

public:
  enum FloatPrecision {
    FP_Full,
    FP_Relaxed,
    FP_Imprecise,
  };

  // Return the minimal floating point precision required for the associated
  // script.
  FloatPrecision getFloatPrecisionRequirement() const;
};

} // end namespace bcc

#endif  // BCC_RS_INFO_H