
/* 
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"OOMac.h"
#include"RepDistLabel.h"
#include"Color.h"
#include"Scene.h"
#include"main.h"
#include"Vector.h"
#include"Setting.h"
#include"PyMOLObject.h"
#include"Text.h"

#include"Word.h"

typedef char DistLabel[8];

typedef struct RepDistLabel {
  Rep R;
  float *V;
  int N;
  DistLabel *L;
  CObject *Obj;
  DistSet *ds;
  int OutlineColor;
} RepDistLabel;

#include"ObjectDist.h"

void RepDistLabelFree(RepDistLabel * I);

void RepDistLabelFree(RepDistLabel * I)
{
  VLAFreeP(I->V);
  VLAFreeP(I->L);
  RepPurge(&I->R);
  OOFreeP(I);
}

static void RepDistLabelRender(RepDistLabel * I, RenderInfo * info)
{
  CRay *ray = info->ray;
  Picking **pick = info->pick;
  PyMOLGlobals *G = I->R.G;
  float *v = I->V;
  int c = I->N;
  DistLabel *l = I->L;
  int n = 0;
  int color;
  int font_id = SettingGet_i(G, I->ds->Setting, I->Obj->Setting, cSetting_label_font_id);
  float font_size = SettingGet_f(G, I->ds->Setting, I->Obj->Setting, cSetting_label_size);

  if(ray) {

    TextSetOutlineColor(G, I->OutlineColor);
    color = SettingGet_color(G, I->ds->Setting, I->Obj->Setting, cSetting_label_color);

    if((color >= 0) || (color == cColorFront) || (color == cColorBack))
      TextSetColor(G, ColorGet(G, color));
    else
      TextSetColor(G, ColorGet(G, I->Obj->Color));

    while(c--) {
      TextSetPos(G, v);
      TextRenderRay(G, ray, font_id, l[n], font_size, v + 3);
      v += 6;
      n++;
    }
  } else if(G->HaveGUI && G->ValidContext) {
    if(pick) {
      Pickable *p = I->R.P;
      int i;
      if(c) {
        int float_text = (int) SettingGet(G, cSetting_float_labels);
        if(float_text)
          glDisable(GL_DEPTH_TEST);

        i = (*pick)->src.index;
        while(c--) {
          if(*l) {
            int first_pass = (!(*pick)[0].src.bond);
            i++;
            TextSetPos(G, v);
            TextSetPickColor(G, first_pass, i);
            if(first_pass) {
              VLACheck((*pick), Picking, i);
              p++;
              (*pick)[i].src = *p;      /* copy object and atom info */
              (*pick)[i].context = I->R.context;
            }
            TextRenderOpenGL(G, info, font_id, l[n], font_size, v + 3);
            n++;
          }
          v += 6;
        }
        if(float_text)
          glEnable(GL_DEPTH_TEST);
        (*pick)[0].src.index = i;       /* pass the count */
      }
    } else {
      int float_text = SettingGet_i(G, I->ds->Setting,
                                    I->Obj->Setting,
                                    cSetting_float_labels);
      if(float_text)
        glDisable(GL_DEPTH_TEST);

#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
      if(!info->line_lighting)
        glDisable(GL_LIGHTING);
#endif

      TextSetOutlineColor(G, I->OutlineColor);
      color = SettingGet_color(G, I->ds->Setting, I->Obj->Setting, cSetting_label_color);

      if((color >= 0) || (color == cColorFront) || (color == cColorBack))
        TextSetColor(G, ColorGet(G, color));
      else
        TextSetColor(G, ColorGet(G, I->Obj->Color));
      while(c--) {

        TextSetPos(G, v);
        TextRenderOpenGL(G, info, font_id, l[n], font_size, v + 3);
        v += 6;
        n++;
      }
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
      glEnable(GL_LIGHTING);
#endif
      if(float_text)
        glEnable(GL_DEPTH_TEST);
    }
  }
}

Rep *RepDistLabelNew(DistSet * ds, int state)
{
  PyMOLGlobals *G = ds->State.G;
  int a;
  int n = 0;
  float *v, *v1, *v2, *v3, d[3], di;
  char buffer[255];
  float *lab_pos =
    SettingGet_3fv(G, ds->Setting, ds->Obj->Obj.Setting, cSetting_label_position);
  int default_digits =
    SettingGet_i(G, ds->Setting, ds->Obj->Obj.Setting, cSetting_label_digits);
  Pickable *rp = NULL;
  OOAlloc(G, RepDistLabel);

  if(!(ds->NIndex || ds->NAngleIndex || ds->NDihedralIndex)) {
    ds->NLabel = 0;
    VLAFreeP(ds->LabCoord);
    VLAFreeP(ds->LabPos);
    OOFreeP(I);
    return (NULL);
  }

  if(default_digits < 0)
    default_digits = 0;
  if(default_digits > 10)
    default_digits = 10;

  RepInit(G, &I->R);

  I->R.fRender = (void (*)(struct Rep *, RenderInfo *)) RepDistLabelRender;
  I->R.fFree = (void (*)(struct Rep *)) RepDistLabelFree;
  I->R.fRecolor = NULL;

  I->N = 0;
  I->V = NULL;
  I->R.P = NULL;
  I->Obj = (CObject *) ds->Obj;
  I->ds = ds;
  I->R.context.object = (void *) ds->Obj;
  I->R.context.state = state;

  I->OutlineColor =
    SettingGet_i(G, ds->Setting, I->Obj->Setting, cSetting_label_outline_color);

  if(ds->NIndex || ds->NAngleIndex || ds->NDihedralIndex) {
    float *lc;

    ds->NLabel = (ds->NIndex / 2 + ds->NAngleIndex / 5 + ds->NDihedralIndex / 6);

    if(!ds->LabCoord) {         /* store label coordinates */
      ds->LabCoord = VLAlloc(float, 3 * ds->NLabel);
    } else {
      VLACheck(ds->LabCoord, float, 3 * ds->NLabel);
    }

    if(ds->LabPos) {            /* make sure this VLA covers all labels */
      VLACheck(ds->LabPos, LabPosType, ds->NLabel);
    }

    if(SettingGet_f(G, ds->Setting, ds->Obj->Obj.Setting, cSetting_pickable)) {
      I->R.P = Alloc(Pickable, ds->NLabel + 1);
      ErrChkPtr(G, I->R.P);
      rp = I->R.P + 1;          /* skip first record! */
    }

    I->V = VLAlloc(float, 3 * (ds->NIndex / 2 + ds->NAngleIndex / 5) + 1);
    I->L = VLAlloc(DistLabel, (ds->NIndex / 2 + ds->NAngleIndex / 5) + 1);

    n = 0;
    lc = ds->LabCoord;

    if(ds->NIndex) {
      int digits = SettingGet_i(G, ds->Setting, ds->Obj->Obj.Setting,
                                cSetting_label_distance_digits);
      WordType format;
      if(digits < 0)
        digits = default_digits;
      if(digits > 10)
        digits = 10;
      sprintf(format, "%c0.%df", '%', digits);
      for(a = 0; a < ds->NIndex; a = a + 2) {
        v1 = ds->Coord + 3 * a;
        v2 = ds->Coord + 3 * (a + 1);
        average3f(v2, v1, d);
        di = (float) diff3f(v1, v2);
        sprintf(buffer, format, di);

        VLACheck(I->V, float, 6 * n + 5);
        VLACheck(I->L, DistLabel, n);
        v = I->V + 6 * n;
        strcpy(I->L[n], buffer);
        copy3f(d, v);
        *(lc++) = v[0];
        *(lc++) = v[1];
        *(lc++) = v[2];
        if(ds->LabPos) {
          LabPosType *lp = ds->LabPos + n;
          switch (lp->mode) {
          case 1:
            add3f(lp->offset, v, v);
            copy3f(lab_pos, v + 3);
            break;
          default:
            copy3f(lab_pos, v + 3);
            break;
          }
        } else {
          copy3f(lab_pos, v + 3);
        }

        if(rp) {
          rp->index = n;        /* label index */
          rp->bond = cPickableLabel;    /* label indicator */
          rp++;
        }

        n++;
      }
    }

    if(ds->NAngleIndex) {

      float d1[3], d2[3], n1[3], n2[3];
      float avg[3];

      float l1, l2;
      float radius;
      int digits = SettingGet_i(G, ds->Setting, ds->Obj->Obj.Setting,
                                cSetting_label_angle_digits);
      WordType format;
      if(digits < 0)
        digits = default_digits;
      if(digits > 10)
        digits = 10;
      sprintf(format, "%c0.%df", '%', digits);

      for(a = 0; a < ds->NAngleIndex; a = a + 5) {
        v1 = ds->AngleCoord + 3 * a;
        v2 = ds->AngleCoord + 3 * (a + 1);
        v3 = ds->AngleCoord + 3 * (a + 2);
        subtract3f(v1, v2, d1);
        subtract3f(v3, v2, d2);

        normalize23f(d1, n1);
        normalize23f(d2, n2);

        average3f(n1, n2, avg);

        l1 = (float) length3f(d1);
        l2 = (float) length3f(d2);

        if(l1 > l2)
          radius = l2;
        else
          radius = l1;
        radius *=
          SettingGet_f(G, ds->Setting, ds->Obj->Obj.Setting,
                       cSetting_angle_size) * SettingGet_f(G, ds->Setting,
                                                           ds->Obj->Obj.Setting,
                                                           cSetting_angle_label_position);

        normalize3f(avg);
        if((avg[0] == 0.0F) && (avg[1] == 0.0F) && (avg[2] == 0.0F))
          avg[0] = 1.0F;

        scale3f(avg, radius, avg);
        add3f(v2, avg, avg);

        di = (float) (180.0F * get_angle3f(d1, d2) / PI);
        sprintf(buffer, format, di);

        VLACheck(I->V, float, 6 * n + 5);
        VLACheck(I->L, DistLabel, n);
        v = I->V + 6 * n;
        strcpy(I->L[n], buffer);
        copy3f(avg, v);
        *(lc++) = v[0];
        *(lc++) = v[1];
        *(lc++) = v[2];
        if(ds->LabPos) {
          LabPosType *lp = ds->LabPos + n;
          switch (lp->mode) {
          case 1:
            add3f(lp->offset, v, v);
            copy3f(lab_pos, v + 3);
            break;
          default:
            copy3f(lab_pos, v + 3);
            break;
          }
        } else {
          copy3f(lab_pos, v + 3);
        }
        if(rp) {
          rp->index = n;        /* label index */
          rp->bond = cPickableLabel;    /* label indicator */
          rp++;
        }
        n++;
      }
    }

    if(ds->NDihedralIndex) {

      float d12[3], d32[3], d43[3], n32[3];
      float p12[3], p43[3], np12[3], np43[3];

      float a32[3];
      float l1, l2;
      float radius;
      float dihedral_size =
        SettingGet_f(G, ds->Setting, ds->Obj->Obj.Setting, cSetting_dihedral_size);
      float dihedral_label_position = SettingGet_f(G, ds->Setting, ds->Obj->Obj.Setting,
                                                   cSetting_dihedral_label_position);

      float *v4, *v5, *v6;
      float avg[3];
      int digits = SettingGet_i(G, ds->Setting, ds->Obj->Obj.Setting,
                                cSetting_label_dihedral_digits);
      WordType format;
      if(digits < 0)
        digits = default_digits;
      if(digits > 10)
        digits = 10;
      sprintf(format, "%c0.%df", '%', digits);

      for(a = 0; a < ds->NDihedralIndex; a = a + 6) {
        v1 = ds->DihedralCoord + 3 * a;
        v2 = v1 + 3;
        v3 = v1 + 6;
        v4 = v1 + 9;
        v5 = v1 + 12;
        v6 = v1 + 15;

        subtract3f(v1, v2, d12);
        subtract3f(v3, v2, d32);
        subtract3f(v4, v3, d43);

        normalize23f(d32, n32);

        remove_component3f(d12, n32, p12);
        remove_component3f(d43, n32, p43);

        average3f(v2, v3, a32);

        l1 = (float) length3f(p12);
        l2 = (float) length3f(p43);

        if(l1 > l2)
          radius = l2;
        else
          radius = l1;
        radius *= dihedral_size * dihedral_label_position;

        normalize23f(p12, np12);
        normalize23f(p43, np43);

        average3f(np12, np43, avg);

        normalize3f(avg);
        if((avg[0] == 0.0F) && (avg[1] == 0.0F) && (avg[2] == 0.0F))
          copy3f(np12, avg);

        scale3f(avg, radius, avg);
        add3f(a32, avg, avg);

        di = (float) (180.0F * get_dihedral3f(v1, v2, v3, v4) / PI);
        sprintf(buffer, format, di);

        VLACheck(I->V, float, 6 * n + 5);
        VLACheck(I->L, DistLabel, n);
        v = I->V + 6 * n;
        strcpy(I->L[n], buffer);
        copy3f(avg, v);
        *(lc++) = v[0];
        *(lc++) = v[1];
        *(lc++) = v[2];
        if(ds->LabPos) {
          LabPosType *lp = ds->LabPos + n;
          switch (lp->mode) {
          case 1:
            add3f(lp->offset, v, v);
            copy3f(lab_pos, v + 3);
            break;
          default:
            copy3f(lab_pos, v + 3);
            break;
          }
        } else {
          copy3f(lab_pos, v + 3);
        }
        copy3f(lab_pos, v + 3);

        if(rp) {
          rp->index = n;        /* label index */
          rp->bond = cPickableLabel;    /* label indicator */
          rp++;
        }
        n++;
      }
    }
  }

  I->N = n;

  if(rp) {
    I->R.P = ReallocForSure(I->R.P, Pickable, (rp - I->R.P));
    I->R.P[0].index = I->N;     /* unnec? */
  }

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