/**
* @file lspdec.cc
* @brief For createSymMap and other functions specific to Lsp in *dec classes.
* @author Supakorn 'Jamie' Rassameemasmuang (jamievlin at outlook.com)
*/

#include "common.h"
#include "dec.h"


#ifdef HAVE_LSP
#  include "locate.h"
#endif

#define DEC_CREATE_SYM_MAP_FUNCTION_DEF(derived_class)                         \
 void derived_class::createSymMap(AsymptoteLsp::SymbolContext* symContext)


namespace absyntax
{

#ifdef HAVE_LSP
DEC_CREATE_SYM_MAP_FUNCTION_DEF(unraveldec)
{
 std::string fileName= static_cast<std::string>(id->getName());
 if (not AsymptoteLsp::isVirtualFile(fileName)) {
   symContext->extRefs.addUnravelVal(fileName);
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(fromaccessdec)
{
 // filename is id;
 std::string idStr(id);
 symContext->extRefs.addEmptyExtRef(idStr);

 auto* f= this->fields;
 if (f) {
   // add [dest] -> [src, filename] to fromAccessDecls;
   f->processListFn([&symContext,
                     &idStr](symbol const& src, symbol const& dest) {
     std::string srcId(src);
     std::string destId(dest);
     symContext->extRefs.addFromAccessVal(idStr, srcId, destId);
   });
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(importdec) { base.createSymMap(symContext); }

DEC_CREATE_SYM_MAP_FUNCTION_DEF(includedec)
{
 std::string fullname(
         (std::string) settings::locateFile(filename, true).c_str()
 );
 if (not AsymptoteLsp::isVirtualFile(fullname)) {
   symContext->addEmptyExtRef(fullname);
   symContext->extRefs.includeVals.emplace(fullname);
 }
}


DEC_CREATE_SYM_MAP_FUNCTION_DEF(recorddec)
{
 auto* newCtx= symContext->newContext(getPos().LineColumn());
 auto* structTyInfo= symContext->newTypeDec<AsymptoteLsp::StructDecs>(
         static_cast<std::string>(id), getPos().LineColumn()
 );
 if (structTyInfo != nullptr) {
   structTyInfo->ctx= newCtx;
   body->createSymMap(newCtx);
 } else {
   cerr << "Cannot create new struct context" << endl;
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(block)
{
 for (auto const& p : stms) {
   p->createSymMap(symContext);
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(decidstart)
{
 std::string name(static_cast<std::string>(getName()));
 AsymptoteLsp::posInFile pos(getPos().LineColumn());
 if (auto decCtx= dynamic_cast<AsymptoteLsp::AddDeclContexts*>(symContext)) {
   decCtx->additionalDecs.emplace(
           std::piecewise_construct, std::forward_as_tuple(name),
           std::forward_as_tuple(name, pos)
   );
 } else {
   symContext->symMap.varDec[name]= AsymptoteLsp::SymbolInfo(name, pos);
 }
}


void decidstart::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{
 std::string name(static_cast<std::string>(getName()));
 AsymptoteLsp::posInFile pos(getPos().LineColumn());
 if (auto decCtx= dynamic_cast<AsymptoteLsp::AddDeclContexts*>(symContext)) {
   if (base == nullptr) {
     decCtx->additionalDecs.emplace(
             std::piecewise_construct, std::forward_as_tuple(name),
             std::forward_as_tuple(name, pos)
     );
   } else {
     decCtx->additionalDecs.emplace(
             std::piecewise_construct, std::forward_as_tuple(name),
             std::forward_as_tuple(name, static_cast<std::string>(*base), pos)
     );
   }
 } else {
   symContext->symMap.varDec[name]=
           base == nullptr ? AsymptoteLsp::SymbolInfo(name, pos)
                           : AsymptoteLsp::SymbolInfo(
                                     name, static_cast<std::string>(*base), pos
                             );
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(decid)
{
 start->createSymMap(symContext);
 if (init) {
   init->createSymMap(symContext);
 }
}

void decid::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{
 start->createSymMapWType(symContext, base);
 if (init) {
   init->createSymMap(symContext);
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(decidlist)
{
 for (auto const& p : decs) {
   p->createSymMap(symContext);
 }
}

void decidlist::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{
 for (auto const& p : decs) {
   p->createSymMapWType(symContext, base);
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(vardec)
{
 decs->createSymMapWType(symContext, base);
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(idpair)
{
 if (valid) {
   string fullSrc(settings::locateFile(src, true));
   if (not AsymptoteLsp::isVirtualFile((std::string) (fullSrc.c_str()))) {
     if (not fullSrc.empty()) {
       symContext->addEmptyExtRef((std::string) (fullSrc.c_str()));
     }

     // add (dest, source) to reference map.
     auto s= symContext->extRefs.fileIdPair.emplace(
             dest, (std::string) fullSrc.c_str()
     );
     auto it= std::get<0>(s);
     auto success= std::get<1>(s);
     if (not success) {
       it->second= (std::string) (fullSrc.c_str());
     }

     symContext->extRefs.addAccessVal(static_cast<std::string>(dest));
   }
 }
}

DEC_CREATE_SYM_MAP_FUNCTION_DEF(accessdec) { base->createSymMap(symContext); }

DEC_CREATE_SYM_MAP_FUNCTION_DEF(idpairlist)
{
 for (auto& idp : base) {
   idp->createSymMap(symContext);
 }
}

#else

#  define DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(derived_class)                 \
   DEC_CREATE_SYM_MAP_FUNCTION_DEF(derived_class) {}

void decidstart::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{}

void decid::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{}

void decidlist::createSymMapWType(
       AsymptoteLsp::SymbolContext* symContext, absyntax::astType* base
)
{}

DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(unraveldec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(fromaccessdec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(importdec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(includedec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(recorddec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(block)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(decidstart)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(decid)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(decidlist)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(vardec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(idpair)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(accessdec)
DEC_CREATE_SYM_MAP_FUNCTION_DEF_EMPTY(idpairlist)

#endif
}// namespace absyntax