
/* 
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"ObjectGadget.h"
#include"ObjectGadgetRamp.h"
#include"GadgetSet.h"
#include"Base.h"
#include"MemoryDebug.h"
#include"CGO.h"
#include"Scene.h"
#include"Setting.h"
#include"PConv.h"
#include"main.h"
#include"Color.h"
#include"VFont.h"

CGO *ObjectGadgetPyListFloatToCGO(PyObject * list);

int ObjectGadgetGetVertex(ObjectGadget * I, int index, int base, float *v)
{
  GadgetSet *gs;
  int ok = false;
  if(I->CurGSet < I->NGSet) {
    gs = I->GSet[I->CurGSet];
    if(gs) {
      ok = GadgetSetGetVertex(gs, index, base, v);
    }
  }
  return (ok);
}

int ObjectGadgetSetVertex(ObjectGadget * I, int index, int base, float *v)
{
  GadgetSet *gs;
  int ok = false;
  if(I->CurGSet < I->NGSet) {
    gs = I->GSet[I->CurGSet];
    if(gs) {
      ok = GadgetSetSetVertex(gs, index, base, v);
    }
  }
  if (index) // if 0 - xyz doesn't change, 1 - mouse position when changing colors
    I->Changed = true;
  return (ok);
}


/* in current state */
ObjectGadget *ObjectGadgetTest(PyMOLGlobals * G)
{
  ObjectGadget *I = NULL;
  GadgetSet *gs = NULL;
  CGO *cgo = NULL;
  int a;

  float coord[] = {
    0.5F, 0.5F, 0.0F,
    0.0F, 0.0F, 0.0F,
    0.3F, 0.0F, 0.0F,
    0.0F, -0.3F, 0.0F,
    0.3F, -0.3F, 0.0F,
    0.03F, -0.03F, 0.03F,
    0.27F, -0.03F, 0.03F,
    0.03F, -0.27F, 0.03F,
    0.27F, -0.27F, 0.03F,
    0.02F, -0.02F, 0.01F,
    0.28F, -0.02F, 0.01F,
    0.02F, -0.28F, 0.01F,
    0.28F, -0.28F, 0.01F,
  };

  float normal[] = {
    1.0, 0.0, 0.0,
    0.0, 1.0, 0.0,
    0.0, 0.0, 1.0,
    -1.0, 0.0, 0.0,
    0.0, -1.0, 0.0,
  };

  I = ObjectGadgetNew(G);
  gs = GadgetSetNew(G);

  gs->NCoord = 13;
  gs->Coord = VLAlloc(float, gs->NCoord * 3);
  for(a = 0; a < gs->NCoord * 3; a++) {
    gs->Coord[a] = coord[a];
  }

  gs->NNormal = 5;
  gs->Normal = VLAlloc(float, gs->NNormal * 3);
  for(a = 0; a < gs->NNormal * 3; a++) {
    gs->Normal[a] = normal[a];
  }

  cgo = CGONewSized(G, 100);
  CGOColor(cgo, 1.0, 1.0, 1.0);

  /* top */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 5.0, 0.0);
  CGOVertex(cgo, 1.0, 6.0, 0.0);

  CGONormal(cgo, 2.0, 1.0, 0.0);
  CGOVertex(cgo, 1.0, 1.0, 0.0);
  CGOVertex(cgo, 1.0, 2.0, 0.0);
  CGOEnd(cgo);

  /* bottom */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 4.0, 0.0);
  CGOVertex(cgo, 1.0, 3.0, 0.0);
  CGOVertex(cgo, 1.0, 4.0, 0.0);

  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 7.0, 0.0);
  CGOVertex(cgo, 1.0, 8.0, 0.0);
  CGOEnd(cgo);

  /* left */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 3.0, 0.0);
  CGOVertex(cgo, 1.0, 1.0, 0.0);
  CGOVertex(cgo, 1.0, 3.0, 0.0);

  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 5.0, 0.0);
  CGOVertex(cgo, 1.0, 7.0, 0.0);
  CGOEnd(cgo);

  /* right */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 6.0, 0.0);
  CGOVertex(cgo, 1.0, 8.0, 0.0);

  CGONormal(cgo, 2.0, 0.0, 0.0);
  CGOVertex(cgo, 1.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 4.0, 0.0);
  CGOEnd(cgo);

  CGOColor(cgo, 1.0, 0.0, 0.0);

  /* center */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 5.0, 0.0);
  CGOVertex(cgo, 1.0, 7.0, 0.0);
  CGOVertex(cgo, 1.0, 6.0, 0.0);
  CGOVertex(cgo, 1.0, 8.0, 0.0);
  CGOEnd(cgo);

  CGOColor(cgo, 0.0, 1.0, 0.0);
  /* backr */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGONormal(cgo, 2.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 9.0, 0.0);
  CGOVertex(cgo, 1.0, 10.0, 0.0);
  CGOVertex(cgo, 1.0, 11.0, 0.0);
  CGOVertex(cgo, 1.0, 12.0, 0.0);
  CGOEnd(cgo);
  CGOStop(cgo);

  gs->ShapeCGO = cgo;

  cgo = CGONewSized(G, 100);
  CGODotwidth(cgo, 5);

  CGOPickColor(cgo, 0, cPickableGadget);

  /* top */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGOVertex(cgo, 1.0, 1.0, 0.0);
  CGOVertex(cgo, 1.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 5.0, 0.0);
  CGOVertex(cgo, 1.0, 6.0, 0.0);
  CGOEnd(cgo);

  /* bottom */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGOVertex(cgo, 1.0, 3.0, 0.0);
  CGOVertex(cgo, 1.0, 4.0, 0.0);
  CGOVertex(cgo, 1.0, 7.0, 0.0);
  CGOVertex(cgo, 1.0, 8.0, 0.0);
  CGOEnd(cgo);

  /* left */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGOVertex(cgo, 1.0, 1.0, 0.0);
  CGOVertex(cgo, 1.0, 3.0, 0.0);
  CGOVertex(cgo, 1.0, 5.0, 0.0);
  CGOVertex(cgo, 1.0, 7.0, 0.0);
  CGOEnd(cgo);

  /* right */
  CGOBegin(cgo, GL_TRIANGLE_STRIP);
  CGOVertex(cgo, 1.0, 6.0, 0.0);
  CGOVertex(cgo, 1.0, 8.0, 0.0);
  CGOVertex(cgo, 1.0, 2.0, 0.0);
  CGOVertex(cgo, 1.0, 4.0, 0.0);
  CGOEnd(cgo);
  CGOEnd(cgo);
  CGOStop(cgo);
  gs->PickShapeCGO = cgo;

  gs->Obj = I;
  gs->State = 0;

  I->GSet[0] = gs;
  I->NGSet = 1;
  I->Obj.Context = 1;
  gs->fUpdate(gs);
  ObjectGadgetUpdateExtents(I);
  return (I);

}

void ObjectGadgetUpdateExtents(ObjectGadget * I)
{
  float maxv[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
  float minv[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
  int a;
  GadgetSet *ds;

  /* update extents */
  copy3f(maxv, I->Obj.ExtentMin);
  copy3f(minv, I->Obj.ExtentMax);
  I->Obj.ExtentFlag = false;
  for(a = 0; a < I->NGSet; a++) {
    ds = I->GSet[a];
    if(ds) {
      if(GadgetSetGetExtent(ds, I->Obj.ExtentMin, I->Obj.ExtentMax))
        I->Obj.ExtentFlag = true;
    }
  }
}

#ifndef _PYMOL_NOPY
static PyObject *ObjectGadgetGSetAsPyList(ObjectGadget * I)
{
  PyObject *result = NULL;
  int a;
  result = PyList_New(I->NGSet);
  for(a = 0; a < I->NGSet; a++) {
    if(I->GSet[a]) {
      PyList_SetItem(result, a, GadgetSetAsPyList(I->GSet[a]));
    } else {
      PyList_SetItem(result, a, PConvAutoNone(Py_None));
    }
  }
  return (PConvAutoNone(result));

}
#endif
#ifndef _PYMOL_NOPY
static int ObjectGadgetGSetFromPyList(ObjectGadget * I, PyObject * list, int version)
{

  int ok = true;
  int a;
  if(ok)
    ok = PyList_Check(list);
  if(ok) {
    VLACheck(I->GSet, GadgetSet *, I->NGSet);
    for(a = 0; a < I->NGSet; a++) {
      if(ok)
        ok = GadgetSetFromPyList(I->Obj.G, PyList_GetItem(list, a), &I->GSet[a], version);
      if(ok && I->GSet[a]) {
        I->GSet[a]->Obj = I;
        I->GSet[a]->State = a;
      }
    }
  }
  return (ok);
}
#endif
int ObjectGadgetInitFromPyList(PyMOLGlobals * G, PyObject * list, ObjectGadget * I,
                               int version)
{
#ifdef _PYMOL_NOPY
  return 0;
#else

  int ok = true;
  int ll;
  if(ok)
    ok = (I != NULL) && (list != NULL);
  if(ok)
    ok = PyList_Check(list);
  if(ok)
    ll = PyList_Size(list);
  /* TO SUPPORT BACKWARDS COMPATIBILITY...
     Always check ll when adding new PyList_GetItem's */
  if(ok)
    ok = ObjectFromPyList(G, PyList_GetItem(list, 0), &I->Obj);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->GadgetType);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->NGSet);
  if(ok)
    ok = ObjectGadgetGSetFromPyList(I, PyList_GetItem(list, 3), version);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(list, 4), &I->CurGSet);

  /*  ObjectGadgetInvalidateRep(I,cRepAll); */
  if(ok) {
    ObjectGadgetUpdateExtents(I);
  } else {
    /* cleanup? */
  }
  return (ok);
#endif
}

int ObjectGadgetNewFromPyList(PyMOLGlobals * G, PyObject * list, ObjectGadget ** result,
                              int version)
{
#ifdef _PYMOL_NOPY
  return 0;
#else

  int ok = true;
  ObjectGadget *I = NULL;
  int gadget_type = -1;
  PyObject *plain;
  (*result) = NULL;

  if(ok)
    ok = (list != NULL);
  if(ok)
    ok = PyList_Check(list);

  /* NOTE there is a serious screw-up here...ramp gadgets aren't saved right, but
     we've got to maintain backward compat...ugh */

  if(ok)
    ok = ((plain = PyList_GetItem(list, 0)) != NULL);
  if(ok)
    ok = PyList_Check(plain);
  if(ok)
    ok = PConvPyIntToInt(PyList_GetItem(plain, 1), &gadget_type);
  if(ok)
    switch (gadget_type) {      /* call the right routine to restore the gadget! */
    case cGadgetRamp:
      ok = ObjectGadgetRampNewFromPyList(G, list, (ObjectGadgetRamp **) result, version);
      break;
    case cGadgetPlain:
      I = ObjectGadgetNew(G);
      if(ok)
        ok = (I != NULL);
      if(ok)
        ok = ObjectGadgetInitFromPyList(G, list, I, version);
      if(ok)
        (*result) = I;
      break;
    default:
      ok = false;
      break;
    }
  return (ok);
#endif
}

PyObject *ObjectGadgetPlainAsPyList(ObjectGadget * I)
{
#ifdef _PYMOL_NOPY
  return NULL;
#else

  PyObject *result = NULL;

  /* first, dump the atoms */

  result = PyList_New(5);
  PyList_SetItem(result, 0, ObjectAsPyList(&I->Obj));
  PyList_SetItem(result, 1, PyInt_FromLong(I->GadgetType));
  PyList_SetItem(result, 2, PyInt_FromLong(I->NGSet));
  PyList_SetItem(result, 3, ObjectGadgetGSetAsPyList(I));
  PyList_SetItem(result, 4, PyInt_FromLong(I->CurGSet));
  return (PConvAutoNone(result));
#endif
}

PyObject *ObjectGadgetAsPyList(ObjectGadget * I)
{
#ifdef _PYMOL_NOPY
  return NULL;
#else

  PyObject *result = NULL;

  /* first, dump the atoms */

  switch (I->GadgetType) {
  case cGadgetRamp:
    result = ObjectGadgetRampAsPyList((ObjectGadgetRamp *) I);
    break;
  case cGadgetPlain:
    result = ObjectGadgetPlainAsPyList(I);
    break;
  }
  return (PConvAutoNone(result));
#endif
}

void ObjectGadgetPurge(ObjectGadget * I)
{
  int a;

  SceneObjectDel(I->Obj.G, (CObject *) I, false);
  for(a = 0; a < I->NGSet; a++)
    if(I->GSet[a]) {
      if(I->GSet[a]->fFree)
        I->GSet[a]->fFree(I->GSet[a]);
      I->GSet[a] = NULL;
    }
  VLAFreeP(I->GSet);
  ObjectPurge(&I->Obj);
}

void ObjectGadgetFree(ObjectGadget * I)
{
  ObjectGadgetPurge(I);
  OOFreeP(I);
}

void ObjectGadgetUpdateStates(ObjectGadget * I)
{
  int a;
  OrthoBusyPrime(I->Obj.G);
  for(a = 0; a < I->NGSet; a++)
    if(I->GSet[a]) {
      OrthoBusySlow(I->Obj.G, a, I->NGSet);
      /*           printf(" ObjectGadget: updating state %d of \"%s\".\n" , a+1, I->Obj.Name); */
      if(I->GSet[a]->fUpdate)
        I->GSet[a]->fUpdate(I->GSet[a]);
    }
}


/*========================================================================*/
void ObjectGadgetUpdate(ObjectGadget * I)
{
  if(I->Changed) {
    ObjectGadgetUpdateStates(I);
    ObjectGadgetUpdateExtents(I);
    I->Changed = false;
  }
}


/*========================================================================*/

static int ObjectGadgetGetNState(ObjectGadget * I)
{
  return (I->NGSet);
}


/*========================================================================*/
static void ObjectGadgetRender(ObjectGadget * I, RenderInfo * info)
{
  int state = info->state;
  CRay *ray = info->ray;
  int pass = info->pass;
  int a;
  if(!pass) {

    ObjectPrepareContext(&I->Obj, ray);
    if(state < 0) {
      for(a = 0; a < I->NGSet; a++)
        if(I->GSet[a])
          if(I->GSet[a]->fRender)
            I->GSet[a]->fRender(I->GSet[a], info);
    } else if(state < I->NGSet) {
      I->CurGSet = state;
      if(I->GSet[I->CurGSet]) {
        if(I->GSet[I->CurGSet]->fRender)
          I->GSet[I->CurGSet]->fRender(I->GSet[I->CurGSet], info);
      }
    } else if(I->NGSet == 1) {  /* if only one coordinate set, assume static */
      if(I->GSet[0]->fRender)
        I->GSet[0]->fRender(I->GSet[0], info);
      I->CurGSet = 0;
    }
  }
}


/*========================================================================*/
void ObjectGadgetInit(PyMOLGlobals * G, ObjectGadget * I)
{
  ObjectInit(G, (CObject *) I);

  I->Obj.type = cObjectGadget;
  I->GSet = VLACalloc(GadgetSet *, 10);        /* auto-zero */
  I->NGSet = 0;
  I->Changed = true;

  I->Obj.fFree = (void (*)(CObject *)) ObjectGadgetFree;
  I->Obj.fUpdate = (void (*)(CObject *)) ObjectGadgetUpdate;
  I->Obj.fRender = (void (*)(CObject *, RenderInfo * info)) ObjectGadgetRender;
  I->Obj.fGetNFrame = (int (*)(CObject *)) ObjectGadgetGetNState;
  I->Obj.fDescribeElement = NULL;
  I->CurGSet = 0;
}


/*========================================================================*/
ObjectGadget *ObjectGadgetNew(PyMOLGlobals * G)
{
  OOAlloc(G, ObjectGadget);

  ObjectGadgetInit(G, I);
  return (I);
}

