/* 
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * 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. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * 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.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * This is a new part of Blender.
 *
 * Contributor(s): Michel Selten
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
*/

#include "Object.h"

/*****************************************************************************/
/* Function:              M_Object_New                                       */
/* Python equivalent:     Blender.Object.New                                 */
/*****************************************************************************/
PyObject *M_Object_New(PyObject *self, PyObject *args)
{
    struct Object   * object;
    C_BlenObject    * blen_object;
    int               type;
    char              name[32];

    printf ("In Object_New()\n");

    if (!PyArg_ParseTuple(args, "i", &type))
    {
        PythonReturnErrorObject (PyExc_TypeError,
                    "type expected");
        return (NULL);
    }

    /* Create a new object. */
    switch (type)
    {
        case OB_MESH:       strcpy (name, "Mesh");      break;
        case OB_CURVE:      strcpy (name, "Curve");     break;
        case OB_SURF:       strcpy (name, "Surf");      break;
        case OB_FONT:       strcpy (name, "Text");      break;
        case OB_MBALL:      strcpy (name, "Mball");     break;
        case OB_CAMERA:     strcpy (name, "Camera");    break;
        case OB_LAMP:       strcpy (name, "Lamp");      break;
        case OB_IKA:        strcpy (name, "Ika");       break;
        case OB_LATTICE:    strcpy (name, "Lattice");   break;
        case OB_WAVE:       strcpy (name, "Wave");      break;
        case OB_ARMATURE:   strcpy (name, "Armature");  break;
        default:            strcpy (name, "Empty");
    }

    object = alloc_libblock (&(G.main->object), ID_OB, name);

    object->flag = 0;
    object->type = type;

    /* transforms */
    QuatOne(object->quat);
    QuatOne(object->dquat);
    
    object->col[3]= 1.0;    // alpha 

    object->size[0] = object->size[1] = object->size[2] = 1.0;
    object->loc[0] = object->loc[1] = object->loc[2] = 0.0;
    Mat4One(object->parentinv);
    Mat4One(object->obmat);
    object->dt = OB_SHADED; // drawtype

    if (U.flag & MAT_ON_OB)
    {
        object->colbits = -1;
    }
    switch (object->type)
    {
        case OB_CAMERA: /* fall through. */
        case OB_LAMP:
            object->trackflag = OB_NEGZ;
            object->upflag = OB_POSY;
            break;
        default:
            object->trackflag = OB_POSY;
            object->upflag = OB_POSZ;
    }
    object->ipoflag = OB_OFFS_OB + OB_OFFS_PARENT;

    /* duplivert settings */
    object->dupon = 1;
    object->dupoff = 0;
    object->dupsta = 1;
    object->dupend = 100;

    /* Gameengine defaults*/
    object->mass = 1.0;
    object->inertia = 1.0;
    object->formfactor = 0.4;
    object->damping = 0.04;
    object->rdamping = 0.1;
    object->anisotropicFriction[0] = 1.0;
    object->anisotropicFriction[1] = 1.0;
    object->anisotropicFriction[2] = 1.0;
    object->gameflag = OB_PROP;
    
    object->lay = 1; // Layer, by default visible

    switch(type)
    {
        case OB_MESH:
            object->data = add_mesh();
            G.totmesh++;
            break;
        case OB_CAMERA:
            object->data = add_camera();
            break;
        case OB_LAMP:
            object->data = add_lamp();
            G.totlamp++;
            break;

    /* TODO the following types will be supported later
        case OB_CURVE:
            object->data = add_curve(OB_CURVE);
            G.totcurve++;
            break;
        case OB_SURF:
            object->data = add_curve(OB_SURF);
            G.totcurve++;
            break;
        case OB_FONT:
            object->data = add_curve(OB_FONT);
            break;
        case OB_MBALL:
            object->data = add_mball();
            break;
        case OB_IKA:
            object->data = add_ika();
            object->dt = OB_WIRE;
            break;
        case OB_LATTICE:
            object->data = (void *)add_lattice();
            object->dt = OB_WIRE;
            break;
        case OB_WAVE:
            object->data = add_wave();
            break;
        case OB_ARMATURE:
            object->data = add_armature();
            break;
    */
    }

    G.totobj++;

    /* Create a Python object from it. */
    blen_object = (C_BlenObject*)PyObject_NEW (C_BlenObject, &object_type); 
    blen_object->object = object;

    return ((PyObject*)blen_object);
}

/*****************************************************************************/
/* Function:              M_Object_Get                                       */
/* Python equivalent:     Blender.Object.Get                                 */
/*****************************************************************************/
PyObject *M_Object_Get(PyObject *self, PyObject *args)
{
    struct Object   * object;
    char            * name = NULL;

    printf ("In Object_Get()\n");

    PyArg_ParseTuple(args, "|s", &name);

    if (name != NULL)
    {
        C_BlenObject    * blen_object;

        object = GetObjectByName (name);

        if (object == NULL)
        {
            /* No object exists with the name specified in the argument name. */
            return (PythonReturnErrorObject (PyExc_AttributeError,
                        "Unknown object specified."));
        }
        blen_object = (C_BlenObject*)PyObject_NEW (C_BlenObject, &object_type); 
        blen_object->object = object;

        return ((PyObject*)blen_object);
    }
    else
    {
        /* No argument has been given. Return a list of all objects by name. */
        PyObject    * obj_list;
        ID          * id_iter;
        int           index = 0;

        obj_list = PyList_New (BLI_countlist (&(G.main->object)));

        if (obj_list == NULL)
        {
            return (PythonReturnErrorObject (PyExc_SystemError,
                        "List creation failed."));
        }

        object = G.main->object.first;
        id_iter = &(object->id);
        while (id_iter)
        {
            PyObject    * object;

            object = PyString_FromString (GetIdName (id_iter));
            if (object == NULL)
            {
                return (PythonReturnErrorObject (PyExc_SystemError,
                        "Python string creation failed."));
            }
            PyList_SetItem (obj_list, index, object);
            id_iter = id_iter->next;
            index++;
        }
        return (obj_list);
    }
}

/*****************************************************************************/
/* Function:              M_Object_GetSelected                               */
/* Python equivalent:     Blender.Object.getSelected                         */
/*****************************************************************************/
PyObject *M_Object_GetSelected (PyObject *self, PyObject *args)
{
    C_BlenObject    * blen_object;
    PyObject        * list;
    Base            * base_iter;

    printf ("In Object_GetSelected()\n");

    list = PyList_New (0);
    if ((G.scene->basact) &&
        ((G.scene->basact->flag & SELECT) &&
         (G.scene->basact->lay & G.vd->lay)))
    {
        /* Active object is first in the list. */
        blen_object = (C_BlenObject*)PyObject_NEW (C_BlenObject, &object_type); 
        if (blen_object == NULL)
        {
            Py_DECREF (list);
            Py_INCREF (Py_None);
            return (Py_None);
        }
        blen_object->object = G.scene->basact->object;
        PyList_Append (list, (PyObject*)blen_object);
    }

    base_iter = G.scene->base.first;
    while (base_iter)
    {
        if (((G.scene->basact->flag & SELECT) &&
             (G.scene->basact->lay & G.vd->lay)) &&
            (base_iter != G.scene->basact))
        {
            blen_object = (C_BlenObject*)PyObject_NEW (C_BlenObject,
                                                       &object_type); 
            if (blen_object == NULL)
            {
                Py_DECREF (list);
                Py_INCREF (Py_None);
                return (Py_None);
            }
            blen_object->object = base_iter->object;
            PyList_Append (list, (PyObject*)blen_object);
        }
        base_iter = base_iter->next;
    }
    return (list);
}

/*****************************************************************************/
/* Function:              initObject                                         */
/*****************************************************************************/
PyObject *initObject (void)
{
    PyObject    * module;

    printf ("In initObject()\n");

    module = Py_InitModule3("Object", M_Object_methods, M_Object_doc);

    return (module);
}

/*****************************************************************************/
/* Function:    ObjectCreatePyObject                                         */
/* Description: This function will create a new BlenObject from an existing  */
/*              Object structure.                                            */
/*****************************************************************************/
PyObject* ObjectCreatePyObject (struct Object *obj)
{
    C_BlenObject    * blen_object;

    printf ("In ObjectCreatePyObject\n");

    blen_object = (C_BlenObject*)PyObject_NEW (C_BlenObject, &object_type);

    if (blen_object == NULL)
    {
        return (NULL);
    }
    blen_object->object = obj;
    return ((PyObject*)blen_object);
}

/*****************************************************************************/
/* Function:    ObjectDeAlloc                                                */
/* Description: This is a callback function for the BlenObject type. It is   */
/*              the destructor function.                                     */
/*****************************************************************************/
void ObjectDeAlloc (C_BlenObject *obj)
{
    PyObject_DEL (obj);
}

/*****************************************************************************/
/* Function:    ObjectGetAttr                                                */
/* Description: This is a callback function for the BlenObject type. It is   */
/*              the function that retrieves any value from Blender and       */
/*              passes it to Python.                                         */
/*****************************************************************************/
PyObject* ObjectGetAttr (C_BlenObject *obj, char *name)
{
    struct Object   * object;
    struct Ika      * ika;

    object = obj->object;
    if (StringEqual (name, "LocX"))
        return (PyFloat_FromDouble(object->loc[0]));
    if (StringEqual (name, "LocY"))
        return (PyFloat_FromDouble(object->loc[1]));
    if (StringEqual (name, "LocZ"))
        return (PyFloat_FromDouble(object->loc[2]));
    if (StringEqual (name, "loc"))
        return (Py_BuildValue ("fff", object->loc[0], object->loc[1],
                               object->loc[2]));
    if (StringEqual (name, "dLocX"))
        return (PyFloat_FromDouble(object->dloc[0]));
    if (StringEqual (name, "dLocY"))
        return (PyFloat_FromDouble(object->dloc[1]));
    if (StringEqual (name, "dLocZ"))
        return (PyFloat_FromDouble(object->dloc[2]));
    if (StringEqual (name, "dloc"))
        return (Py_BuildValue ("fff", object->dloc[0], object->dloc[1],
                               object->dloc[2]));
    if (StringEqual (name, "RotX"))
        return (PyFloat_FromDouble(object->rot[0]));
    if (StringEqual (name, "RotY"))
        return (PyFloat_FromDouble(object->rot[1]));
    if (StringEqual (name, "RotZ"))
        return (PyFloat_FromDouble(object->rot[2]));
    if (StringEqual (name, "rot"))
        return (Py_BuildValue ("fff", object->rot[0], object->rot[1],
                               object->rot[2]));
    if (StringEqual (name, "dRotX"))
        return (PyFloat_FromDouble(object->drot[0]));
    if (StringEqual (name, "dRotY"))
        return (PyFloat_FromDouble(object->drot[1]));
    if (StringEqual (name, "dRotZ"))
        return (PyFloat_FromDouble(object->drot[2]));
    if (StringEqual (name, "drot"))
        return (Py_BuildValue ("fff", object->drot[0], object->drot[1],
                               object->drot[2]));
    if (StringEqual (name, "SizeX"))
        return (PyFloat_FromDouble(object->size[0]));
    if (StringEqual (name, "SizeY"))
        return (PyFloat_FromDouble(object->size[1]));
    if (StringEqual (name, "SizeZ"))
        return (PyFloat_FromDouble(object->size[2]));
    if (StringEqual (name, "size"))
        return (Py_BuildValue ("fff", object->size[0], object->size[1],
                               object->size[2]));
    if (StringEqual (name, "dSizeX"))
        return (PyFloat_FromDouble(object->dsize[0]));
    if (StringEqual (name, "dSizeY"))
        return (PyFloat_FromDouble(object->dsize[1]));
    if (StringEqual (name, "dSizeZ"))
        return (PyFloat_FromDouble(object->dsize[2]));
    if (StringEqual (name, "dsize"))
        return (Py_BuildValue ("fff", object->dsize[0], object->dsize[1],
                               object->dsize[2]));
    if (strncmp (name,"Eff", 3) == 0)
    {
        if ( (object->type == OB_IKA) && (object->data != NULL) )
        {
            ika = object->data;
            switch (name[3])
            {
                case 'X':
                    return (PyFloat_FromDouble (ika->effg[0]));
                case 'Y':
                    return (PyFloat_FromDouble (ika->effg[1]));
                case 'Z':
                    return (PyFloat_FromDouble (ika->effg[2]));
                default:
                    /* Do we need to display a sensible error message here? */
                    return (NULL);
            }
        }
        return (NULL);
    }
    if (StringEqual (name, "Layer"))
        return (PyInt_FromLong(object->lay));
    if (StringEqual (name, "parent"))
        return (ObjectCreatePyObject (object->parent));
    if (StringEqual (name, "track"))
        return (ObjectCreatePyObject (object->track));
    if (StringEqual (name, "data"))
    {
        printf ("This is not implemented yet.\n");
        return (Py_None);
    }
    if (StringEqual (name, "ipo"))
    {
        printf ("This is not implemented yet.\n");
        return (Py_None);
    }
    if (StringEqual (name, "mat"))
    {
        printf ("This is not implemented yet. (matrix)\n");
        return (Py_None);
    }
    if (StringEqual (name, "matrix"))
    {
        printf ("This is not implemented yet. (matrix)\n");
        return (Py_None);
    }
    if (StringEqual (name, "colbits"))
        return (Py_BuildValue ("h", object->colbits));
    if (StringEqual (name, "drawType"))
        return (Py_BuildValue ("b", object->dt));
    if (StringEqual (name, "drawMode"))
        return (Py_BuildValue ("b", object->dtx));

    printf ("Unknown variable.\n");
    return (Py_None);
}

/*****************************************************************************/
/* Function:    ObjectSetAttr                                                */
/* Description: This is a callback function for the BlenObject type. It is   */
/*              the function that retrieves any value from Python and sets   */
/*              it accordingly in Blender.                                   */
/*****************************************************************************/
int ObjectSetAttr (C_BlenObject *obj, char *name, PyObject *value)
{
    struct Object    * object;
    struct Ika      * ika;

    object = obj->object;
    if (StringEqual (name, "LocX"))
        return (!PyArg_Parse (value, "f", &(object->loc[0])));
    if (StringEqual (name, "LocY"))
        return (!PyArg_Parse (value, "f", &(object->loc[1])));
    if (StringEqual (name, "LocZ"))
        return (!PyArg_Parse (value, "f", &(object->loc[2])));
    if (StringEqual (name, "loc"))
        return (!PyArg_Parse (value, "fff", &(object->loc[0]),
                              &(object->loc[1]), &(object->loc[2])));
    if (StringEqual (name, "dLocX"))
        return (!PyArg_Parse (value, "f", &(object->dloc[0])));
    if (StringEqual (name, "dLocY"))
        return (!PyArg_Parse (value, "f", &(object->dloc[1])));
    if (StringEqual (name, "dLocZ"))
        return (!PyArg_Parse (value, "f", &(object->dloc[2])));
    if (StringEqual (name, "dloc"))
        return (!PyArg_Parse (value, "fff", &(object->dloc[0]),
                              &(object->dloc[1]), &(object->dloc[2])));
    if (StringEqual (name, "RotX"))
        return (!PyArg_Parse (value, "f", &(object->rot[0])));
    if (StringEqual (name, "RotY"))
        return (!PyArg_Parse (value, "f", &(object->rot[1])));
    if (StringEqual (name, "RotZ"))
        return (!PyArg_Parse (value, "f", &(object->rot[2])));
    if (StringEqual (name, "rot"))
        return (!PyArg_Parse (value, "fff", &(object->rot[0]),
                              &(object->rot[1]), &(object->rot[2])));
    if (StringEqual (name, "dRotX"))
        return (!PyArg_Parse (value, "f", &(object->drot[0])));
    if (StringEqual (name, "dRotY"))
        return (!PyArg_Parse (value, "f", &(object->drot[1])));
    if (StringEqual (name, "dRotZ"))
        return (!PyArg_Parse (value, "f", &(object->drot[2])));
    if (StringEqual (name, "drot"))
        return (!PyArg_Parse (value, "fff", &(object->drot[0]),
                              &(object->drot[1]), &(object->drot[2])));
    if (StringEqual (name, "SizeX"))
        return (!PyArg_Parse (value, "f", &(object->size[0])));
    if (StringEqual (name, "SizeY"))
        return (!PyArg_Parse (value, "f", &(object->size[1])));
    if (StringEqual (name, "SizeZ"))
        return (!PyArg_Parse (value, "f", &(object->size[2])));
    if (StringEqual (name, "size"))
        return (!PyArg_Parse (value, "fff", &(object->size[0]),
                              &(object->size[1]), &(object->size[2])));
    if (StringEqual (name, "dSizeX"))
        return (!PyArg_Parse (value, "f", &(object->dsize[0])));
    if (StringEqual (name, "dSizeY"))
        return (!PyArg_Parse (value, "f", &(object->dsize[1])));
    if (StringEqual (name, "dSizeZ"))
        return (!PyArg_Parse (value, "f", &(object->dsize[2])));
    if (StringEqual (name, "dsize"))
        return (!PyArg_Parse (value, "fff", &(object->dsize[0]),
                              &(object->dsize[1]), &(object->dsize[2])));
    if (strncmp (name,"Eff", 3) == 0)
    {
        if ( (object->type == OB_IKA) && (object->data != NULL) )
        {
            ika = object->data;
            switch (name[3])
            {
                case 'X':
                    return (!PyArg_Parse (value, "f", &(ika->effg[0])));
                case 'Y':
                    return (!PyArg_Parse (value, "f", &(ika->effg[1])));
                case 'Z':
                    return (!PyArg_Parse (value, "f", &(ika->effg[2])));
                default:
                    /* Do we need to display a sensible error message here? */
                    return (0);
            }
        }
        return (0);
    }
    if (StringEqual (name, "Layer"))
        return (!PyArg_Parse (value, "i", &(object->lay)));
    if (StringEqual (name, "parent"))
    {
        /* This is not allowed. */
        PythonReturnErrorObject (PyExc_AttributeError,
                    "Setting the parent is not allowed.");
        return (0);
    }
    if (StringEqual (name, "track"))
    {
        /* This is not allowed. */
        PythonReturnErrorObject (PyExc_AttributeError,
                    "Setting the track is not allowed.");
        return (0);
    }
    if (StringEqual (name, "data"))
    {
        /* This is not allowed. */
        PythonReturnErrorObject (PyExc_AttributeError,
                    "Setting the data is not allowed.");
        return (0);
    }
    if (StringEqual (name, "ipo"))
    {
        /* This is not allowed. */
        PythonReturnErrorObject (PyExc_AttributeError,
                    "Setting the ipo is not allowed.");
        return (0);
    }
    if (StringEqual (name, "mat"))
    {
        printf ("This is not implemented yet. (matrix)\n");
        return (1);
    }
    if (StringEqual (name, "matrix"))
    {
        printf ("This is not implemented yet. (matrix)\n");
        return (1);
    }
    if (StringEqual (name, "colbits"))
        return (!PyArg_Parse (value, "h", &(object->colbits)));
    if (StringEqual (name, "drawType"))
        return (!PyArg_Parse (value, "b", &(object->dt)));
    if (StringEqual (name, "drawMode"))
        return (!PyArg_Parse (value, "b", &(object->dtx)));

    printf ("Unknown variable.\n");
    return (0);
}

