// genDElement - An abstract data element library
// Copyright (C) 2013-2026 Eric Bechet, Frederic Duboeuf
//
// See the LICENSE file for license information.
// Please report all bugs and problems to <bechet@cadxfem.org> or <duboeuf@outlook.com>.
//
// Initial design: Frederic Duboeuf (rev.1511)


#include "genDataParentSupport.h"
#include <assert.h>
#include "MTriangle.h"
#include "genDataConnectivity.h"

// return true if the parent support of de2 is contained in de1
template<class E> struct DElementPolicies
{
  static bool isInside(DElementBase<E>* de1, DElementBase<E>* de2)
  {
    DataParentSupport<E>* data2;
    de2->getDatas()->getData("ParentSupport",data2);
    assert(data2);
    DElementBase<E>* support=data2->getParentSupport();

    if (support==de1)
      return true;

    DataConnectivity<E>* data1;
    de1->getDatas()->getData("Connectivity",data1);
    assert(data1);

    for (int dim=2; dim>-1; --dim)
    {
      for (int i=0; i<data1->getNumComponents(dim); ++i)
      {
        DElementBase<E>* dcomp;
        dcomp = data1->getComponent(dim,i);
        if (support==dcomp)
          return true;
      }
    }
    return false;
  }
};



template<class E1,class E2> struct ElementPoliciesBinary
{
  static bool isInside(E1* e1, E2* e2) {std::cout << "ElementPoliciesBinary not available for this class" << std::endl; return false;}
};

template<> struct ElementPoliciesBinary<MElement,MVertex>
{
  static bool isInside(MElement* e, MVertex* v)
  {
    double xyz[3];
    xyz[0]=v->x(); xyz[1]=v->y(); xyz[2]=v->z();
    double uvw[3];
    e->xyz2uvw(xyz,uvw);
    return e->isInside(uvw[0], uvw[1], uvw[2]);
  }
};

template<> struct ElementPoliciesBinary<MVertex,MVertex>
{
  static bool isInside(MVertex* v1, MVertex* v2) {return (v1==v2) ? true : false;}
};

template<> struct ElementPoliciesBinary<MEdge,MVertex>
{
  static bool isInside(MEdge* e, MVertex* v) {return e->isInside(v);}
};

template<> struct ElementPoliciesBinary<MFace,MVertex>
{
  static bool isInside(MFace* f, MVertex* v) {assert(f->getNumVertices()==3); MElement* t=new MTriangle(f->getVertex(0),f->getVertex(1),f->getVertex(2)); return ElementPoliciesBinary<MElement,MVertex>::isInside(t,v);}
};



template<class E> void DataParentSupport<E>::buildVertexSupports(DElementBase<E>* de, DElementBase<E>* parent)
{
  if(de->getDim()==0)
  {
    if(parentSupport) return;

    genDataGroup* pdataGroup;
    DataConnectivity<E>* pdata;
    pdataGroup = parent->getDatas();
    pdataGroup->getData("Connectivity",pdata);
    assert(pdata);

    VertexType* v = de->getVertex(0);

    // is a parent vertex
    for (int i=0; i<pdata->getNumComponents(0); ++i)
    {
      DElementBase<E>* dvert;
      dvert = pdata->getComponent(0,i);
      VertexType* vert = dvert->getVertex(0);
      if(ElementPoliciesBinary<VertexType,VertexType>::isInside(vert,v))
      {
        parentSupport=dvert;
        return;
      }
    }
    // is on a parent edge
    for (int i=0; i<pdata->getNumComponents(1); ++i)
    {
      DElementBase<E>* dedge;
      dedge = pdata->getComponent(1,i);
      EdgeType* edge = dedge->getEdge(0);
      if(ElementPoliciesBinary<EdgeType,VertexType>::isInside(edge,v))
      {
        parentSupport=dedge;
        return;
      }
    }
    // is on a parent face
    for (int i=0; i<pdata->getNumComponents(2); ++i)
    {
      DElementBase<E>* dface;
      dface = pdata->getComponent(2,i);
      FaceType* face = dface->getFace(0);
      if(ElementPoliciesBinary<FaceType,VertexType>::isInside(face,v))
      {
        parentSupport=dface;
        return;
      }
    }
    // is in the parent volume
    parentSupport=parent; return;
  }
  else
  {
    KeyManagerBase<E>* keyManager = KeyManagerBase<E>::getInstance();
    typename KeyManagerBase<E>::KeyContainer::const_iterator it;
    int nV;
    Key key;

    nV = de->getNumVertices();
    VertexType* vert;
    DElementBase<E>* Dvert;
    std::string s = list->getIndex();
    for(int i=0; i<nV; ++i)
    {
      vert = de->getVertex(i);
      ElementPolicies<VertexType>::getKey(vert,key);
      it = keyManager->find(key);
      if (it==keyManager->end())
        Dvert = new MDElement<VertexType,E>(vert);
      else
        Dvert = it->second;
      genDataGroup* dataGroup;
      DataParentSupport<E>* data;
      dataGroup = Dvert->getDatas();
      dataGroup->getData(s.c_str(),data);
      if (!data)
      {
        data = new DataParentSupport<E>(s.c_str(),Dvert);
        dataGroup->setData(s,data);
        data->buildSupports(Dvert,parent);
      }
    }
  }
}


template<class E> void DataParentSupport<E>::buildEdgeSupports(DElementBase<E>* de, DElementBase<E>* parent)
{
  if(de->getDim()==1)
  {
    if(parentSupport) return;

    genDataGroup* dataGroup;
    DataConnectivity<E>* data;
    dataGroup = de->getDatas();
    dataGroup->getData("SubConnectivity",data);
    assert(data);

    genDataGroup* pdataGroup;
    DataConnectivity<E>* pdata;
    pdataGroup = parent->getDatas();
    pdataGroup->getData("Connectivity",pdata);
    assert(pdata);

    assert(data->getNumComponents(0)==2);
    std::string s = list->getIndex();
    DElementBase<E>* dv0 = data->getComponent(0,0);
    DElementBase<E>* dv1 = data->getComponent(0,1);

    // is on a parent edge
    for (int i=0; i<pdata->getNumComponents(1); ++i)
    {
      DElementBase<E>* dedge;
      dedge = pdata->getComponent(1,i);

      if ( DElementPolicies<E>::isInside(dedge,dv0) && DElementPolicies<E>::isInside(dedge,dv1) )
      {
        parentSupport=dedge;
        return;
      }
    }
    // is on a parent face
    for (int i=0; i<pdata->getNumComponents(2); ++i)
    {
      DElementBase<E>* dface;
      dface = pdata->getComponent(2,i);

      if ( DElementPolicies<E>::isInside(dface,dv0) && DElementPolicies<E>::isInside(dface,dv1) )
      {
        parentSupport=dface;
        return;
      }
    }
    // is in the parent volume
    parentSupport=parent; return;
  }
  else
  {
    KeyManagerBase<E>* keyManager = KeyManagerBase<E>::getInstance();
    typename KeyManagerBase<E>::KeyContainer::const_iterator it;
    int nE;
    Key key;

    nE = de->getNumEdges();
    EdgeType* edge;
    DElementBase<E>* Dedge;
    std::string s = list->getIndex();
    for(int i=0; i<nE; ++i)
    {
      edge = de->getEdge(i);
      ElementPolicies<EdgeType>::getKey(edge,key);
      it = keyManager->find(key);
      if (it==keyManager->end())
        Dedge = new MDElement<EdgeType,E>(edge);
      else
        Dedge = it->second;
      genDataGroup* dataGroup;
      DataParentSupport<E>* data;
      dataGroup = Dedge->getDatas();
      dataGroup->getData(s.c_str(),data);
      if (!data)
      {
        data = new DataParentSupport<E>(s.c_str(),Dedge);
        dataGroup->setData(s,data);
        data->buildSupports(Dedge,parent);
      }
    }
  }
}

template<class E> void DataParentSupport<E>::buildFaceSupports(DElementBase<E>* de, DElementBase<E>* parent)
{
  if(de->getDim()==2)
  {
    if(parentSupport) return;

    genDataGroup* dataGroup;
    DataConnectivity<E>* data;
    dataGroup = de->getDatas();
    dataGroup->getData("SubConnectivity",data);
    assert(data);

    genDataGroup* pdataGroup;
    DataConnectivity<E>* pdata;
    pdataGroup = parent->getDatas();
    pdataGroup->getData("Connectivity",pdata);
    assert(pdata);

    assert(data->getNumComponents(0)==3);
    std::string s = list->getIndex();
    DElementBase<E>* dv0 = data->getComponent(0,0);
    DElementBase<E>* dv1 = data->getComponent(0,1);
    DElementBase<E>* dv2 = data->getComponent(0,2);

    // is on a parent face
    for (int i=0; i<pdata->getNumComponents(2); ++i)
    {
      DElementBase<E>* dface;
      dface = pdata->getComponent(2,i);

      if ( ( DElementPolicies<E>::isInside(dface,dv0) && DElementPolicies<E>::isInside(dface,dv1) ) && DElementPolicies<E>::isInside(dface,dv2) )
      {
        parentSupport=dface;
        return;
      }
    }
    // is in the parent volume
    parentSupport=parent; return;
  }
  else
  {
    KeyManagerBase<E>* keyManager = KeyManagerBase<E>::getInstance();
    typename KeyManagerBase<E>::KeyContainer::const_iterator it;
    int nF;
    Key key;

    nF = de->getNumFaces();
    FaceType* face;
    DElementBase<E>* Dface;
    std::string s = list->getIndex();
    for(int i=0; i<nF; ++i)
    {
      face = de->getFace(i);
      ElementPolicies<FaceType>::getKey(face,key);
      it = keyManager->find(key);
      if (it==keyManager->end())
        Dface = new MDElement<FaceType,E>(face);
      else
        Dface = it->second;
      genDataGroup* dataGroup;
      DataParentSupport<E>* data;
      dataGroup = Dface->getDatas();
      dataGroup->getData(s.c_str(),data);
      if (!data)
      {
        data = new DataParentSupport<E>(s.c_str(),Dface);
        dataGroup->setData(s,data);
        data->buildSupports(Dface,parent);
      }
    }
  }
}

template<class E> void DataParentSupport<E>::buildVolumeSupports(DElementBase<E>* de, DElementBase<E>* parent)
{
  if(de->getDim()==3)
  {
    if(parentSupport) return;

    // is in the parent volume
    parentSupport=parent; return;
  }
}
