/*

Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

*/
module derelict.sdl.macinit.runtime;

version(DigitalMars) version(OSX) version = darwin;

version (darwin):

import derelict.sdl.macinit.string;
import derelict.util.compat;

import derelict.sdl.macinit.NSGeometry;
import derelict.util.loader;
import derelict.util.exception;

package:

alias objc_ivar* Ivar;
alias objc_method* Method;
alias objc_object Protocol;

alias objc_selector* SEL;
alias objc_class* Class;
alias objc_object* id;

alias extern (C) id function(id, SEL, ...) IMP;

struct objc_selector
{
    void* sel_id;
    char* sel_types;
}

struct objc_object
{
    Class isa;
}

struct objc_super
{
    id receiver;
    Class clazz;
}

struct objc_class
{
    Class isa;
    Class super_class;
    CCPTR name;
    int versionn;
    int info;
    int instance_size;
    objc_ivar_list* ivars;
    objc_method_list** methodLists;
    objc_cache* cache;
    objc_protocol_list* protocols;
}

struct objc_ivar
{
    CCPTR ivar_name;
    CCPTR ivar_type;
    int ivar_offset;

    version (X86_64)
        int space;
}

struct objc_ivar_list
{
    int ivar_count;

    version (X86_64)
        int space;

    /* variable length structure */
    objc_ivar[1] ivar_list;
}

struct objc_method
{
    SEL method_name;
    CCPTR method_types;
    IMP method_imp;
}

struct objc_method_list
{
    objc_method_list* obsolete;

    int method_count;

    version (X86_64)
        int space;

    /* variable length structure */
    objc_method[1] method_list;
}

struct objc_cache
{
    uint mask /* total = mask + 1 */;
    uint occupied;
    Method[1] buckets;
}

struct objc_protocol_list
{
    objc_protocol_list* next;
    long count;
    Protocol*[1] list;
}

// Objective-C runtime bindings from the Cocoa framework
extern (C)
{
    mixin(gsharedString!() ~ "
    Class function (Class superclass, CCPTR name, size_t extraBytes) c_objc_allocateClassPair;
    Class function (Class superclass) objc_registerClassPair;
    id function (CCPTR name) c_objc_getClass;
    id function (id theReceiver, SEL theSelector, ...) c_objc_msgSend;
    SEL function (CCPTR str) c_sel_registerName;

    bool function () NSApplicationLoad;

    void function (Class myClass) objc_addClass;
    void function (Class arg0, objc_method_list* arg1) class_addMethods;
    bool function (Class c13ls, SEL name, IMP imp, CCPTR types) class_addMethod;");
}

void load (void delegate(void**, string, bool doThrow) bindFunc)
{
    bindFunc(cast(void**)&c_objc_getClass, "objc_getClass", true);
    bindFunc(cast(void**)&c_objc_msgSend, "objc_msgSend", true);
    bindFunc(cast(void**)&c_sel_registerName, "sel_registerName", true);

    bindFunc(cast(void**)&NSApplicationLoad, "NSApplicationLoad", true);

    try
    {
        /*
         * These methods are expected to not be found in > Leopard, they have
         * been deprecated in favor of objc_{allocate|register}ClassPair and
         * class_addMethod.
         */
        bindFunc(cast(void**)&objc_addClass, "objc_addClass", true);
        bindFunc(cast(void**)&class_addMethods, "class_addMethods", true);
    }

    catch (Exception e)
    {
        bindFunc(cast(void**)&class_addMethod, "class_addMethod", true);
        bindFunc(cast(void**)&c_objc_allocateClassPair, "objc_allocateClassPair", true);
        bindFunc(cast(void**)&objc_registerClassPair, "objc_registerClassPair", true);
    }
}

Class objc_allocateClassPair (string name) (Class superclass, size_t extraBytes)
{
    return c_objc_allocateClassPair(superclass, name.ptr, extraBytes);
}

id objc_getClass (string name) ()
{
    return c_objc_getClass(name.ptr);
}

SEL sel_registerName (string str) ()
{
    return c_sel_registerName(str.ptr);
}

id objc_msgSend (ARGS...)(id theReceiver, SEL theSelector, ARGS args)
{
    // the dmd import generator can't handle this
    //alias extern (C) id function (id, SEL, ARGS) fp;
    //return (cast(fp)&c_objc_msgSend)(theReceiver, theSelector, args);

    return c_objc_msgSend(theReceiver, theSelector, args);
}
