/*
 *  Python extension for reading a Mac OS X plist file.
 *  Copyright (C) 2004  Andreas Junghans <aj@lucid-cake.net>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <Python.h>
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#include <sys/sysctl.h>
#include "globals.h"
#include "util.h"
#include "cfdate.h"

#define CHAR_BUFFER_SIZE 256
//static char char_buffer[CHAR_BUFFER_SIZE];
#define ADD_STRING_CONSTANT(m, c) \
    if (CFStringGetCString(c, char_buffer, CHAR_BUFFER_SIZE, kCFStringEncodingASCII)) \
    { \
        PyModule_AddStringConstant(m, #c, char_buffer); \
    }
#define ADD_INT_CONSTANT(m, c) \
    PyModule_AddIntConstant(m, #c, c);
#define INIT_TYPE(dict, name, typeObject) \
    PyType_Ready(typeObject); \
    PyDict_SetItemString(dict, name, (PyObject*) typeObject);


// Test method for error handling.
static PyObject* errorTest(PyObject* self, PyObject* args)
{
    PyErr_SetString(PyExc_OSError, "an error test");
    return NULL;
}


// Read a plist file.
static PyObject* readplist(PyObject* self, PyObject* args)
{
    PyObject*   pyPlistPath;
    CFStringRef plistPath = NULL;
    CFURLRef    plistURL = NULL;
    CFDataRef   plistData = NULL;
    CFPropertyListRef   plist = NULL;
    CFStringRef errorString = NULL;
    bool		error = false;
    Boolean     status;
    PyObject*   retVal = NULL;
    SInt32      errorCode;
    char        errString[256];
    
    // parse the arguments
    if (!PyArg_ParseTuple(args, "O", &pyPlistPath)) {
        error = true;
    }
    if (!error) {
        plistPath = CFStringFromPyString(pyPlistPath);
        if (plistPath == NULL) {
            error = true;
        }
    }
    
    // load the plist
    if (!error) {
        plistURL = CFURLCreateWithFileSystemPath(NULL, plistPath, kCFURLPOSIXPathStyle, FALSE);
        if (plistURL == NULL) {
            PyErr_SetString(PyExc_OSError, "cannot convert the path to a URL");
            error = true;
        }
    }
    if (!error) {
        status = CFURLCreateDataAndPropertiesFromResource(
            NULL, plistURL, &plistData, NULL, NULL, &errorCode
        );
        if (!status) {
            sprintf(errString, "cannot load plist file (error code %d)", (int) errorCode);
            PyErr_SetString(PyExc_OSError, errString);
            error = true;
        }
    }
    if (!error) {
        plist = CFPropertyListCreateFromXMLData(
            NULL, plistData, kCFPropertyListImmutable, &errorString
        );
        if (plist == NULL) {
            PyErr_SetString(PyExc_OSError, "cannot read the plist file");
            error = true;
        }
    }
    
    // convert the contents to python objects
    if (!error) {
        retVal = convertToPyObject(plist);
        if (retVal == NULL) {
            error = true;
        }
    }
    
    // free everything
    CFRELEASE(errorString);
    CFRELEASE(plist);
    CFRELEASE(plistData);
    CFRELEASE(plistURL);
    CFRELEASE(plistPath);

    return retVal;
}


static PyMethodDef PlistMethods[] = {
    {"errorTest", errorTest, METH_NOARGS, "Method for testing error handling, please ignore."},
    {"readplist", readplist, METH_VARARGS, "Reads in a plist file."},
    {NULL, NULL, 0, NULL}
};


void initplist() {
    PyObject *m, *dict;
    
    // initialize the module
    m = Py_InitModule(MODULE_NAME, PlistMethods);
    dict = PyModule_GetDict(m);

    // initialize and publish our types
    INIT_TYPE(dict, CFDATE_NAME, &cfdate_type);
}
