// $Id: Skin.cpp,v 1.29 2004-11-25 02:10:40 geuzaine Exp $
//
// Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
// 
// Please report all bugs and problems to <gmsh@geuz.org>.

#include "Plugin.h"
#include "Skin.h"
#include "List.h"
#include "Tree.h"
#include "Views.h"
#include "Context.h"
#include "Malloc.h"

extern Context_T CTX;

List_T *GMSH_SkinPlugin::_list = NULL;
Tree_T *GMSH_SkinPlugin::_skin = NULL;
int *GMSH_SkinPlugin::_nbList = NULL;
int GMSH_SkinPlugin::_nbNod = 0;
int GMSH_SkinPlugin::_nbComp = 0;
int GMSH_SkinPlugin::_nbTimeStep = 0;

StringXNumber SkinOptions_Number[] = {
  {GMSH_FULLRC, "iView", NULL, -1.}
};

extern "C"
{
  GMSH_Plugin *GMSH_RegisterSkinPlugin()
  {
    return new GMSH_SkinPlugin();
  }
}

GMSH_SkinPlugin::GMSH_SkinPlugin()
{
  ;
}

void GMSH_SkinPlugin::getName(char *name) const
{
  strcpy(name, "Skin");
}

void GMSH_SkinPlugin::getInfos(char *author, char *copyright, char *help_text) const
{
  strcpy(author, "C. Geuzaine (geuz@geuz.org)");
  strcpy(copyright, "DGR (www.multiphysics.com)");
  strcpy(help_text,
         "Plugin(Skin) extracts the skin (the boundary) of\n"
	 "the view `iView'. If `iView' < 0, the plugin is run\n"
	 "on the current view.\n"
	 "\n"
	 "Plugin(Skin) creates one new view.\n");
}

int GMSH_SkinPlugin::getNbOptions() const
{
  return sizeof(SkinOptions_Number) / sizeof(StringXNumber);
}

StringXNumber *GMSH_SkinPlugin::getOption(int iopt)
{
  return &SkinOptions_Number[iopt];
}

void GMSH_SkinPlugin::catchErrorMessage(char *errorMessage) const
{
  strcpy(errorMessage, "Skin failed...");
}

int GMSH_SkinPlugin::fcmpElm(const void *a, const void *b)
{
  Elm *e1 = (Elm *)a, *e2 = (Elm *)b;
  double s1, s2, TOL = CTX.lc * 1.e-12;
  int i;

  s1 = s2 = 0.0;
  for(i = 0; i < _nbNod; i++) {
    s1 += e1->coord[i];
    s2 += e2->coord[i];
  }
  if(s1 - s2 > TOL)
    return 1;
  else if(s1 - s2 < -TOL)
    return -1;
  s1 = s2 = 0.0;
  for(i = 0; i < _nbNod; i++) {
    s1 += e1->coord[_nbNod + i];
    s2 += e2->coord[_nbNod + i];
  }
  if(s1 - s2 > TOL)
    return 1;
  else if(s1 - s2 < -TOL)
    return -1;
  s1 = s2 = 0.0;
  for(i = 0; i < _nbNod; i++) {
    s1 += e1->coord[2 * _nbNod + i];
    s2 += e2->coord[2 * _nbNod + i];
  }
  if(s1 - s2 > TOL)
    return 1;
  else if(s1 - s2 < -TOL)
    return -1;

  return 0;
}

void GMSH_SkinPlugin::addInView(void *a, void *b)
{
  Elm *e = (Elm *)a;

  for(int i = 0; i < 3 * _nbNod; i++)
    List_Add(_list, &e->coord[i]);
  for(int ts = 0; ts < _nbTimeStep; ts++)
    for(int k = 0; k < _nbNod * _nbComp; k++)
      List_Add(_list, &e->val[_nbNod * _nbComp * ts + k]);
  Free(e->val);
  (*_nbList)++;
}

void GMSH_SkinPlugin::skinList(List_T *inList, int inNbList, 
			       int inNbNod, int inNbFac, int fxn[6][4])
{
  if(!inNbList)
    return;

  int nb = List_Nbr(inList) / inNbList;
  for(int i = 0; i < List_Nbr(inList); i += nb) {
    double *coord = (double *)List_Pointer_Fast(inList, i);
    double *val = (double *)List_Pointer_Fast(inList, i + 3 * inNbNod);
    for(int j = 0; j < inNbFac; j++) {
      Elm e, *pe;
      for(int k = 0; k < _nbNod; k++) {
	e.coord[k] = coord[fxn[j][k]]; // x
	e.coord[_nbNod + k] = coord[inNbNod + fxn[j][k]]; // y
	e.coord[2 * _nbNod + k] = coord[2 * inNbNod + fxn[j][k]]; // z
      }
      if(!(pe = (Elm *)Tree_PQuery(_skin, &e))) {
	e.val = (double *)Malloc(_nbNod * _nbComp * _nbTimeStep * sizeof(double));
	for(int k = 0; k < _nbNod; k++)
	  for(int ts = 0; ts < _nbTimeStep; ts++)
	    for(int n = 0; n < _nbComp; n++)
	      e.val[_nbNod * _nbComp * ts + _nbComp * k + n] =
		val[inNbNod * _nbComp * ts + _nbComp * fxn[j][k] + n];
        Tree_Add(_skin, &e);
      }
      else {
        Free(pe->val);
        Tree_Suppress(_skin, pe);
      }
    }
  }
}

Post_View *GMSH_SkinPlugin::execute(Post_View * v)
{
  int iView = (int)SkinOptions_Number[0].def;

  if(iView < 0)
    iView = v ? v->Index : 0;

  if(!List_Pointer_Test(CTX.post.list, iView)) {
    Msg(GERROR, "View[%d] does not exist", iView);
    return v;
  }

  Post_View *v1 = *(Post_View **)List_Pointer(CTX.post.list, iView);
  Post_View *v2 = BeginView(1);

  _nbTimeStep = v1->NbTimeStep;

  int skinTri[6][4] = {{0,1,-1,-1}, {1,2,-1,-1}, {2,0,-1,-1}};
  int skinQua[6][4] = {{0,1,-1,-1}, {1,2,-1,-1}, {2,3,-1,-1}, {3,0,-1,-1}};
  int skinTet[6][4] = {{0,1,3,-1}, {0,2,1,-1}, {0,3,2,-1}, {1,2,3,-1}};
  int skinHex[6][4] = {{0,1,5,4}, {0,3,2,1}, {0,4,7,3},
		       {1,2,6,5}, {2,3,7,6}, {4,5,6,7}};
  int skinPri1[6][4] = {{0,1,4,3}, {0,3,5,2}, {1,2,5,4}};
  int skinPri2[6][4] = {{0,2,1,-1}, {3,4,5,-1}};
  int skinPyr1[6][4] = {{0,3,2,1}};
  int skinPyr2[6][4] = {{0,1,4,-1}, {0,4,3,-1}, {1,2,4,-1}, {2,3,4,-1}};

  // Generate lines
  _nbNod = 2;
  // scalar
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->SL; _nbList = &v2->NbSL; _nbComp = 1;
  skinList(v1->ST, v1->NbST, 3, 3, skinTri);
  skinList(v1->SQ, v1->NbSQ, 4, 4, skinQua);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // vector
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->VL; _nbList = &v2->NbVL; _nbComp = 3;
  skinList(v1->VT, v1->NbVT, 3, 3, skinTri);
  skinList(v1->VQ, v1->NbVQ, 4, 4, skinQua);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // tensor
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->TL; _nbList = &v2->NbTL; _nbComp = 9;
  skinList(v1->TT, v1->NbTT, 3, 3, skinTri);
  skinList(v1->TQ, v1->NbTQ, 4, 4, skinQua);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);

  // Generate triangles
  _nbNod = 3;
  // scalar
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->ST; _nbList = &v2->NbST; _nbComp = 1;
  skinList(v1->SS, v1->NbSS, 4, 4, skinTet);
  skinList(v1->SI, v1->NbSI, 6, 2, skinPri2);
  skinList(v1->SY, v1->NbSY, 5, 4, skinPyr2);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // vector
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->VT; _nbList = &v2->NbVT; _nbComp = 3;
  skinList(v1->VS, v1->NbVS, 4, 4, skinTet);
  skinList(v1->VI, v1->NbVI, 6, 2, skinPri2);
  skinList(v1->VY, v1->NbVY, 5, 4, skinPyr2);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // tensor
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->TT; _nbList = &v2->NbTT; _nbComp = 9;
  skinList(v1->TS, v1->NbTS, 4, 4, skinTet);
  skinList(v1->TI, v1->NbTI, 6, 2, skinPri2);
  skinList(v1->TY, v1->NbTY, 5, 4, skinPyr2);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);

  // Generate quads
  _nbNod = 4;
  // scalar
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->SQ; _nbList = &v2->NbSQ; _nbComp = 1;
  skinList(v1->SH, v1->NbSH, 8, 6, skinHex);
  skinList(v1->SI, v1->NbSI, 6, 3, skinPri1);
  skinList(v1->SY, v1->NbSY, 5, 1, skinPyr1);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // vector
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->VQ; _nbList = &v2->NbVQ; _nbComp = 3;
  skinList(v1->VH, v1->NbVH, 8, 6, skinHex);
  skinList(v1->VI, v1->NbVI, 6, 3, skinPri1);
  skinList(v1->VY, v1->NbVY, 5, 1, skinPyr1);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);
  // tensor
  _skin = Tree_Create(sizeof(Elm), fcmpElm);
  _list = v2->TQ; _nbList = &v2->NbTQ; _nbComp = 9;
  skinList(v1->TH, v1->NbTH, 8, 6, skinHex);
  skinList(v1->TI, v1->NbTI, 6, 3, skinPri1);
  skinList(v1->TY, v1->NbTY, 5, 1, skinPyr1);
  Tree_Action(_skin, addInView);
  Tree_Delete(_skin);

  // copy time data
  for(int i = 0; i < List_Nbr(v1->Time); i++)
    List_Add(v2->Time, List_Pointer(v1->Time, i));
  // finalize
  char name[1024], filename[1024];
  sprintf(name, "%s_Skin", v1->Name);
  sprintf(filename, "%s_Skin.pos", v1->Name);
  EndView(v2, 1, filename, name);
  return v2;
}