/* Copyright 1998 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Generic (c.module)
 *
 * THIS FILE REQUIRES CUSTOMISATION
 *
 * � Acorn Computers Ltd. 1997
 *
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
#include "swis.h"
#include "sys/errno.h"
#include "module.h"
#include "GenericHdr.h"
#include "utils.h"
#include "protocol.h"
#include "connect.h"

#include "MemCheck:MemCheck.h"

#if CMHG_VERSION < 516
#define CMHG_CONST
#else
#define CMHG_CONST const
#endif

/* TinySupport SWIs.  This does not work if any library static data
 * is used within the module.  This means errno (__errno) and __ctype
 * (as used by any macro call to is.... functions)  If you re-enable
 * this SWI call, this module will fail to work at all.
 */
#define NO_TINYSTUBS

#ifdef ROM
#ifndef NO_TINYSTUBS
static _kernel_oserror *__ROM;
#define TinySupport_Share       (0x82c43)
#define TinySupport_Die         (0x82c41)
#endif
#endif

#define NO_SUCH_SWI     (0x1E6)

#define URL_PROTOCOL_REGISTER 0x83e20
#define URL_PROTOCOL_DEREGISTER 0x83e21

#ifndef ROM
extern int messages_file(void);
#endif

#ifndef ROM
#ifdef DEFINE_ERRNO
int __errno;
#endif
#endif


static int callback_pending_flag;

/* Sets clibrary_realloc_routine_is_buggy to 1 if RISC OS 3.1 or earlier (ROM realloc is broken) */
static int clibrary_realloc_routine_is_buggy;
static void module_check_os_version(void)
{
        int os;
        (void) _swix(OS_Byte, _INR(0,2)|_OUT(1), 129, 0, 255, &os);
        clibrary_realloc_routine_is_buggy = (os <= 0xA4);
}


static int registered;

static _kernel_oserror *try_to_register(void)
{
        _kernel_oserror *e;

        if (registered) return NULL;
        e = _swix(URL_PROTOCOL_REGISTER, _INR(0,4), 0,
                GenericFetcher_00,
                CONNECT_DEFAULT_PROTOCOL_NAME ":",
                Module_VersionNumber, Module_Help " � Acorn");
        if (e == NULL) {
                registered = 1;
        }
        return e;
}

static _kernel_oserror *try_to_deregister(void)
{
        if (!registered) {
                return NULL;
        }
        else {
                registered = 0;
                return _swix(URL_PROTOCOL_DEREGISTER, _INR(0,1), 0, GenericFetcher_00);
        }
}

static _kernel_oserror *register_with_url(void)
{
        _kernel_oserror *e;

        e = try_to_register();
        if (e == NULL) return e;
        if (e->errnum != NO_SUCH_SWI) return e;
        #ifndef ROM
        e = _swix(OS_Module, _INR(0,1), 1 /* Load */, "System:Modules.Network.URL.URL");
        return try_to_register();
        #else
        return NULL;
        #endif
}

/*************************************************************/
/* _kernel_oserror *module_init(char *cmd_fail, int podu...  */
/*************************************************************/
/* Start up and register ourselves with the URL module       */
/*************************************************************/
_kernel_oserror *module_init(CMHG_CONST char *cmd_tail, int podule_base, void *pw)
{
        _kernel_oserror *e;

        (void) podule_base;
        (void) cmd_tail;

        #ifdef ROM
        #ifndef NO_TINYSTUBS
        __ROM = _swix(TinySupport_Share, _IN(0), pw);
        #endif
        #endif

        debug_initialise("Generic", 0, 0);
        debug_output_device(DEBUGIT_OUTPUT);

        registered = 0;
        callback_pending_flag = 0;

        session_init();
        module_check_os_version();

        MemCheck_InitNoDebug();
        MemCheck_SetQuitting(0,0);
        MemCheck_RedirectToFilename("adfs::4.Trace.Genericmem");
        MemCheck_InterceptSCLStringFunctions();
        MemCheck_RegisterMiscBlock((void *)0x01c0001c,8164);
        MemCheck_SetStoreMallocFunctions(1);
        MemCheck_SetReportFrees(1);
        MemCheck_SetChecking(0,0);

        e = register_with_url();
        if (e) {
                #ifndef ROM
                if (e->errnum == NO_SUCH_SWI) {
                        _kernel_oserror *newe = find_error();
                        newe->errnum = 0x115; /* NeedMod */
                        strcpy(newe->errmess, "NeedMod");
                        e = _swix(MessageTrans_ErrorLookup, _INR(0,7), newe, 0, 0, 0, Module_Title, "URL", 0, 0);
                }
                #endif
                return e;
        }

        #ifndef ROM
        e = _swix(ResourceFS_RegisterFiles, _IN(0), messages_file());
        if (e) {
                (void) try_to_deregister();
                return e;
        }
        #endif

        if (getenv(Module_Title "$Path")) {
                e = messages_file_open(Module_Title ":Messages");
        }
        else {
                e = messages_file_open("Resources:$.Resources.URL." "Generic" ".Messages");
        }

        if (e) {
                #ifndef ROM
                (void) _swix(ResourceFS_DeregisterFiles, _IN(0), messages_file());
                #endif
                (void) try_to_deregister();
                return e;
        }

        callback_pending_flag = 0;
        /* Set up a callevery */
        e = _swix(OS_CallEvery, _INR(0,2), 15*100-1, callevery_entry, pw);
        if (e) {
                messages_file_close();
                #ifndef ROM
                (void) _swix(ResourceFS_DeregisterFiles, _IN(0), messages_file());
                #endif
                (void) try_to_deregister();
                return e;
        }

        return NULL;
}


void service_handler(int service_number, _kernel_swi_regs *r, void *pw)
{
        dprintf(("module_1", "Service &%X: R0 = %d for URL version  %03d\n", service_number, r->r[0], r->r[2]));
        (void) pw;
        (void) service_number;

        switch (r->r[0]) {
                case 0:
                        (void) try_to_register();
                        break;
                case 1:
                        (void) try_to_deregister();
                        break;
                default:
                        break;
        }
}


_kernel_oserror *module_kill(int fatal, int podule, void *pw)
{
        (void) fatal;
        (void) podule;

        (void) _swix(OS_RemoveTickerEvent, _INR(0,1), callevery_entry, pw);
        if (callback_pending_flag) {
                (void) _swix(OS_RemoveCallBack, _INR(0,1), callback_entry, pw);
        }
        messages_file_close();
        #ifndef ROM
        (void) _swix(ResourceFS_DeregisterFiles, _IN(0), messages_file());
        #endif

        ses_kill_all();

       (void) try_to_deregister();

        #ifdef ROM
        #ifndef NO_TINYSTUBS
        if(!__ROM) _swix(TinySupport_Die, 0);
        #endif
        #endif

        return NULL;
}


_kernel_oserror *swi_handler(int swi_offset, _kernel_swi_regs *r, void *pw)
{
        (void) pw;

        switch (swi_offset) {
                case GenericFetcher_GetData - GenericFetcher_00:
                        return(generic_start(r));

                case GenericFetcher_Status - GenericFetcher_00:
                        return(generic_status(r));

                case GenericFetcher_ReadData - GenericFetcher_00:
                        return(generic_readdata(r));

                case GenericFetcher_Stop - GenericFetcher_00:
                       return (generic_stop(r));

                default:
                        return(error_BAD_SWI);
        }
}


int callback_handler(_kernel_swi_regs *r, void *pw)
{
        (void) pw;
        (void) r;

        if (!callback_pending_flag) {
                return 1;
        }

        callback_pending_flag = 0;
        return 1;
}

int callevery_handler(_kernel_swi_regs *r, void *pw)
{
        (void) r;
        if (callback_pending_flag) {
                return 1;
        }
        callback_pending_flag = 1;
        (void) _swix(OS_AddCallBack, _INR(0,1), callback_entry, pw);
        return 1;
}

/* RISC OS 3.1 SVC mode realloc is broken, so we must provide our own */
char *module_realloc(void *ptr, size_t size)
{
        dprintf(("module_1", "module_realloc(%p, %d)\n", ptr, size));
        if (!clibrary_realloc_routine_is_buggy) return realloc(ptr, size);

        if (ptr == NULL) {
                return malloc(size);
        }
        if (size == 0) {
                free(ptr);
                return NULL;
        }
        else {
                const int *rma_block = ptr;
                const size_t newsize = size - (rma_block[-1] - 4);
                char *newptr;

                if (_swix(OS_Module, _IN(0)|_INR(2,3)|_OUT(2), 0xD, ptr, newsize, &newptr) != NULL) {
                        return NULL;
                }

                return newptr;
        }
}