// $Id: ViewsIO.cpp,v 1.4 2006/01/29 20:32:48 geuzaine Exp $
//
// Copyright (C) 1997-2006 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 <set>
#include "Gmsh.h"
#include "Numeric.h"
#include "Views.h"
#include "Context.h"

extern Context_T CTX;

#if defined(HAVE_FLTK)
void UpdateViewsInGUI();
#endif

// Read view from file

void ReadView(FILE *file, char *filename)
{
  char str[256], name[256];
  int i, nb, format, size, testone, swap, t2l, t3l;
  double version;
  Post_View *v;

  Msg(INFO, "Reading post-processing file '%s'", filename);

  while(1) {

    do {
      if(!fgets(str, 256, file))
	break;
      if(feof(file))
        break;
    } while(str[0] != '$');

    if(feof(file))
      break;

    if(!strncmp(&str[1], "PostFormat", 10)) {
      if(!fscanf(file, "%lf %d %d\n", &version, &format, &size)){
        Msg(GERROR, "Read error");
        return;
      }
      if(version < 1.0) {
        Msg(GERROR, "This post-processing file is too old (version %g < 1.0)",
            version);
        return;
      }
      if(size == sizeof(double))
        Msg(DEBUG, "Data is in double precision format (size==%d)", size);
      else if(size == sizeof(float))
        Msg(DEBUG, "Data is in single precision format (size==%d)", size);
      else {
        Msg(GERROR, "Unknown data size (%d) in post-processing file", size);
        return;
      }
      if(format == 0)
        format = LIST_FORMAT_ASCII;
      else if(format == 1)
        format = LIST_FORMAT_BINARY;
      else {
        Msg(GERROR, "Unknown format for view");
        return;
      }
    }


    if(!strncmp(&str[1], "View", 4)) {
      v = BeginView(0);
      if(version <= 1.0) {
        Msg(DEBUG, "Detected post-processing view format <= 1.0");
        if(!fscanf(file, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		   name, &v->NbTimeStep,
		   &v->NbSP, &v->NbVP, &v->NbTP, &v->NbSL, &v->NbVL, &v->NbTL,
		   &v->NbST, &v->NbVT, &v->NbTT, &v->NbSS, &v->NbVS, &v->NbTS)){
	  Msg(GERROR, "Read error");
	  return;
	}
        v->NbT2 = t2l = v->NbT3 = t3l = 0;
      }
      else if(version == 1.1) {
        Msg(DEBUG, "Detected post-processing view format 1.1");
        if(!fscanf(file,
		   "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		   name, &v->NbTimeStep, &v->NbSP, &v->NbVP, &v->NbTP, &v->NbSL,
		   &v->NbVL, &v->NbTL, &v->NbST, &v->NbVT, &v->NbTT, &v->NbSS,
		   &v->NbVS, &v->NbTS, &v->NbT2, &t2l, &v->NbT3, &t3l)){
	  Msg(GERROR, "Read error");
	  return;
	}
      }
      else if(version == 1.2 || version == 1.3) {
        Msg(DEBUG, "Detected post-processing view format %g", version);
        if(!fscanf(file, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
		   "%d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		   name, &v->NbTimeStep,
		   &v->NbSP, &v->NbVP, &v->NbTP, &v->NbSL, &v->NbVL, &v->NbTL,
		   &v->NbST, &v->NbVT, &v->NbTT, &v->NbSQ, &v->NbVQ, &v->NbTQ,
		   &v->NbSS, &v->NbVS, &v->NbTS, &v->NbSH, &v->NbVH, &v->NbTH,
		   &v->NbSI, &v->NbVI, &v->NbTI, &v->NbSY, &v->NbVY, &v->NbTY,
		   &v->NbT2, &t2l, &v->NbT3, &t3l)){
	  Msg(GERROR, "Read error");
	  return;
	}
      }
      else if(version == 1.4) {
        Msg(DEBUG, "Detected post-processing view format 1.4");
        if(!fscanf(file, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
		   "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
		   "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
		   name, &v->NbTimeStep,
		   &v->NbSP, &v->NbVP, &v->NbTP, &v->NbSL, &v->NbVL, &v->NbTL,
		   &v->NbST, &v->NbVT, &v->NbTT, &v->NbSQ, &v->NbVQ, &v->NbTQ,
		   &v->NbSS, &v->NbVS, &v->NbTS, &v->NbSH, &v->NbVH, &v->NbTH,
		   &v->NbSI, &v->NbVI, &v->NbTI, &v->NbSY, &v->NbVY, &v->NbTY,
		   &v->NbSL2, &v->NbVL2, &v->NbTL2, &v->NbST2, &v->NbVT2, &v->NbTT2,
		   &v->NbSQ2, &v->NbVQ2, &v->NbTQ2, &v->NbSS2, &v->NbVS2, &v->NbTS2,
		   &v->NbSH2, &v->NbVH2, &v->NbTH2, &v->NbSI2, &v->NbVI2, &v->NbTI2,
		   &v->NbSY2, &v->NbVY2, &v->NbTY2, &v->NbT2, &t2l, &v->NbT3, &t3l)){
	  Msg(GERROR, "Read error");
	  return;
	}
      }
      else {
        Msg(GERROR, "Unknown post-processing file format (version %g)",
            version);
        return;
      }

      for(i = 0; i < (int)strlen(name); i++)
        if(name[i] == '^')
          name[i] = ' ';

      swap = 0;
      if(format == LIST_FORMAT_BINARY) {
        if(!fread(&testone, sizeof(int), 1, file)){
	  Msg(GERROR, "Read error");
	  return;
	}
        if(testone != 1) {
          Msg(INFO, "Swapping bytes from binary file");
          swap = 1;
        }
      }

      v->DataSize = size;

      // Time values
      v->Time = List_CreateFromFile(v->NbTimeStep, 100, size, file, format, swap);

      // Note: if nb==0, we still allocates the lists (so that they
      // are ready to be filled later, e.g. in plugins)

#define LCD List_CreateFromFile(nb, 1000, size, file, format, swap)

      // Points
      nb = v->NbSP ? v->NbSP * (v->NbTimeStep * 1 + 3) : 0; v->SP = LCD;
      nb = v->NbVP ? v->NbVP * (v->NbTimeStep * 3 + 3) : 0; v->VP = LCD;
      nb = v->NbTP ? v->NbTP * (v->NbTimeStep * 9 + 3) : 0; v->TP = LCD;

      // Lines
      nb = v->NbSL ? v->NbSL * (v->NbTimeStep * 2 * 1 + 6) : 0; v->SL = LCD;
      nb = v->NbVL ? v->NbVL * (v->NbTimeStep * 2 * 3 + 6) : 0; v->VL = LCD;
      nb = v->NbTL ? v->NbTL * (v->NbTimeStep * 2 * 9 + 6) : 0; v->TL = LCD;

      // Triangles
      nb = v->NbST ? v->NbST * (v->NbTimeStep * 3 * 1 + 9) : 0; v->ST = LCD;
      nb = v->NbVT ? v->NbVT * (v->NbTimeStep * 3 * 3 + 9) : 0; v->VT = LCD;
      nb = v->NbTT ? v->NbTT * (v->NbTimeStep * 3 * 9 + 9) : 0; v->TT = LCD;

      // Quadrangles
      nb = v->NbSQ ? v->NbSQ * (v->NbTimeStep * 4 * 1 + 12) : 0; v->SQ = LCD;
      nb = v->NbVQ ? v->NbVQ * (v->NbTimeStep * 4 * 3 + 12) : 0; v->VQ = LCD;
      nb = v->NbTQ ? v->NbTQ * (v->NbTimeStep * 4 * 9 + 12) : 0; v->TQ = LCD;

      // Tetrahedra
      nb = v->NbSS ? v->NbSS * (v->NbTimeStep * 4 * 1 + 12) : 0; v->SS = LCD;
      nb = v->NbVS ? v->NbVS * (v->NbTimeStep * 4 * 3 + 12) : 0; v->VS = LCD;
      nb = v->NbTS ? v->NbTS * (v->NbTimeStep * 4 * 9 + 12) : 0; v->TS = LCD;

      // Hexahedra
      nb = v->NbSH ? v->NbSH * (v->NbTimeStep * 8 * 1 + 24) : 0; v->SH = LCD;
      nb = v->NbVH ? v->NbVH * (v->NbTimeStep * 8 * 3 + 24) : 0; v->VH = LCD;
      nb = v->NbTH ? v->NbTH * (v->NbTimeStep * 8 * 9 + 24) : 0; v->TH = LCD;

      // Prisms
      nb = v->NbSI ? v->NbSI * (v->NbTimeStep * 6 * 1 + 18) : 0; v->SI = LCD;
      nb = v->NbVI ? v->NbVI * (v->NbTimeStep * 6 * 3 + 18) : 0; v->VI = LCD;
      nb = v->NbTI ? v->NbTI * (v->NbTimeStep * 6 * 9 + 18) : 0; v->TI = LCD;

      // Pyramids
      nb = v->NbSY ? v->NbSY * (v->NbTimeStep * 5 * 1 + 15) : 0; v->SY = LCD;
      nb = v->NbVY ? v->NbVY * (v->NbTimeStep * 5 * 3 + 15) : 0; v->VY = LCD;
      nb = v->NbTY ? v->NbTY * (v->NbTimeStep * 5 * 9 + 15) : 0; v->TY = LCD;

      // 2nd order Lines
      nb = v->NbSL2 ? v->NbSL2 * (v->NbTimeStep * 3 * 1 + 9) : 0; v->SL2 = LCD;
      nb = v->NbVL2 ? v->NbVL2 * (v->NbTimeStep * 3 * 3 + 9) : 0; v->VL2 = LCD;
      nb = v->NbTL2 ? v->NbTL2 * (v->NbTimeStep * 3 * 9 + 9) : 0; v->TL2 = LCD;

      // 2nd order Triangles
      nb = v->NbST2 ? v->NbST2 * (v->NbTimeStep * 6 * 1 + 18) : 0; v->ST2 = LCD;
      nb = v->NbVT2 ? v->NbVT2 * (v->NbTimeStep * 6 * 3 + 18) : 0; v->VT2 = LCD;
      nb = v->NbTT2 ? v->NbTT2 * (v->NbTimeStep * 6 * 9 + 18) : 0; v->TT2 = LCD;

      // 2nd order Quadrangles
      nb = v->NbSQ2 ? v->NbSQ2 * (v->NbTimeStep * 9 * 1 + 27) : 0; v->SQ2 = LCD;
      nb = v->NbVQ2 ? v->NbVQ2 * (v->NbTimeStep * 9 * 3 + 27) : 0; v->VQ2 = LCD;
      nb = v->NbTQ2 ? v->NbTQ2 * (v->NbTimeStep * 9 * 9 + 27) : 0; v->TQ2 = LCD;

      // 2nd order Tetrahedra
      nb = v->NbSS2 ? v->NbSS2 * (v->NbTimeStep * 10 * 1 + 30) : 0; v->SS2 = LCD;
      nb = v->NbVS2 ? v->NbVS2 * (v->NbTimeStep * 10 * 3 + 30) : 0; v->VS2 = LCD;
      nb = v->NbTS2 ? v->NbTS2 * (v->NbTimeStep * 10 * 9 + 30) : 0; v->TS2 = LCD;

      // 2nd order Hexahedra
      nb = v->NbSH2 ? v->NbSH2 * (v->NbTimeStep * 27 * 1 + 81) : 0; v->SH2 = LCD;
      nb = v->NbVH2 ? v->NbVH2 * (v->NbTimeStep * 27 * 3 + 81) : 0; v->VH2 = LCD;
      nb = v->NbTH2 ? v->NbTH2 * (v->NbTimeStep * 27 * 9 + 81) : 0; v->TH2 = LCD;

      // 2nd order Prisms
      nb = v->NbSI2 ? v->NbSI2 * (v->NbTimeStep * 18 * 1 + 54) : 0; v->SI2 = LCD;
      nb = v->NbVI2 ? v->NbVI2 * (v->NbTimeStep * 18 * 3 + 54) : 0; v->VI2 = LCD;
      nb = v->NbTI2 ? v->NbTI2 * (v->NbTimeStep * 18 * 9 + 54) : 0; v->TI2 = LCD;

      // 2nd order Pyramids
      nb = v->NbSY2 ? v->NbSY2 * (v->NbTimeStep * 14 * 1 + 42) : 0; v->SY2 = LCD;
      nb = v->NbVY2 ? v->NbVY2 * (v->NbTimeStep * 14 * 3 + 42) : 0; v->VY2 = LCD;
      nb = v->NbTY2 ? v->NbTY2 * (v->NbTimeStep * 14 * 9 + 42) : 0; v->TY2 = LCD;

#undef LCD

      // 2D strings
      nb = v->NbT2 ? v->NbT2 * 4 : 0;
      v->T2D = List_CreateFromFile(nb, 100, size, file, format, swap);
      if(version <= 1.2)
	v->T2C = List_CreateFromFileOld(t2l, 100, sizeof(char), file, format, swap);
      else
	v->T2C = List_CreateFromFile(t2l, 100, sizeof(char), file, format, swap);

      // 3D strings
      nb = v->NbT3 ? v->NbT3 * 5 : 0;
      v->T3D = List_CreateFromFile(nb, 100, size, file, format, swap);
      if(version <= 1.2)
	v->T3C = List_CreateFromFileOld(t3l, 100, sizeof(char), file, format, swap);
      else
	v->T3C = List_CreateFromFile(t3l, 100, sizeof(char), file, format, swap);

      Msg(DEBUG,
          "Read View '%s' (%d TimeSteps): "
	  "SP(%d/%d) VP(%d/%d) TP(%d/%d) "
	  "SL(%d/%d) VL(%d/%d) TL(%d/%d) "
	  "ST(%d/%d) VT(%d/%d) TT(%d/%d) "
	  "SQ(%d/%d) VQ(%d/%d) TQ(%d/%d) "
	  "SS(%d/%d) VS(%d/%d) TS(%d/%d) "
	  "SH(%d/%d) VH(%d/%d) TH(%d/%d) "
	  "SI(%d/%d) VI(%d/%d) TI(%d/%d) "
	  "SY(%d/%d) VY(%d/%d) TY(%d/%d) " 
	  "SL2(%d/%d) VL2(%d/%d) TL2(%d/%d) "
	  "ST2(%d/%d) VT2(%d/%d) TT2(%d/%d) "
	  "SQ2(%d/%d) VQ2(%d/%d) TQ2(%d/%d) "
	  "SS2(%d/%d) VS2(%d/%d) TS2(%d/%d) " 
	  "SH2(%d/%d) VH2(%d/%d) TH2(%d/%d) "
	  "SI2(%d/%d) VI2(%d/%d) TI2(%d/%d) "
	  "SY2(%d/%d) VY2(%d/%d) TY2(%d/%d) "
	  "T2(%d/%d/%d) T3(%d/%d/%d) ", 
	  name, v->NbTimeStep,
          v->NbSP, List_Nbr(v->SP), v->NbVP, List_Nbr(v->VP), v->NbTP, List_Nbr(v->TP),
          v->NbSL, List_Nbr(v->SL), v->NbVL, List_Nbr(v->VL), v->NbTL, List_Nbr(v->TL),
          v->NbST, List_Nbr(v->ST), v->NbVT, List_Nbr(v->VT), v->NbTT, List_Nbr(v->TT),
          v->NbSQ, List_Nbr(v->SQ), v->NbVQ, List_Nbr(v->VQ), v->NbTQ, List_Nbr(v->TQ),
          v->NbSS, List_Nbr(v->SS), v->NbVS, List_Nbr(v->VS), v->NbTS, List_Nbr(v->TS),
          v->NbSH, List_Nbr(v->SH), v->NbVH, List_Nbr(v->VH), v->NbTH, List_Nbr(v->TH),
          v->NbSI, List_Nbr(v->SI), v->NbVI, List_Nbr(v->VI), v->NbTI, List_Nbr(v->TI),
          v->NbSY, List_Nbr(v->SY), v->NbVY, List_Nbr(v->VY), v->NbTY, List_Nbr(v->TY),
          v->NbSL2, List_Nbr(v->SL2), v->NbVL2, List_Nbr(v->VL2), v->NbTL2, List_Nbr(v->TL2),
          v->NbST2, List_Nbr(v->ST2), v->NbVT2, List_Nbr(v->VT2), v->NbTT2, List_Nbr(v->TT2),
          v->NbSQ2, List_Nbr(v->SQ2), v->NbVQ2, List_Nbr(v->VQ2), v->NbTQ2, List_Nbr(v->TQ2),
          v->NbSS2, List_Nbr(v->SS2), v->NbVS2, List_Nbr(v->VS2), v->NbTS2, List_Nbr(v->TS2),
          v->NbSH2, List_Nbr(v->SH2), v->NbVH2, List_Nbr(v->VH2), v->NbTH2, List_Nbr(v->TH2),
          v->NbSI2, List_Nbr(v->SI2), v->NbVI2, List_Nbr(v->VI2), v->NbTI2, List_Nbr(v->TI2),
          v->NbSY2, List_Nbr(v->SY2), v->NbVY2, List_Nbr(v->VY2), v->NbTY2, List_Nbr(v->TY2),
	  v->NbT2, List_Nbr(v->T2D), List_Nbr(v->T2C), 
	  v->NbT3, List_Nbr(v->T3D), List_Nbr(v->T3C));
      
      // don't update the ui after each view, but only at the end
      EndView(v, 0, filename, name); 
    }

    do {
      if(!fgets(str, 256, file))
	Msg(GERROR, "Prematured end of file");
      if(feof(file))
        Msg(GERROR, "Prematured end of file");
    } while(str[0] != '$');

  }     /* while 1 ... */

#if defined(HAVE_FLTK)
  UpdateViewsInGUI();
#endif

  Msg(INFO, "Read post-processing file '%s'", filename);
  Msg(STATUS2N, "Read '%s'", filename);
}

// Write view to file in Parsed, ASCII or Binary format

static void write_parsed_time(List_T *list, FILE *fp)
{
  if(List_Nbr(list) > 1) {
    fprintf(fp, "TIME{");
    for(int i = 0; i < List_Nbr(list); i ++) {
      if(i) fprintf(fp, ",");
      fprintf(fp, "%.16g", *(double *)List_Pointer(list, i));
    }
    fprintf(fp, "};\n");
  }
}

static void write_parsed_elements(char *str, int nbnod, int nb, List_T *list, FILE *fp)
{
  if(nb) {
    int n = List_Nbr(list) / nb;
    for(int i = 0; i < List_Nbr(list); i += n) {
      double *x = (double *)List_Pointer(list, i);
      double *y = (double *)List_Pointer(list, i + nbnod);
      double *z = (double *)List_Pointer(list, i + 2 * nbnod);
      fprintf(fp, "%s(", str);
      for(int j = 0; j < nbnod; j++) {
	if(j) fprintf(fp, ",");
	fprintf(fp, "%.16g,%.16g,%.16g", x[j], y[j], z[j]);
      }
      fprintf(fp, "){");
      for(int j = 3 * nbnod; j < n; j++) {
	if(j - 3 * nbnod) fprintf(fp, ",");
	fprintf(fp, "%.16g", *(double *)List_Pointer(list, i + j));
      }
      fprintf(fp, "};\n");
    }
  }
}

static void write_parsed_strings(int nbc, int nb, List_T *TD, List_T *TC, FILE *fp)
{
  if(!nb || (nbc != 4 && nbc != 5)) return;
  for(int j = 0; j < List_Nbr(TD); j += nbc){
    double x, y, z, style, start, end;
    List_Read(TD, j, &x);
    List_Read(TD, j+1, &y);
    if(nbc == 5)
      List_Read(TD, j+2, &z);
    List_Read(TD, j+nbc-2, &style);
    if(nbc == 4)
      fprintf(fp, "T2(%g,%g,%g){", x, y, style);
    else
      fprintf(fp, "T3(%g,%g,%g,%g){", x, y, z, style);
    List_Read(TD, j+nbc-1, &start);
    if(j+nbc*2-1 < List_Nbr(TD))
      List_Read(TD, j+nbc*2-1, &end);
    else
      end = List_Nbr(TC);
    int l = 0;
    while(l < end-start){
      char *str = (char*)List_Pointer(TC, (int)start + l);
      if(l) fprintf(fp, ",");
      fprintf(fp, "\"%s\"", str);
      l += strlen(str)+1;
    }
    fprintf(fp, "};\n");
  }
}

void WriteViewPost(Post_View *v, FILE *file, int binary=0, int parsed=1, int append=0)
{
  char name[256];
  int f, One = 1;

  if(!parsed && !append){
    fprintf(file, "$PostFormat /* Gmsh 1.3, %s */\n",
	    binary ? "binary" : "ascii");
    fprintf(file, "1.3 %d %d\n", binary, (int)sizeof(double));
    fprintf(file, "$EndPostFormat\n");
  }

  strcpy(name, v->Name);
  for(int i = 0; i < (int)strlen(name); i++)
    if(name[i] == ' ') name[i] = '^';

  if(!parsed){
    fprintf(file, "$View /* %s */\n", v->Name);
    fprintf(file, "%s ", name);
    fprintf(file, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
	    "%d %d %d %d %d %d %d %d %d %d %d %d\n",
	    List_Nbr(v->Time),
	    v->NbSP, v->NbVP, v->NbTP, v->NbSL, v->NbVL, v->NbTL,
	    v->NbST, v->NbVT, v->NbTT, v->NbSQ, v->NbVQ, v->NbTQ,
	    v->NbSS, v->NbVS, v->NbTS, v->NbSH, v->NbVH, v->NbTH,
	    v->NbSI, v->NbVI, v->NbTI, v->NbSY, v->NbVY, v->NbTY,
	    v->NbT2, List_Nbr(v->T2C), v->NbT3, List_Nbr(v->T3C));
    if(binary) {
      f = LIST_FORMAT_BINARY;
      if(!fwrite(&One, sizeof(int), 1, file)){
	Msg(GERROR, "Write error");
	return;
      }
    }
    else
      f = LIST_FORMAT_ASCII;
    List_WriteToFile(v->Time, file, f);
    List_WriteToFile(v->SP, file, f);
    List_WriteToFile(v->VP, file, f);
    List_WriteToFile(v->TP, file, f);
    List_WriteToFile(v->SL, file, f);
    List_WriteToFile(v->VL, file, f);
    List_WriteToFile(v->TL, file, f);
    List_WriteToFile(v->ST, file, f);
    List_WriteToFile(v->VT, file, f);
    List_WriteToFile(v->TT, file, f);
    List_WriteToFile(v->SQ, file, f);
    List_WriteToFile(v->VQ, file, f);
    List_WriteToFile(v->TQ, file, f);
    List_WriteToFile(v->SS, file, f);
    List_WriteToFile(v->VS, file, f);
    List_WriteToFile(v->TS, file, f);
    List_WriteToFile(v->SH, file, f);
    List_WriteToFile(v->VH, file, f);
    List_WriteToFile(v->TH, file, f);
    List_WriteToFile(v->SI, file, f);
    List_WriteToFile(v->VI, file, f);
    List_WriteToFile(v->TI, file, f);
    List_WriteToFile(v->SY, file, f);
    List_WriteToFile(v->VY, file, f);
    List_WriteToFile(v->TY, file, f);
    List_WriteToFile(v->T2D, file, f);
    List_WriteToFile(v->T2C, file, f);
    List_WriteToFile(v->T3D, file, f);
    List_WriteToFile(v->T3C, file, f);
    fprintf(file, "\n");
    fprintf(file, "$EndView\n");
  }
  else{
    fprintf(file, "View \"%s\" {\n", v->Name);
    write_parsed_time(v->Time, file);
    write_parsed_elements("SP", 1, v->NbSP, v->SP, file);
    write_parsed_elements("VP", 1, v->NbVP, v->VP, file);
    write_parsed_elements("TP", 1, v->NbTP, v->TP, file);
    write_parsed_elements("SL", 2, v->NbSL, v->SL, file);
    write_parsed_elements("VL", 2, v->NbVL, v->VL, file);
    write_parsed_elements("TL", 2, v->NbTL, v->TL, file);
    write_parsed_elements("ST", 3, v->NbST, v->ST, file);
    write_parsed_elements("VT", 3, v->NbVT, v->VT, file);
    write_parsed_elements("TT", 3, v->NbTT, v->TT, file);
    write_parsed_elements("SQ", 4, v->NbSQ, v->SQ, file);
    write_parsed_elements("VQ", 4, v->NbVQ, v->VQ, file);
    write_parsed_elements("TQ", 4, v->NbTQ, v->TQ, file);
    write_parsed_elements("SS", 4, v->NbSS, v->SS, file);
    write_parsed_elements("VS", 4, v->NbVS, v->VS, file);
    write_parsed_elements("TS", 4, v->NbTS, v->TS, file);
    write_parsed_elements("SH", 8, v->NbSH, v->SH, file);
    write_parsed_elements("VH", 8, v->NbVH, v->VH, file);
    write_parsed_elements("TH", 8, v->NbTH, v->TH, file);
    write_parsed_elements("SI", 6, v->NbSI, v->SI, file);
    write_parsed_elements("VI", 6, v->NbVI, v->VI, file);
    write_parsed_elements("TI", 6, v->NbTI, v->TI, file);
    write_parsed_elements("SY", 5, v->NbSY, v->SY, file);
    write_parsed_elements("VY", 5, v->NbVY, v->VY, file);
    write_parsed_elements("TY", 5, v->NbTY, v->TY, file);
    write_parsed_strings(4, v->NbT2, v->T2D, v->T2C, file);
    write_parsed_strings(5, v->NbT3, v->T3D, v->T3C, file);
    fprintf(file, "};\n");
  }
}

// Write view to file in STL format

static void write_stl(FILE *file, int nbelm, List_T *list, int nbnod)
{
  if(nbelm){
    int nb = List_Nbr(list) / nbelm;
    for(int i = 0; i < List_Nbr(list); i+=nb){
      double *x = (double*)List_Pointer(list, i);
      double n[3];
      normal3points(x[0], x[3], x[6],
		    x[1], x[4], x[7],
		    x[2], x[5], x[8], n);
      if(nbnod == 3){
	fprintf(file, "facet normal %g %g %g\n", n[0], n[1], n[2]);
	fprintf(file, "  outer loop\n");
	fprintf(file, "    vertex %g %g %g\n", x[0], x[3], x[6]);
	fprintf(file, "    vertex %g %g %g\n", x[1], x[4], x[7]);
	fprintf(file, "    vertex %g %g %g\n", x[2], x[5], x[8]);
	fprintf(file, "  endloop\n");
	fprintf(file, "endfacet\n");
      }
      else{
	fprintf(file, "facet normal %g %g %g\n", n[0], n[1], n[2]);
	fprintf(file, "  outer loop\n");
	fprintf(file, "    vertex %g %g %g\n", x[0], x[4], x[8]);
	fprintf(file, "    vertex %g %g %g\n", x[1], x[5], x[9]);
	fprintf(file, "    vertex %g %g %g\n", x[2], x[6], x[10]);
	fprintf(file, "  endloop\n");
	fprintf(file, "endfacet\n");
	fprintf(file, "facet normal %g %g %g\n", n[0], n[1], n[2]);
	fprintf(file, "  outer loop\n");
	fprintf(file, "    vertex %g %g %g\n", x[0], x[4], x[8]);
	fprintf(file, "    vertex %g %g %g\n", x[2], x[6], x[10]);
	fprintf(file, "    vertex %g %g %g\n", x[3], x[7], x[11]);
	fprintf(file, "  endloop\n");
	fprintf(file, "endfacet\n");
      }
    }
  }
}

void WriteViewSTL(Post_View *v, FILE *file)
{
  if(!v->NbST && !v->NbVT && !v->NbTT &&
     !v->NbSQ && !v->NbVQ && !v->NbTQ){
    Msg(GERROR, "No surface elements to save");
    return;
  }

  fprintf(file, "solid Created by Gmsh\n");
  write_stl(file, v->NbST, v->ST, 3);
  write_stl(file, v->NbVT, v->VT, 3);
  write_stl(file, v->NbTT, v->TT, 3);
  write_stl(file, v->NbSQ, v->SQ, 4);
  write_stl(file, v->NbVQ, v->VQ, 4);
  write_stl(file, v->NbTQ, v->TQ, 4);
  fprintf(file, "endsolid Created by Gmsh\n");
}

// Write view to file in Text format (should change this to have 2
// choices: "Tabular (By Element)" and "Tabular (By Time Step)")

static void write_txt(FILE *file, int nbelm, List_T *list,
		      int nbnod, int nbcomp, int nbtime)
{
  if(nbelm){
    int nb = List_Nbr(list) / nbelm;
    for(int i = 0; i < List_Nbr(list); i+=nb){
      double *x = (double*)List_Pointer(list, i);
      for(int j = 0; j < nbnod*(3+nbcomp*nbtime); j++)
	fprintf(file, "%.16g ", x[j]);
      fprintf(file, "\n");
    }
    fprintf(file, "\n");
  }
}

void WriteViewTXT(Post_View *v, FILE *file)
{
  write_txt(file, v->NbSP, v->SP, 1, 1, v->NbTimeStep);
  write_txt(file, v->NbVP, v->VP, 1, 3, v->NbTimeStep);
  write_txt(file, v->NbTP, v->TP, 1, 9, v->NbTimeStep);
  write_txt(file, v->NbSL, v->SL, 2, 1, v->NbTimeStep);
  write_txt(file, v->NbVL, v->VL, 2, 3, v->NbTimeStep);
  write_txt(file, v->NbTL, v->TL, 2, 9, v->NbTimeStep);
  write_txt(file, v->NbST, v->ST, 3, 1, v->NbTimeStep);
  write_txt(file, v->NbVT, v->VT, 3, 3, v->NbTimeStep);
  write_txt(file, v->NbTT, v->TT, 3, 9, v->NbTimeStep);
  write_txt(file, v->NbSQ, v->SQ, 4, 1, v->NbTimeStep);
  write_txt(file, v->NbVQ, v->VQ, 4, 3, v->NbTimeStep);
  write_txt(file, v->NbTQ, v->TQ, 4, 9, v->NbTimeStep);
  write_txt(file, v->NbSS, v->SS, 4, 1, v->NbTimeStep);
  write_txt(file, v->NbVS, v->VS, 4, 3, v->NbTimeStep);
  write_txt(file, v->NbTS, v->TS, 4, 9, v->NbTimeStep);
  write_txt(file, v->NbSH, v->SH, 8, 1, v->NbTimeStep);
  write_txt(file, v->NbVH, v->VH, 8, 3, v->NbTimeStep);
  write_txt(file, v->NbTH, v->TH, 8, 9, v->NbTimeStep);
  write_txt(file, v->NbSI, v->SI, 6, 1, v->NbTimeStep);
  write_txt(file, v->NbVI, v->VI, 6, 3, v->NbTimeStep);
  write_txt(file, v->NbTI, v->TI, 6, 9, v->NbTimeStep);
  write_txt(file, v->NbSY, v->SY, 5, 1, v->NbTimeStep);
  write_txt(file, v->NbVY, v->VY, 5, 3, v->NbTimeStep);
  write_txt(file, v->NbTY, v->TY, 5, 9, v->NbTimeStep);
}

// Write view to file in MSH format

class Nod{
 public:
  int Num;
  double X, Y, Z;
  Nod() : Num(0), X(0.), Y(0.), Z(0.) {}
  Nod(double x, double y, double z) : Num(0), X(x), Y(y), Z(z) {}
};

class NodCompPos{
 public:
  bool operator()(const Nod ent1, const Nod ent2) const
  {
    double tol = CTX.lc * 1.e-10 ;
    if(ent1.X - ent2.X  >  tol) return true;
    if(ent1.X - ent2.X  < -tol) return false;
    if(ent1.Y - ent2.Y  >  tol) return true;
    if(ent1.Y - ent2.Y  < -tol) return false;
    if(ent1.Z - ent2.Z  >  tol) return true;
    return false;
  }
};

static void get_nod(int nbelm, List_T *list, int nbnod, int nbcomp, 
		    std::set<Nod, NodCompPos> *nodes,
		    int *numelm)
{
  if(nbelm){
    int nb = List_Nbr(list) / nbelm;
    for(int i = 0; i < List_Nbr(list); i+=nb){
      double *x = (double *)List_Pointer_Fast(list, i);
      double *y = (double *)List_Pointer_Fast(list, i + nbnod);
      double *z = (double *)List_Pointer_Fast(list, i + 2 * nbnod);
      for(int j = 0; j < nbnod; j++) {
	Nod n(x[j], y[j], z[j]);
	std::set<Nod, NodCompPos>::iterator it = nodes->find(n);
	if(it == nodes->end()){
	  n.Num = nodes->size() + 1;
	  nodes->insert(n);
	}
      }
      (*numelm)++;
    }
  }
}

static void print_elm(FILE *file, int num, int nbnod, Nod nod[8], 
		      int nbcomp, double *vals, int dim)
{
  // compute average value in elm
  double d = 0.;
  for(int k = 0; k < nbnod; k++) {
    double *v = &vals[nbcomp * k];
    switch(nbcomp) {
    case 1: // scalar
      d += v[0];
      break;
    case 3 : // vector
      d += sqrt(DSQR(v[0]) + DSQR(v[1]) + DSQR(v[2]));
      break;
    case 9 : // tensor
      d += ComputeVonMises(v);
      break;
    }
  }
  d /= (double)nbnod;

  // assign val as elementary region number
  int ele = (int)fabs(d), phys = 1;

  switch(dim){
  case 0:
    fprintf(file, "%d 15 %d %d 1 %d\n", num, phys, ele, nod[0].Num);
    break;
  case 1:
    fprintf(file, "%d 1 %d %d 2 %d %d\n", num, phys, ele, nod[0].Num, nod[1].Num);
    break;
  case 2:
    if(nbnod == 3)
      fprintf(file, "%d 2 %d %d 3 %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num);
    else
      fprintf(file, "%d 3 %d %d 4 %d %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num, nod[3].Num);
    break;
  case 3:
  default:
    if(nbnod == 4)
      fprintf(file, "%d 4 %d %d 4 %d %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num, nod[3].Num);
    else if(nbnod == 5)
      fprintf(file, "%d 7 %d %d 5 %d %d %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num, nod[3].Num, nod[4].Num);
    else if(nbnod == 6)
      fprintf(file, "%d 6 %d %d 6 %d %d %d %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num, nod[3].Num, nod[4].Num, 
	      nod[5].Num);
    else
      fprintf(file, "%d 5 %d %d 8 %d %d %d %d %d %d %d %d\n", num, phys, ele, 
	      nod[0].Num, nod[1].Num, nod[2].Num, nod[3].Num, nod[4].Num, 
	      nod[5].Num, nod[6].Num, nod[7].Num);
    break;
  }
}

static void print_elms(FILE *file, int nbelm, List_T *list,
		       int nbnod, int nbcomp, int dim, 
		       std::set<Nod, NodCompPos> *nodes,
		       int *numelm)
{
  Nod nod[8];

  if(nbelm){
    int nb = List_Nbr(list) / nbelm;
    for(int i = 0; i < List_Nbr(list); i+=nb){
      double *x = (double *)List_Pointer_Fast(list, i);
      double *y = (double *)List_Pointer_Fast(list, i + nbnod);
      double *z = (double *)List_Pointer_Fast(list, i + 2 * nbnod);
      double *v = (double *)List_Pointer_Fast(list, i + 3 * nbnod);
      for(int j = 0; j < nbnod; j++) {
	Nod n(x[j], y[j], z[j]);
	std::set<Nod, NodCompPos>::iterator it = nodes->find(n);
	if(it == nodes->end()){
	  Msg(GERROR, "Unknown node in element");
	  return;
	}
	else{
	  nod[j] = (Nod)(*it);
	}
      }
      (*numelm)++;
      print_elm(file, *numelm, nbnod, nod, nbcomp, v, dim);
    }
  }
}

void WriteViewMSH(Post_View *v, FILE *file)
{
  std::set<Nod, NodCompPos> nodes;
  int numelm = 0;
  get_nod(v->NbSP, v->SP, 1, 1, &nodes, &numelm);
  get_nod(v->NbVP, v->VP, 1, 3, &nodes, &numelm);
  get_nod(v->NbTP, v->TP, 1, 9, &nodes, &numelm);
  get_nod(v->NbSL, v->SL, 2, 1, &nodes, &numelm);
  get_nod(v->NbVL, v->VL, 2, 3, &nodes, &numelm);
  get_nod(v->NbTL, v->TL, 2, 9, &nodes, &numelm);
  get_nod(v->NbST, v->ST, 3, 1, &nodes, &numelm);
  get_nod(v->NbVT, v->VT, 3, 3, &nodes, &numelm);
  get_nod(v->NbTT, v->TT, 3, 9, &nodes, &numelm);
  get_nod(v->NbSQ, v->SQ, 4, 1, &nodes, &numelm);
  get_nod(v->NbVQ, v->VQ, 4, 3, &nodes, &numelm);
  get_nod(v->NbTQ, v->TQ, 4, 9, &nodes, &numelm);
  get_nod(v->NbSS, v->SS, 4, 1, &nodes, &numelm);
  get_nod(v->NbVS, v->VS, 4, 3, &nodes, &numelm);
  get_nod(v->NbTS, v->TS, 4, 9, &nodes, &numelm);
  get_nod(v->NbSH, v->SH, 8, 1, &nodes, &numelm);
  get_nod(v->NbVH, v->VH, 8, 3, &nodes, &numelm);
  get_nod(v->NbTH, v->TH, 8, 9, &nodes, &numelm);
  get_nod(v->NbSI, v->SI, 6, 1, &nodes, &numelm);
  get_nod(v->NbVI, v->VI, 6, 3, &nodes, &numelm);
  get_nod(v->NbTI, v->TI, 6, 9, &nodes, &numelm);
  get_nod(v->NbSY, v->SY, 5, 1, &nodes, &numelm);
  get_nod(v->NbVY, v->VY, 5, 3, &nodes, &numelm);
  get_nod(v->NbTY, v->TY, 5, 9, &nodes, &numelm);

  fprintf(file, "$NOD\n");
  fprintf(file, "%d\n", (int)nodes.size());
  std::set<Nod, NodCompPos>::const_iterator it = nodes.begin();
  std::set<Nod, NodCompPos>::const_iterator ite = nodes.end();
  for(; it != ite; ++it){
    Nod n = (Nod)(*it);
    fprintf(file, "%d %.16g %.16g %.16g\n", n.Num, n.X, n.Y, n.Z);
  }
  fprintf(file, "$ENDNOD\n");

  fprintf(file, "$ELM\n");
  fprintf(file, "%d\n", numelm);
  numelm = 0;
  print_elms(file, v->NbSP, v->SP, 1, 1, 0, &nodes, &numelm);
  print_elms(file, v->NbVP, v->VP, 1, 3, 0, &nodes, &numelm);
  print_elms(file, v->NbTP, v->TP, 1, 9, 0, &nodes, &numelm);
  print_elms(file, v->NbSL, v->SL, 2, 1, 1, &nodes, &numelm);
  print_elms(file, v->NbVL, v->VL, 2, 3, 1, &nodes, &numelm);
  print_elms(file, v->NbTL, v->TL, 2, 9, 1, &nodes, &numelm);
  print_elms(file, v->NbST, v->ST, 3, 1, 2, &nodes, &numelm);
  print_elms(file, v->NbVT, v->VT, 3, 3, 2, &nodes, &numelm);
  print_elms(file, v->NbTT, v->TT, 3, 9, 2, &nodes, &numelm);
  print_elms(file, v->NbSQ, v->SQ, 4, 1, 2, &nodes, &numelm);
  print_elms(file, v->NbVQ, v->VQ, 4, 3, 2, &nodes, &numelm);
  print_elms(file, v->NbTQ, v->TQ, 4, 9, 2, &nodes, &numelm);
  print_elms(file, v->NbSS, v->SS, 4, 1, 3, &nodes, &numelm);
  print_elms(file, v->NbVS, v->VS, 4, 3, 3, &nodes, &numelm);
  print_elms(file, v->NbTS, v->TS, 4, 9, 3, &nodes, &numelm);
  print_elms(file, v->NbSH, v->SH, 8, 1, 3, &nodes, &numelm);
  print_elms(file, v->NbVH, v->VH, 8, 3, 3, &nodes, &numelm);
  print_elms(file, v->NbTH, v->TH, 8, 9, 3, &nodes, &numelm);
  print_elms(file, v->NbSI, v->SI, 6, 1, 3, &nodes, &numelm);
  print_elms(file, v->NbVI, v->VI, 6, 3, 3, &nodes, &numelm);
  print_elms(file, v->NbTI, v->TI, 6, 9, 3, &nodes, &numelm);
  print_elms(file, v->NbSY, v->SY, 5, 1, 3, &nodes, &numelm);
  print_elms(file, v->NbVY, v->VY, 5, 3, 3, &nodes, &numelm);
  print_elms(file, v->NbTY, v->TY, 5, 9, 3, &nodes, &numelm);
  fprintf(file, "$ENDELM\n");
}

// Write view to file

void WriteView(Post_View *v, char *filename, int format, int append)
{
  FILE *file;
  int binary = (format == 1) ? 1 : 0;
  int parsed = (format == 2);
  int stl = (format == 3);
  int txt = (format == 4);
  int msh = (format == 5);

  if(filename) {
    file = fopen(filename, append ? (binary ? "ab" : "a") : (binary ? "wb" : "w"));
    if(!file) {
      Msg(GERROR, "Unable to open file '%s'", filename);
      return;
    }
    if(!append)
      Msg(INFO, "Writing post-processing file '%s'", filename);
  }
  else
    file = stdout;

  if(stl)
    WriteViewSTL(v, file);
  else if(txt)
    WriteViewTXT(v, file);
  else if(msh)
    WriteViewMSH(v, file);
  else
    WriteViewPost(v, file, binary, parsed, append);
  
  if(filename) {
    fclose(file);
    Msg(INFO, "Wrote view '%s' in file '%s'", v->Name, filename);
    Msg(STATUS2N, "Wrote '%s'", filename);
  }
 
}
