/*******************************************************************************
*
*       This file is part of the General Hidden Markov Model Library,
*       GHMM version __VERSION__, see http://ghmm.org
*
*       Filename: sclass_change.c
*       Authors:  Benjamin Georgi
*
*       Copyright (C) 1998-2004 Alexander Schliep
*       Copyright (C) 1998-2001 ZAIK/ZPR, Universitaet zu Koeln
*       Copyright (C) 2002-2004 Max-Planck-Institut fuer Molekulare Genetik,
*                               Berlin
*
*       Contact: schliep@ghmm.org
*
*       This library is free software; you can redistribute it and/or
*       modify it under the terms of the GNU Library General Public
*       License as published by the Free Software Foundation; either
*       version 2 of the License, or (at your option) any later version.
*
*       This library 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
*       Library General Public License for more details.
*
*       You should have received a copy of the GNU Library General Public
*       License along with this library; if not, write to the Free
*       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*       This file is version $Revision: 2210 $
*                       from $Date: 2008-03-27 12:05:00 -0400 (Thu, 27 Mar 2008) $
*             last change by $Author: grunau $.
*
*******************************************************************************/

/* XXX FIXME: breaks out of tree build of ghmmwrapper */
#include "../config.h"

#include <Python.h>
#include <stdio.h>
#include <stdlib.h>

#include <ghmm/rng.h>
#include <ghmm/sequence.h>
#include <ghmm/smodel.h>



/* smo is a ghmm_cmodel struct
   seq is a double matrix of observations 
   k is the first current sequence index (corresponding to the rows in seq)
   t is the second current sequence index (corresponding to seq[k] )
*/
int cp_class_change(ghmm_cmodel *smo, const double* seq, int k, int t) {
  int res;
  
  if((t%2) == 0){
    /*if(t < 500){*/
      res= 0;
  }
  else {
      res = 1;
  }        
  printf("cp_class_change with value %d -> return %d\n",t,res);    

  return res;
} 		
		
/*
   setSwitchingFunction assigns cp_class_change as switching function in ghmm_dmodel smo.
   Needs to be modified for user defined C switching function.
*/
void setSwitchingFunction(ghmm_cmodel *smd) {
  smd->class_change->get_class = cp_class_change;
}

/* Implements the Python Callback to the switching function defined in smo->class_change.
   Arguments ( which are analogue to cp_class_change (s.a.)) are parsed into Python data structures
   before the call-back.
*/
int python_class_change( ghmm_cmodel* smo, const double *seq, int k, int t ){
   char* ModuleName = smo->class_change->python_module;
   char* FunctionName = smo->class_change->python_function;
   int class,i;

   PyObject *pName,  *pDict, *pArgs, *pValue, *pList;
  
   /* Python module and function are static */
   static PyObject *pModule = NULL;
   static PyObject *pFunc = NULL;
   
   /* importing module and function on first function call */
   
   if (pModule == NULL) {   
     printf("C: Importing Python module ... ");
    
     pName = PyString_FromString(ModuleName);
   
     pModule = PyImport_Import(pName);       /* Import module */
     if(!pModule) {
       printf("python_class_change: import error - Module %s.py not found in current paths.\n",ModuleName);
       return(-1);
     }    
   
     pDict = PyModule_GetDict(pModule);
     printf("done.\n"); 
    
      /*--- TEST ---
      printf("Python Module %s.\n",ModuleName);
      pDict = PyImport_GetModuleDict(); */
    
    
     /*printf("C: Calling Python with value %d\n",t); */
     pFunc = PyDict_GetItemString(pDict, FunctionName);
     if(!pFunc) {
       printf("python_class_change: Error - Function %s not found in namespace of module %s.\n",FunctionName,ModuleName);
       return(-1);
     }  
     Py_DECREF(pDict); 
     Py_DECREF(pName); 
   }
   
   pArgs = PyTuple_New(3);
   
   pList = PyList_New(t);
   for(i=0;i<t;i++){
     pValue = PyFloat_FromDouble(seq[i]);
     PyList_SetItem(pList, i, pValue);
   }    
   PyTuple_SetItem(pArgs, 0, pList); 

   pValue = PyInt_FromLong((long)k);
   PyTuple_SetItem(pArgs, 1, pValue); 

   pValue = PyInt_FromLong((long)t);
   PyTuple_SetItem(pArgs, 2, pValue); 
   
   pValue = PyObject_CallObject(pFunc, pArgs); // Calling Python 

   /* parsing the result from Python to C data type */
   class = PyInt_AsLong(pValue);
  /*printf("C: The returned class is %d\n",class);*/
     
   /* cleaning up */
   Py_DECREF(pArgs); 
   Py_DECREF(pValue); 
   Py_DECREF(pList);    
   
   return class; 
 
}

/* Assignment of Python module and function for class change. The values are stored in smo->class_change.
   
   smo: ghmm_cmodel struct with multiple transition classes
   python_module: Name of the module the switching function is defined in
   python_function: Name of the Python function to be used. 
   IMPORTANT: python_function must have a signature compatible to cp_class_change (that means three arguments:
   first the sequence (as a Python list), second sequence number, third the time step in the current sequence.
   See class_change.py in the ghmmwrapper directory for an example.)

*/
void setPythonSwitching( ghmm_cmodel *smd, char* python_module, char* python_function ){
   if(!smd->class_change) {
     printf("setPythonSwitching ERROR: class_change struct not initialized.\n");
   }     
   smd->class_change->python_module = python_module;
   smd->class_change->python_function = python_function;     
   smd->class_change->get_class = python_class_change ;
}



/* --------------------------------------------------------------------------------------- */
/* The functions below provide an interface to passing arbitrary python objects as class switch
   functions. Such an object has to fulfill two conditions:
    1) it needs to be callable
    2) it needs to have the following (or an equivalent) signature:
        def switchFun(seq,k,t) for a function
            or
        def __call__(self,seq,k,t)  for a callable object
*/

/* pyCallback holds the global Python callback variable */
static PyObject *pyCallback = NULL;

/* executePythonCallback carries out the actual function call of the object
   assigned to pyCallback.

    Arguments:
     smo: pointer on the ghmm_cmodel
     seq: current sequence
     k:   index of current sequence in the sequence set it originated from
     t:   current time step (e.q seq[t] is the current observation)
   
*/
int executePythonCallback(ghmm_cmodel* smo, const double *seq, int k, int t){
   int class,i;

   /*printf("k=%d, t=%d\n",k,t); */
   
   PyObject  *pArgs, *pValue, *pList; /*  *pDict, *pName, */

   pArgs = PyTuple_New(3);
   
   pList = PyList_New(t);
   for(i=0;i<t;i++){
     pValue = PyFloat_FromDouble(seq[i]);
     PyList_SetItem(pList, i, pValue);
   }    
   PyTuple_SetItem(pArgs, 0, pList); 

   pValue = PyInt_FromLong((long)k);
   PyTuple_SetItem(pArgs, 1, pValue); 

   pValue = PyInt_FromLong((long)t);
   PyTuple_SetItem(pArgs, 2, pValue); 
   
   pValue = PyObject_CallObject(pyCallback, pArgs); // Calling Python 

   /* parsing the result from Python to C data type */
   class = PyInt_AsLong(pValue);
   /*printf("C: The returned class is %d\n",class);*/
   
   if (class == -1)  {
     printf("ERROR: Python exception has been thrown during call-back (class is -1)\n.");
     abort();
   }  
     
   /* cleaning up */
   Py_DECREF(pArgs); 
   Py_DECREF(pValue); 
   Py_DECREF(pList);    
   
   return class; 
 
}

/* setPythonCallback assigns the arguement py_cb to the global
   callback variable and sets the corresponding switching function executePythonCallback 
   in the smodel-class_change context.

 */
void setPythonCallback(ghmm_cmodel *smo, PyObject *py_cb){
  pyCallback = py_cb;
  smo->class_change->get_class = executePythonCallback;
}


