
/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/
#include"os_python.h"

#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"

#include"Base.h"
#include"OOMac.h"
#include"RepNonbondedSphere.h"
#include"Color.h"
#include"Sphere.h"
#include"Setting.h"
#include"main.h"
#include"ShaderMgr.h"

typedef struct RepNonbondedSphere {
  Rep R;
  float *V;
  float *VC;
  SphereRec *SP;
  int N, NC;
  float *VP;
  Pickable *P;
  int NP;
  int VariableAlphaFlag;
  CGO *shaderCGO;
} RepNonbondedSphere;

#include"ObjectMolecule.h"

void RepNonbondedSphereFree(RepNonbondedSphere * I);

void RepNonbondedSphereInit(void)
{
}

void RepNonbondedSphereFree(RepNonbondedSphere * I)
{
  if (I->shaderCGO){
    CGOFree(I->shaderCGO);
    I->shaderCGO = 0;
  }
  FreeP(I->VP);
  RepPurge(&I->R);
  FreeP(I->VC);
  FreeP(I->V);
  OOFreeP(I);
}

static void RepNonbondedSphereRender(RepNonbondedSphere * I, RenderInfo * info)
{
  CRay *ray = info->ray;
  Picking **pick = info->pick;
  register PyMOLGlobals *G = I->R.G;
  float *v = I->V;
  int c = I->N;
  int cc = 0;
  int a;
  SphereRec *sp;
  int i, j;
  Pickable *p;

  float alpha;

  alpha =
    SettingGet_f(G, I->R.cs->Setting, I->R.obj->Setting, cSetting_nonbonded_transparency);
  alpha = 1.0F - alpha;
  if(fabs(alpha - 1.0) < R_SMALL4)
    alpha = 1.0F;

  if(ray) {
    int variable_alpha = I->VariableAlphaFlag;
    ray->fTransparentf(ray, 1.0F - alpha);
    v = I->VC;
    c = I->NC;
    while(c--) {
      if(variable_alpha) {
        ray->fTransparentf(ray, 1.0F - v[3]);
      }
      ray->fColor3fv(ray, v);
      v += 4;
      ray->fSphere3fv(ray, v, *(v + 3));
      v += 4;
    }
    ray->fTransparentf(ray, 0.0);
  } else if(G->HaveGUI && G->ValidContext) {
    if(pick) {

      i = (*pick)->src.index;

      v = I->VP;
      c = I->NP;
      p = I->R.P;

#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
#ifdef _PYMOL_GL_DRAWARRAYS
      {
	int nverts = c * 6, pl, plc = 0;
	GLubyte *tmp_ptr;
	ALLOCATE_ARRAY(GLfloat,vertVals,nverts*3)
	ALLOCATE_ARRAY(GLubyte,colorVals,nverts*4)
	pl = 0;
	while(c--) {
	  i++;
	  if(!(*pick)[0].src.bond) {
	    /* pass 1 - low order bits */
	    colorVals[plc++] = (uchar) ((i & 0xF) << 4);
	    colorVals[plc++] = (uchar) ((i & 0xF0) | 0x8);
	    colorVals[plc++] = (uchar) ((i & 0xF00) >> 4);
	    colorVals[plc++] = (uchar) 255;
	    VLACheck((*pick), Picking, i);
	    p++;
	    (*pick)[i].src = *p;  /* copy object and atom info */
	    (*pick)[i].context = I->R.context;
	  } else {
	    /* pass 2 - high order bits */
	    j = i >> 12;
	    colorVals[plc++] = (uchar) ((j & 0xF) << 4);
	    colorVals[plc++] = (uchar) ((j & 0xF0) | 0x8);
	    colorVals[plc++] = (uchar) ((j & 0xF00) >> 4);
	    colorVals[plc++] = (uchar) 255;
	  }
	  memcpy(&vertVals[pl], v, 18*sizeof(GLfloat));
	  v += 18;
	  pl += 18;
	  tmp_ptr = &colorVals[plc-4];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = tmp_ptr[3];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = tmp_ptr[3];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = tmp_ptr[3];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = tmp_ptr[3];
	  colorVals[plc++] = tmp_ptr[0]; colorVals[plc++] = tmp_ptr[1]; colorVals[plc++] = tmp_ptr[2]; colorVals[plc++] = tmp_ptr[3];
	}
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, vertVals);
	glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorVals);
	glDrawArrays(GL_LINES, 0, nverts);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	DEALLOCATE_ARRAY(vertVals)
	DEALLOCATE_ARRAY(colorVals)
      }
#else
      glBegin(GL_LINES);
      while(c--) {
        i++;
        if(!(*pick)[0].src.bond) {
          /* pass 1 - low order bits */
          glColor3ub((uchar) ((i & 0xF) << 4), (uchar) ((i & 0xF0) | 0x8),
                     (uchar) ((i & 0xF00) >> 4));
          VLACheck((*pick), Picking, i);
          p++;
          (*pick)[i].src = *p;  /* copy object and atom info */
          (*pick)[i].context = I->R.context;
        } else {
          /* pass 2 - high order bits */
          j = i >> 12;
          glColor3ub((uchar) ((j & 0xF) << 4), (uchar) ((j & 0xF0) | 0x8),
                     (uchar) ((j & 0xF00) >> 4));
        }
        glVertex3fv(v);
        v += 3;
        glVertex3fv(v);
        v += 3;
        glVertex3fv(v);
        v += 3;
        glVertex3fv(v);
        v += 3;
        glVertex3fv(v);
        v += 3;
        glVertex3fv(v);
        v += 3;
      }
      glEnd();
#endif
#endif
      (*pick)[0].src.index = i;

    } else { /* rendering */
      int variable_alpha = I->VariableAlphaFlag;
      short use_shader, use_default_shader, use_sphere_shader, generate_shader_cgo = 0, use_display_lists = 0;
      use_shader = (int) SettingGet(G, cSetting_nb_spheres_use_shader) &&
	           (int) SettingGet(G, cSetting_use_shaders);
      use_sphere_shader = (int) (SettingGet(G, cSetting_nb_spheres_use_shader)==2) &&
	                  (int) SettingGet(G, cSetting_use_shaders);
      use_default_shader = (int) (SettingGet(G, cSetting_nb_spheres_use_shader)==1) && 
	                   (int) SettingGet(G, cSetting_use_shaders);
      use_display_lists = (int) SettingGet(G, cSetting_use_display_lists);

      if (I->shaderCGO){
	if (!use_shader || use_sphere_shader ^ I->shaderCGO->has_draw_sphere_buffers){
	  CGOFree(I->shaderCGO);
	  I->shaderCGO = 0;
	}
      }

#ifdef _PYMOL_GL_CALLLISTS
        if(use_display_lists && I->R.displayList) {
          glCallList(I->R.displayList);
	  return;
	}
#endif

      if (use_shader){
	if (!I->shaderCGO){
	  I->shaderCGO = CGONew(G);
	  I->shaderCGO->use_shader = true;
	  generate_shader_cgo = 1;
	} else {
	  I->shaderCGO->enable_shaders = true;
	  CGORenderGL(I->shaderCGO, NULL, NULL, NULL, info, &I->R);
	  return;
	}
      }
#ifdef _PYMOL_GL_CALLLISTS
      if(use_display_lists) {
	if(!I->R.displayList) {
	  I->R.displayList = glGenLists(1);
	  if(I->R.displayList) {
	    glNewList(I->R.displayList, GL_COMPILE_AND_EXECUTE);
	  }
	}
      }
#else
      (void) use_display_lists;
#endif

      sp = I->SP;

      if (generate_shader_cgo){
	if (use_sphere_shader){
	  /* Go through the ray tracing data, its easier (and available!) */
	  int variable_alpha = I->VariableAlphaFlag;
	  CGOAlpha(I->shaderCGO, alpha);
	  v = I->VC;
	  c = I->NC;
	  while(c--) {
	    if(variable_alpha) {
	      CGOAlpha(I->shaderCGO, v[3]);
	    }
	    CGOColorv(I->shaderCGO, v);
	    v += 4;
	    CGOSphere(I->shaderCGO, v, *(v + 3));
	    v += 4;
	  }
	  CGOAlpha(I->shaderCGO, 1.);
	} else {
	  while(c--) {
	    if((alpha == 1.0) && (!variable_alpha)) {
	      CGOAlpha(I->shaderCGO, 1.f);
	      CGOColorv(I->shaderCGO, v);
	    } else {
	      if(variable_alpha)
		CGOAlpha(I->shaderCGO, v[3]);
	      else
		CGOAlpha(I->shaderCGO, alpha);
	      CGOColor(I->shaderCGO, v[0], v[1], v[2]);
	    }
	    v += 4;
	    for(a = 0; a < sp->NStrip; a++) {
	      cc = sp->StripLen[a];
	      CGOBegin(I->shaderCGO, GL_TRIANGLE_STRIP);
	      while(cc--) {
		CGONormalv(I->shaderCGO, v);
		v += 3;
		CGOVertexv(I->shaderCGO, v);
		v += 3;
	      }
	      CGOEnd(I->shaderCGO);
	    }
	  }
	}
      } else {
	while(c--) {
#ifdef PURE_OPENGL_ES_2
	  /* TODO */
#else
	  if((alpha == 1.0) && (!variable_alpha)) {
	    glColor3fv(v);
	  } else {
	    if(variable_alpha)
	      glColor4f(v[0], v[1], v[2], v[3]);
	    else
	      glColor4f(v[0], v[1], v[2], alpha);
	  }
#endif
	  v += 4;
	  for(a = 0; a < sp->NStrip; a++) {
	    cc = sp->StripLen[a];
#ifdef PURE_OPENGL_ES_2
	    /* TODO */
#else
#ifdef _PYMOL_GL_DRAWARRAYS
	    {
	      int nverts = cc, pl;
	      ALLOCATE_ARRAY(GLfloat,vertVals,nverts*3)
	      ALLOCATE_ARRAY(GLfloat,normVals,nverts*3)
	      pl = 0;
	      while(cc--) {
		normVals[pl] = v[0]; normVals[pl+1] = v[1]; normVals[pl+2] = v[2];
		v += 3;
		vertVals[pl++] = v[0]; vertVals[pl++] = v[1]; vertVals[pl++] = v[2];
		v += 3;
	      }
	      glEnableClientState(GL_VERTEX_ARRAY);
	      glEnableClientState(GL_NORMAL_ARRAY);
	      glVertexPointer(3, GL_FLOAT, 0, vertVals);
	      glNormalPointer(GL_FLOAT, 0, normVals);
	      glDrawArrays(GL_TRIANGLE_STRIP, 0, nverts);
	      glDisableClientState(GL_NORMAL_ARRAY);
	      glDisableClientState(GL_VERTEX_ARRAY);
	      DEALLOCATE_ARRAY(vertVals)
	      DEALLOCATE_ARRAY(normVals)
	    }
#else
	    glBegin(GL_TRIANGLE_STRIP);
	    while(cc--) {
	      glNormal3fv(v);
	      v += 3;
	      glVertex3fv(v);
	      v += 3;
	    }
	    glEnd();
#endif
#endif
	  }
        }
      }

      if (use_shader) {
	if (generate_shader_cgo){
	  CGO *convertcgo = NULL;
	  CGOStop(I->shaderCGO);
#ifdef _PYMOL_CGO_DRAWARRAYS
	  convertcgo = CGOCombineBeginEnd(I->shaderCGO, 0);    
	  CGOFree(I->shaderCGO);    
	  I->shaderCGO = convertcgo;
#else
	  (void)convertcgo;
#endif
#ifdef _PYMOL_CGO_DRAWBUFFERS
	  if (use_sphere_shader){
	    convertcgo = CGOOptimizeSpheresToVBONonIndexed(I->shaderCGO, 0);
	  } else {
	    convertcgo = CGOOptimizeToVBOIndexed(I->shaderCGO, 0);
	  }
	  if(convertcgo){
	    CGOFree(I->shaderCGO);
	    I->shaderCGO = convertcgo;
	  }
#else
	  (void)convertcgo;
#endif
	}
	
	{
	  I->shaderCGO->enable_shaders = true;
	  CGORenderGL(I->shaderCGO, NULL, NULL, NULL, info, &I->R);
	}
      }
#ifdef _PYMOL_GL_CALLLISTS
      if (use_display_lists && I->R.displayList){
	glEndList();
	glCallList(I->R.displayList);      
      }
#endif
    }
  }
}

Rep *RepNonbondedSphereNew(CoordSet * cs, int state)
{
  PyMOLGlobals *G = cs->State.G;
  ObjectMolecule *obj = cs->Obj;
  int a, c, d, c1;
  float *v, *v0, *vc;
  float nb_spheres_size;
  int *q, *s;
  SphereRec *sp = G->Sphere->Sphere[0];
  int nb_spheres_quality;
  int *active = NULL;
  AtomInfoType *ai;
  int nSphere = 0;
  int a1;
  float *v1;
  float tmpColor[3];
  int variable_alpha = false;
  float transp =
    SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_nonbonded_transparency);

  OOAlloc(G, RepNonbondedSphere);

  active = Alloc(int, cs->NIndex);

  if(obj->RepVisCache[cRepNonbondedSphere])
    for(a = 0; a < cs->NIndex; a++) {
      ai = obj->AtomInfo + cs->IdxToAtm[a];
      active[a] = (!ai->bonded) && (ai->visRep[cRepNonbondedSphere]);
      if(active[a]) {
        if(ai->masked)
          active[a] = -1;
        else
          active[a] = 1;
      }
      if(active[a]) {
        nSphere++;
      }
    }
  if(!nSphere) {
    OOFreeP(I);
    FreeP(active);
    return (NULL);              /* skip if no dots are visible */
  }

  nb_spheres_size =
    SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_nb_spheres_size);

  /* get current dot sampling */
  nb_spheres_quality = SettingGet_i(G, cs->Setting, obj->Obj.Setting, cSetting_nb_spheres_quality);
  if(nb_spheres_quality < 0)
    nb_spheres_quality = 0;
  if(nb_spheres_quality > (NUMBER_OF_SPHERE_LEVELS-1))
    nb_spheres_quality = NUMBER_OF_SPHERE_LEVELS-1;
  sp = G->Sphere->Sphere[nb_spheres_quality];

  RepInit(G, &I->R);
  I->R.fRender = (void (*)(struct Rep *, RenderInfo *)) RepNonbondedSphereRender;
  I->R.fFree = (void (*)(struct Rep *)) RepNonbondedSphereFree;
  I->R.fRecolor = NULL;
  I->R.obj = (CObject *) (cs->Obj);
  I->R.cs = cs;
  I->shaderCGO = NULL;
  I->N = 0;
  I->NC = 0;
  I->V = NULL;
  I->VC = NULL;
  I->SP = NULL;
  I->NP = 0;
  I->VP = NULL;
  I->R.P = NULL;

  /* raytracing primitives */

  I->VC = (float *) mmalloc(sizeof(float) * nSphere * 8);
  ErrChkPtr(G, I->VC);
  I->NC = 0;

  v = I->VC;

  for(a = 0; a < cs->NIndex; a++) {
    if(active[a]) {
      float at_transp;
      ai = obj->AtomInfo + cs->IdxToAtm[a];
      if(AtomInfoGetSetting_f(G, ai, cSetting_nonbonded_transparency, transp, &at_transp))
        variable_alpha = true;

      I->NC++;
      c1 = *(cs->Color + a);
      v0 = cs->Coord + 3 * a;
      if(ColorCheckRamped(G, c1)) {
        ColorGetRamped(G, c1, v0, tmpColor, state);
        vc = tmpColor;
      } else {
        vc = ColorGet(G, c1);
      }
      *(v++) = *(vc++);
      *(v++) = *(vc++);
      *(v++) = *(vc++);
      *(v++) = 1.0F - at_transp;
      *(v++) = *(v0++);
      *(v++) = *(v0++);
      *(v++) = *(v0++);
      *(v++) = nb_spheres_size;
    }
  }

  I->VariableAlphaFlag = variable_alpha;
  if(I->NC)
    I->VC = ReallocForSure(I->VC, float, (v - I->VC));
  else
    I->VC = ReallocForSure(I->VC, float, 1);

  I->V = (float *) mmalloc(sizeof(float) * nSphere * (4 + sp->NVertTot * 6));
  ErrChkPtr(G, I->V);

  /* rendering primitives */

  I->N = 0;
  I->SP = sp;
  v = I->V;

  for(a = 0; a < cs->NIndex; a++) {
    if(active[a]) {
      float at_transp;
      ai = obj->AtomInfo + cs->IdxToAtm[a];
      c1 = *(cs->Color + a);
      v0 = cs->Coord + 3 * a;
      vc = ColorGet(G, c1);
      if(AtomInfoGetSetting_f(G, ai, cSetting_nonbonded_transparency, transp, &at_transp))
        variable_alpha = true;

      if(ColorCheckRamped(G, c1)) {
        ColorGetRamped(G, c1, v0, tmpColor, state);
        vc = tmpColor;
      } else {
        vc = ColorGet(G, c1);
      }

      *(v++) = *(vc++);
      *(v++) = *(vc++);
      *(v++) = *(vc++);
      *(v++) = 1.0F - at_transp;

      q = sp->Sequence;
      s = sp->StripLen;

      for(d = 0; d < sp->NStrip; d++) {
        for(c = 0; c < (*s); c++) {
          *(v++) = sp->dot[*q][0];      /* normal */
          *(v++) = sp->dot[*q][1];
          *(v++) = sp->dot[*q][2];
          *(v++) = v0[0] + nb_spheres_size * sp->dot[*q][0];     /* point */
          *(v++) = v0[1] + nb_spheres_size * sp->dot[*q][1];
          *(v++) = v0[2] + nb_spheres_size * sp->dot[*q][2];
          q++;
        }
        s++;
      }
      I->N++;
    }
  }

  if(I->N)
    I->V = ReallocForSure(I->V, float, (v - I->V));
  else
    I->V = ReallocForSure(I->V, float, 1);

  /* use pickable representation from nonbonded */
  if(SettingGet_f(G, cs->Setting, obj->Obj.Setting, cSetting_pickable)) {
    I->VP = (float *) mmalloc(sizeof(float) * nSphere * 18);
    ErrChkPtr(G, I->VP);

    I->R.P = Alloc(Pickable, cs->NIndex + 1);
    ErrChkPtr(G, I->R.P);

    v = I->VP;

    for(a = 0; a < cs->NIndex; a++)
      if(active[a] > 0) {

        a1 = cs->IdxToAtm[a];

        if(!obj->AtomInfo[a1].masked) {
          I->NP++;

          I->R.P[I->NP].index = a1;
          I->R.P[I->NP].bond = -1;
          v1 = cs->Coord + 3 * a;

          *(v++) = v1[0] - nb_spheres_size;
          *(v++) = v1[1];
          *(v++) = v1[2];
          *(v++) = v1[0] + nb_spheres_size;
          *(v++) = v1[1];
          *(v++) = v1[2];
          *(v++) = v1[0];
          *(v++) = v1[1] - nb_spheres_size;
          *(v++) = v1[2];
          *(v++) = v1[0];
          *(v++) = v1[1] + nb_spheres_size;
          *(v++) = v1[2];
          *(v++) = v1[0];
          *(v++) = v1[1];
          *(v++) = v1[2] - nb_spheres_size;
          *(v++) = v1[0];
          *(v++) = v1[1];
          *(v++) = v1[2] + nb_spheres_size;
        }
      }
    I->R.P = Realloc(I->R.P, Pickable, I->NP + 1);
    I->R.context.object = (void *) obj;
    I->R.context.state = state;

    I->R.P[0].index = I->NP;
    I->VP = Realloc(I->VP, float, I->NP * 21);
  }

  FreeP(active);
  return ((void *) (struct Rep *) I);
}
