/*
 * 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.
 */

#include <sys/stat.h>

#include <cstdarg>
#include <cctype>

#include <algorithm>
#include <limits>
#include <sstream>
#include <string>
#include <utility>

#include "os_sep.h"
#include "slang_rs_context.h"
#include "slang_rs_export_var.h"
#include "slang_rs_export_foreach.h"
#include "slang_rs_export_func.h"
#include "slang_rs_reflect_utils.h"
#include "slang_version.h"
#include "slang_utils.h"

#include "slang_rs_reflection_base.h"



using namespace std;

namespace slang {

static const char *const gApacheLicenseNote =
"/*\n"
" * Copyright (C) 2012 The Android Open Source Project\n"
" *\n"
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
" * you may not use this file except in compliance with the License.\n"
" * You may obtain a copy of the License at\n"
" *\n"
" *      http://www.apache.org/licenses/LICENSE-2.0\n"
" *\n"
" * Unless required by applicable law or agreed to in writing, software\n"
" * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
" * See the License for the specific language governing permissions and\n"
" * limitations under the License.\n"
" */\n"
"\n";


RSReflectionBase::RSReflectionBase(const RSContext *con)
  : mVerbose(true) {
  mRSContext = con;
  mLicenseNote = gApacheLicenseNote;

}

RSReflectionBase::~RSReflectionBase() {

}

/*
bool RSReflectionBase::openFile(const string &name, string &errorMsg) {
    if(!mUseStdout) {
        mOF.clear();
        if(!SlangUtils::CreateDirectoryWithParents(mOutputPath, &errorMsg)) {
            return false;
        }

        string cf(mOutputPath + OS_PATH_SEPARATOR_STR + name);
        mOF.open(cf.c_str());
        if(!mOF.good()) {
            errorMsg = "failed to open file '" + cf + "' for write";
            return false;
        }
    }
    return true;
}
*/

void RSReflectionBase::startFile(const string &filename) {
  if(mVerbose) {
    printf("Generating %s\n", filename.c_str());
  }

  // License
  write(mLicenseNote);

  // Notice of generated file
  write("/*");
  write(" * This file is auto-generated. DO NOT MODIFY!");
  write(" * The source Renderscript file: " + mInputFileName);
  write(" */");
  write("");
}

// remove path plus .rs from filename to generate class name
string RSReflectionBase::stripRS(const string &s) const {
  string tmp(s);
  size_t pos = tmp.rfind(".rs");
  if(pos != string::npos) {
    tmp.erase(pos);
  }
  pos = tmp.rfind("/");
  if (pos != string::npos) {
    tmp.erase(0, pos+1);
  }
  return tmp;
}

void RSReflectionBase::write(const std::string &t) {
  //printf("%s%s\n", mIndent.c_str(), t.c_str());
  mText.push_back(mIndent + t);
}

void RSReflectionBase::write(const std::stringstream &t) {
  mText.push_back(mIndent + t.str());
}


void RSReflectionBase::incIndent() {
  mIndent.append("    ");
}

void RSReflectionBase::decIndent() {
  mIndent.erase(0, 4);
}

bool RSReflectionBase::writeFile(const string &filename, const vector< string > &txt) {
  FILE *pfin = fopen((mOutputPath + filename).c_str(), "wt");
  if (pfin == NULL) {
    fprintf(stderr, "Error: could not write file %s\n", filename.c_str());
    return false;
  }

  for(size_t ct=0; ct < txt.size(); ct++) {
    fprintf(pfin, "%s\n", txt[ct].c_str());
  }
  fclose(pfin);
  return true;
}


string RSReflectionBase::genInitValue(const clang::APValue &Val, bool asBool) {
  stringstream tmp;
  switch (Val.getKind()) {
    case clang::APValue::Int: {
      llvm::APInt api = Val.getInt();
      if(asBool) {
        tmp << ((api.getSExtValue() == 0) ? "false" : "true");
      } else {
        // TODO: Handle unsigned possibly for C++ API.
        tmp << api.getSExtValue();
        if (api.getBitWidth() > 32) {
          tmp << "L";
        }
      }
      break;
    }

    case clang::APValue::Float: {
      llvm::APFloat apf = Val.getFloat();
      llvm::SmallString<30> s;
      apf.toString(s);
      tmp << s.c_str();
      if (&apf.getSemantics() == &llvm::APFloat::IEEEsingle) {
        if (s.count('.') == 0) {
          tmp << ".f";
        } else {
          tmp << "f";
        }
      }
      break;
    }

    case clang::APValue::ComplexInt:
    case clang::APValue::ComplexFloat:
    case clang::APValue::LValue:
    case clang::APValue::Vector: {
      slangAssert(false && "Primitive type cannot have such kind of initializer");
      break;
    }

    default: {
      slangAssert(false && "Unknown kind of initializer");
    }
  }
  return tmp.str();
}

bool RSReflectionBase::addTypeNameForElement(
    const std::string &TypeName) {
  if (mTypesToCheck.find(TypeName) == mTypesToCheck.end()) {
    mTypesToCheck.insert(TypeName);
    return true;
  } else {
    return false;
  }
}

const char *RSReflectionBase::getVectorAccessor(unsigned Index) {
  static const char *VectorAccessorMap[] = {
    /* 0 */ "x",
    /* 1 */ "y",
    /* 2 */ "z",
    /* 3 */ "w",
  };

  slangAssert((Index < (sizeof(VectorAccessorMap) / sizeof(const char*))) &&
              "Out-of-bound index to access vector member");

  return VectorAccessorMap[Index];
}

}