/* Copyright (c) 1996 by Lele Gaifax.  All Rights Reserved
 *
 * This software may be used and distributed freely for any purpose
 * provided that this notice is included unchanged on any and all
 * copies. The author does not warrant or guarantee this software in
 * any way.
 *
 * This file is part of the PyObjC package.
 *
 * $RCSfile: OC_PythonObject.m,v $
 * $Revision: 1.1.1.4 $
 * $Date: 1996/11/14 01:50:15 $
 *
 * Created Wed Sep  4 19:57:44 1996.
 */

#include "objc_support.h"
#include "abstract.h"
#include "compile.h"
#include <stdarg.h>

#define CLASS_VERSION 0

#ifndef MAX
#define MAX(x,y) ({ unsigned int __x=(x), __y=(y); (__x > __y ? __x : __y); })
#define MIN(x,y) ({ unsigned int __x=(x), __y=(y); (__x < __y ? __x : __y); })
#endif

@implementation OC_PythonObject

+ (void) initialize
{
  if (self == [OC_PythonObject class])
    {
      [OC_PythonObject setVersion:CLASS_VERSION];
    }
}

+ newWithObject:(PyObject *) obj
{
  if (ObjCObject_Check (obj))
    return ((ObjCObject *) obj)->oc_object;
  else
    {
      id instance = [[self alloc] initWithObject:obj];
      
#ifdef WITH_FOUNDATION
      [instance autorelease];
#endif

      return instance;
    }
}

- initWithObject:(PyObject *) obj
{
  [super init];

#ifndef WITH_FOUNDATION
  Py_INCREF (obj);
#endif
  
  pyObject = obj;

  return self;
}

#ifdef WITH_FOUNDATION

- (void) retain
{
  [super retain];
  Py_INCREF (pyObject);
}

- (oneway void) release
{
  [super release];
  Py_DECREF (pyObject);
}

#else

- copyFromZone:(NXZone *) zone
{
  id copy;

  Py_INCREF (pyObject);
  copy = [super copyFromZone:zone];
  
  return copy;
}

- free
{
  Py_DECREF (pyObject);

  return [super free];
}

#endif

#define RETURN_RESULT(result) do {					\
  if (result)								\
    {									\
      OC_PythonObject *ret = [[self class] newWithObject:result];	\
      									\
      Py_DECREF (result);						\
      return ret;							\
    }									\
  else									\
    return nil;						     		\
} while(0)

- doesNotRecognize:(SEL) aSelector
{
#define ERRORMSG " does not recognize -"
  const char *whoiam = NAMEOF(self);
  const char *selname = SELNAME(aSelector);
  char buffer[strlen (whoiam) + sizeof ERRORMSG + strlen (selname) + 1];

  strcpy (buffer, whoiam);
  strcat (buffer, ERRORMSG);
  strcat (buffer, selname);
  PyErr_SetString (ObjC_Error, buffer);
  return nil;
}

/*#F Check the argument count of the method/function @var{pymethod},
  returning the method itself if it matches @var{argcount}, NULL
  otherwise. */
static inline PyObject *
check_argcount (PyObject *pymethod, unsigned int argcount)
{
  if (pymethod && (PyFunction_Check (pymethod) || PyMethod_Check (pymethod)))
    {
      PyCodeObject *func_code;
      
      if (PyMethod_Check (pymethod))
	{
	  func_code = (PyCodeObject *) PyFunction_GetCode (PyMethod_Function (pymethod));
	  argcount++;		// for `self'
	}
      else
	func_code = (PyCodeObject *) PyFunction_GetCode (pymethod);
      
      if (func_code->co_argcount == argcount)
	return pymethod;
    }

  return NULL;
}

/*#F If the Python object @var{obj} implements a method whose name matches
  the Objective-C selector @var{aSelector} and accepts the correct number
  of arguments, return that method, otherwise NULL. */
static PyObject *
get_method_for_selector (PyObject *obj, SEL aSelector)
{
  if (aSelector)
    {
      const char *meth_name = SELNAME(aSelector);
      int len = strlen (meth_name);
      char *pymeth_name;
      unsigned int argcount;
      PyObject *pymethod;
      register const char *p;
      
      for (argcount=0, p=meth_name; *p; p++)
	if (*p == ':')
	  argcount++;
  
      pymeth_name = alloca (PYTHONIFIED_LENGTH(meth_name, len, argcount));
#if PYTHONIFICATION_METHOD != WITH_BOTH
      pythonify_objc_message (meth_name, pymeth_name);

      pymethod = PyObject_GetAttrString (obj, pymeth_name);
      return check_argcount (pymethod, argcount);
#else
      {
	PyObject *meth;

	pythonify_objc_message (meth_name, pymeth_name, PYTHONIFICATION_FIRST_TRY);
	pymethod = PyObject_GetAttrString (obj, pymeth_name);
	meth = check_argcount (pymethod, argcount);
	if (meth)
	  return meth;

#if PYTHONIFICATION_FIRST_TRY == WITH_DOUBLE_UNDERSCORE
	pythonify_objc_message (meth_name, pymeth_name, WITH_SINGLE_UNDERSCORE);
#else
	pythonify_objc_message (meth_name, pymeth_name, WITH_DOUBLE_UNDERSCORE);
#endif
	pymethod = PyObject_GetAttrString (obj, pymeth_name);
	meth = check_argcount (pymethod, argcount);
	if (meth)
	  {
	    if (Py_VerboseFlag)
	      {
		char faster_name[200];
		
		fprintf (stderr, "PyObjC Warning: method `%s' matches `%s',\n\t", pymeth_name, meth_name);
		
		pythonify_objc_message (meth_name, faster_name, PYTHONIFICATION_FIRST_TRY);
		
		fprintf (stderr, "but `%s' would be faster.\n", faster_name);
	      }

	    return meth;
	  }
      }
#endif /* PYTHONIFICATION_METHOD != WITH_BOTH */
    }
  return NULL;    
}

#ifdef WITH_FOUNDATION
- (BOOL) respondsToSelector:(SEL) aSelector
#else
- (BOOL) respondsTo:(SEL) aSelector
#endif
{
#ifdef WITH_FOUNDATION
  if ([super respondsToSelector:aSelector])
#else
  if ([super respondsTo:aSelector])
#endif
    return YES;
  else
    {
      PyObject *m = get_method_for_selector ([self object], aSelector);

      if (m)
	return YES;
      else
	{
	  PyErr_Clear();
	  return NO;
	}
    }
}

- forward:(SEL) aSelector :(marg_list) argFrame
{
  PyObject *pymethod = get_method_for_selector ([self object], aSelector);

  if (pymethod)
    {
      PyObject *args = NULL;
      unsigned int i, argcount;      
      PyCodeObject *func_code;
	  
      if (PyMethod_Check (pymethod))
	{
	  func_code = (PyCodeObject *) PyFunction_GetCode (PyMethod_Function (pymethod));
	  argcount = func_code->co_argcount-1; // adjust for `self'
	}
      else
	{
	  func_code = (PyCodeObject *) PyFunction_GetCode (pymethod);
	  argcount = func_code->co_argcount;
	}
      
      args = PyTuple_New (argcount);
      if (args)
	{
	  unsigned int offset = sizeof (id *) + sizeof (SEL *);
	  PyObject *result;
	  
	  for (i=0; i<argcount; i++)
	    {
#ifdef GNU_RUNTIME
	      /* XXX is this right? What about arguments in register? */
	      id argument = *(id *) (argFrame->arg_ptr + offset + i*sizeof (id *));
#else
	      id argument = marg_getValue (argFrame, offset + i*sizeof (id *), id);
#endif
	      if ([argument isKindOf:[OC_PythonObject class]])
		{
		  PyObject *pyarg = [argument object];
		  
		  Py_INCREF(pyarg);
		  PyTuple_SET_ITEM (args, i, pyarg);
		}
	      else
		{
		  ObjCObject *pyarg = ObjCObject_new (argument);
		  
		  PyTuple_SET_ITEM (args, i, (PyObject *) pyarg);
		}
	    }

	  result = PyObject_CallObject (pymethod, args);
  
	  Py_DECREF(args);

	  RETURN_RESULT(result);
	}
    }

  return [self doesNotRecognize:aSelector];
}    


// NUMBER PROTOCOL

- (int) numberCheck
{
  return PyNumber_Check ([self object]);
}

- (id <PythonObject>) numberAdd:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Add (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberSubtract:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Subtract (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberMultiply:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Multiply (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberDivide:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Divide (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberRemainder:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Remainder (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberDivmod:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Divmod (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberPower:(id <PythonObject>) o2
				 :(id <PythonObject>) o3
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *pyo3 = [o3 object];
  PyObject *result = PyNumber_Power (pyo1, pyo2, pyo3);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberNegative
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Negative (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberPositive
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Positive (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberAbsolute
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Absolute (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberInvert
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Invert (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberLshift:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Lshift (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberRshift:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Rshift (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberAnd:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_And (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberXor:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Xor (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberOr:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PyNumber_Or (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberInt
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Int (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberLong
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Long (pyo);

  RETURN_RESULT(result);
}

- (id <PythonObject>) numberFloat
{
  PyObject *pyo = [self object];
  PyObject *result = PyNumber_Float (pyo);

  RETURN_RESULT(result);
}

// SEQUENCE PROTOCOL

- (int) sequenceCheck
{
  return PySequence_Check ([self object]);
}

- (int) sequenceLength
{
  return PySequence_Length ([self object]);
}

- (id <PythonObject>) sequenceConcat:(id <PythonObject>) o2
{
  PyObject *pyo1 = [self object];
  PyObject *pyo2 = [o2 object];
  PyObject *result = PySequence_Concat (pyo1, pyo2);

  RETURN_RESULT(result);
}

- (id <PythonObject>) sequenceRepeat:(int) count
{
  PyObject *pyo = [self object];
  PyObject *result = PySequence_Repeat (pyo, count);

  RETURN_RESULT(result);
}

- (id <PythonObject>) sequenceGetItem:(int) count
{
  PyObject *pyo = [self object];
  PyObject *result = PySequence_GetItem (pyo, count);

  RETURN_RESULT(result);
}
  
- (id <PythonObject>) sequenceGetSliceFrom:(int) i1 to:(int) i2
{
  PyObject *pyo = [self object];
  PyObject *result = PySequence_GetSlice (pyo, i1, i2);

  RETURN_RESULT(result);
}

- (int) sequenceSetItem:(int) i value:(id <PythonObject>) v
{
  
  PyObject *pyo = [self object];
  PyObject *pyv = [v object];

  return PySequence_SetItem (pyo, i, pyv);
}

- (int) sequenceDelItem:(int) i
{
  
  PyObject *pyo = [self object];

  return PySequence_DelItem (pyo, i);
}

- (int) sequenceSetSliceFrom:(int) i1
			  to:(int) i2
		       value:(id <PythonObject>) v
{
  PyObject *pyo = [self object];
  PyObject *pyv = [v object];

  return PySequence_SetSlice (pyo, i1, i2, pyv);
}

- (int) sequenceDelSliceFrom:(int) i1
			  to:(int) i2
{
  PyObject *pyo = [self object];

  return PySequence_DelSlice (pyo, i1, i2);
}

- (id <PythonObject>) sequenceTuple
{
  PyObject *pyo = [self object];
  PyObject *result = PySequence_Tuple (pyo);

  RETURN_RESULT(result);
}

- (int) sequenceCount:(id <PythonObject>) value
{
  PyObject *pyo = [self object];
  PyObject *pyv = [value object];

  return PySequence_Count (pyo, pyv);
}

- (int) sequenceIn:(id <PythonObject>) value
{
  PyObject *pyo = [self object];
  PyObject *pyv = [value object];

  return PySequence_In (pyo, pyv);
}

- (int) sequenceIndex:(id <PythonObject>) value
{
  PyObject *pyo = [self object];
  PyObject *pyv = [value object];

  return PySequence_Index (pyo, pyv);
}

// CALLING PROTOCOL

- (int) callableCheck
{
  return PyCallable_Check ([self object]);
}

- (id <PythonObject>) callableCall:(id <PythonObject>) args
{
  PyObject *pyo = [self object];
  PyObject *pyargs = [args object];
  PyObject *result = PyObject_CallObject (pyo, pyargs);

  RETURN_RESULT(result);
}

- (id <PythonObject>) callableCallFunction:(char *) format, ...
{
  va_list va;
  PyObject *args;
  PyObject *result;

  va_start (va, format);

  if (format)
    args = Py_VaBuildValue (format, va);
  else
    args = PyTuple_New (0);
  
  va_end(va);
  
  if (! args)
    return nil;

  if(! PyTuple_Check (args))
    {
      PyObject *a = PyTuple_New (1);

      if (!a)
	return nil;
      PyTuple_SET_ITEM (a, 0, args);

      args = a;
    }

  result = PyObject_CallObject ([self object], args);
  
  Py_DECREF(args);
  
  RETURN_RESULT(result);
}

- (id <PythonObject>) callableCallMethod:(char *) m
				withArgs:(char *) format, ...
{
  va_list va;
  PyObject *args;
  PyObject *method;
  PyObject *result;

  method = PyObject_GetAttrString ([self object], m);
  if (! method)
    {
      PyErr_SetString (PyExc_AttributeError, m);
      return nil;
    }
  else if (! PyCallable_Check (method))
    {
      PyErr_SetString(PyExc_TypeError,"call of non-callable attribute");
      return nil;
    }
  
  va_start (va, format);

  if (format)
    args = Py_VaBuildValue (format, va);
  else
    args = PyTuple_New (0);
  
  va_end(va);
  
  if (! args)
    return nil;

  if(! PyTuple_Check (args))
    {
      PyObject *a = PyTuple_New (1);

      if (!a)
	return nil;
      PyTuple_SET_ITEM (a, 0, args);

      args = a;
    }

  result = PyObject_CallObject (method, args);
  
  Py_DECREF(args);
  
  RETURN_RESULT(result);
}


// MAPPING PROTOCOL

- (int) mappingCheck
{
  return PyMapping_Check ([self object]);
}

- (int) mappingLength
{
  return PyMapping_Length ([self object]);
}

- (int) mappingDelItemString:(char *) key
{
#ifndef PyMapping_DelItemString
  return PyDict_DelItemString ([self object], key);
#else
  return PyMapping_DelItemString ([self object], key);
#endif
}

- (int) mappingDelItem:(id <PythonObject>) key
{
#ifndef PyMapping_DelItem
  return PyDict_DelItem ([self object], [key object]);
#else
  return PyMapping_DelItem ([self object], [key object]);
#endif
}

- (int) mappingHasKeyString:(char *) key
{
  return PyMapping_HasKeyString ([self object], key);
}

- (int) mappingHasKey:(id <PythonObject>) key
{
  return PyMapping_HasKey ([self object], [key object]);
}

- (id <PythonObject>) mappingKeys
{
  PyObject *pykeys = PyMapping_Keys ([self object]);

  RETURN_RESULT(pykeys);
}

- (id <PythonObject>) mappingItems
{
  PyObject *pyitems = PyMapping_Items ([self object]);

  RETURN_RESULT(pyitems);
}

- (id <PythonObject>) mappingValues
{
  PyObject *pyvalues = PyMapping_Values ([self object]);

  RETURN_RESULT(pyvalues);
}

- (id <PythonObject>) mappingGetItemString:(char *) key
{
  PyObject *result = PyMapping_GetItemString ([self object], key);

  RETURN_RESULT(result);
}

- (int) mappingSetItemString:(char *) key value:(id <PythonObject>) value
{
  return PyMapping_SetItemString ([self object], key, [value object]);
}


// PYTHON OBJECT PROTOCOL

- (PyObject *) object
{
  return pyObject;
}

- (int) print:(FILE *) fp flags:(int) flags
{
  return PyObject_Print ([self object], fp, flags);
}

- (int) hasAttrString:(char *) attr_name
{
  return PyObject_HasAttrString ([self object], attr_name);
}

- (id <PythonObject>) getAttrString:(char *) attr_name
{
  PyObject *result = PyObject_GetAttrString ([self object], attr_name);

  RETURN_RESULT(result);
}

- (int) hasAttr:(id <PythonObject>) attr_name
{
  [self notImplemented:_cmd];
  return 0;
  // XXX this is not yet implemented as of 1.4b3
  // return PyObject_HasAttr ([self object], [attr_name object]);
}

- (id <PythonObject>) getAttr:(id <PythonObject>) attr_name
{
  PyObject *result = PyObject_GetAttr ([self object], [attr_name object]);

  RETURN_RESULT(result);
}

- (int) setAttrString:(char *) attr_name value:(id <PythonObject>) value
{
  return PyObject_SetAttrString ([self object], attr_name, [value object]);
}

- (int) setAttr:(id <PythonObject>) attr_name value:(id <PythonObject>) value
{
  return PyObject_SetAttr ([self object], [attr_name object], [value object]);
}

- (int) delAttrString:(char *) attr_name
{
  return PyObject_DelAttrString ([self object], attr_name);
}

- (int) delAttr:(id <PythonObject>) attr_name
{
  return PyObject_DelAttr ([self object], [attr_name object]);
}

- (int) cmp:(id <PythonObject>) o2 result:(int *) result
{
  return PyObject_Cmp ([self object], [o2 object], result);
}

- (int) compare:(id <PythonObject>) o2
{
  return PyObject_Compare ([self object], [o2 object]);
}

- (id <PythonObject>) repr
{
  RETURN_RESULT(PyObject_Repr ([self object]));
}

- (id <PythonObject>) str
{
  RETURN_RESULT(PyObject_Str ([self object]));
}

- (long) hash
{
  return PyObject_Hash ([self object]);
}

- (int) isTrue
{
  return PyObject_IsTrue ([self object]);
}

- (id <PythonObject>) type
{
  RETURN_RESULT(PyObject_Type ([self object]));
}

- (int) length
{
  return PyObject_Length ([self object]);
}

- (int) count
{
  return PyObject_Length ([self object]);
}

- (id <PythonObject>) getItem:(id <PythonObject>) key
{
  RETURN_RESULT(PyObject_GetItem ([self object], [key object]));
}

- (int) setItem:(id <PythonObject>) key value:(id <PythonObject>) v
{
  return PyObject_SetItem ([self object],
			   [key object],
			   [v object]);
}

- (int) delItem:(id <PythonObject>) key
{
  return PyObject_DelItem ([self object], [key object]);
}


@end /* OC_PythonObject class implementation */

/*
** Local Variables:
** change-log-default-name:"../ChangeLog.PyObjC"
** End:
*/
