/*
  Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions: The above copyright notice and this
  permission notice shall be included in all copies or substantial
  portions of the Software.


  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 AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
  THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
  ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


  Except as contained in this notice, the names of The Open Group and/or
  Sun Microsystems, Inc. shall not be used in advertising or otherwise to
  promote the sale, use or other dealings in this Software without prior
  written authorization from The Open Group and/or Sun Microsystems,
  Inc., as applicable.


  X Window System is a trademark of The Open Group

  OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
  logo, LBX, X Window System, and Xinerama are trademarks of the Open
  Group. All other trademarks and registered trademarks mentioned herein
  are the property of their respective owners. No right, title or
  interest in or to any trademark, service mark, logo or trade name of
  Sun Microsystems, Inc. or its licensors is granted.

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef  sun
#include <unistd.h>
#endif

#include "SunIM.h"
#include "SunIMLock.h"

Public iml_inst *iml_execute_iml_wrapper(iml_session_t *, iml_inst **);
Public void close_le_module(void *);
Public iml_if_t *if_OpenIF(const char *if_path,
			   const char *if_name,
			   const char *locale, Bool);
Public void if_CloseIF(iml_if_t *, Bool);
Public Bool if_GetIFValues(iml_if_t *, IMArgList, int);
Public Bool if_SetIFValues(iml_if_t *, IMArgList, int);
Public iml_session_t *if_CreateSC(iml_if_t *, IMArgList, int);
Public Bool if_DestroySC(iml_session_t *);
Public Bool if_GetSCValues(iml_session_t *, IMArgList, int);
Public Bool if_SetSCValues(iml_session_t *, IMArgList, int);
Public void if_SendEvent(iml_session_t *, IMInputEvent *);
Public void if_SendEvent_AuxGet(iml_session_t *, IMInputEvent *);
Public IMText *if_ResetSC(iml_session_t *);
Public void if_SetSCFocus(iml_session_t *);
Public void if_UnsetSCFocus(iml_session_t *);

Public Bool if_configure(iml_if_t * If,
			 const char *if_path,
			 const char *if_name,
			 const char *if_locale,
			 Bool);
Public iml_session_t *iml_construct_session(iml_desktop_t *, IMArgList, int);

iml_desktop_t *new_user(iml_if_t * If,
			const char *user_name,
			const char *host_name,
			const char *display_id);
iml_desktop_t *find_user(iml_if_t * If, char *user_name, char *host_name, char *display_id);
void del_user(iml_desktop_t * desktop);

void add_session_to_desktop(iml_session_t *);
void remove_session_from_desktop(iml_session_t *);

static listener_id_t add_listener_to_desktop(iml_session_t *, void *, void *);
iml_listener_t *lookup_listener_from_desktop(iml_session_t *, int);
void delete_listener_from_desktop(iml_session_t *, int);

Public iml_if_t *
if_OpenIF(
    const char *if_path,
    const char *if_name,
    const char *if_locale,
    Bool call_openif
)
{
    iml_if_t *If;
    If = (iml_if_t *) calloc(1, sizeof(iml_if_t));
    if (if_configure(If, if_path, if_name, if_locale, call_openif) == False) {
        if (If->locale) {
            free(If->locale);
        }
        if (If->if_name) {
            free(If->if_name);
        }
        if (If->ifpath_name) {
            free(If->ifpath_name);
        }
        free(If);
        If = NULL;
    }
    return If;
}

Public void
if_CloseIF(
    iml_if_t * If,
    Bool call_openif
)
{
    if(call_openif == True) {
        If->ifm->if_CloseIF(If);
    }

    if (If->m) {
        free(If->m);
    }
    if (If->locale) {
        free(If->locale);
    }
    if (If->if_name) {
        free(If->if_name);
    }
    if (If->ifpath_name) {
        free(If->ifpath_name);
    }

    close_le_module(If->dl_module);

    free(If);
}

Public Bool
if_GetIFValues(
    iml_if_t * If,
    IMArgList args,
    int n_args
)
{
    if (If) {
        return If->ifm->if_GetIFValues(If, args, n_args);
    }
    return False;
}

Public Bool
if_SetIFValues(
    iml_if_t * If,
    IMArgList args,
    int n_args
)
{
    if (If) {
        return If->ifm->if_SetIFValues(If, args, n_args);
    }
    return False;
}

#ifdef	sun
mutex_t		desktop_mutex;
#elif	WIN32
HANDLE		desktop_mutex;
#else	/* Linux */
pthread_mutex_t	desktop_mutex;
#endif


Public iml_session_t *
if_CreateSC(
    iml_if_t * If,
    IMArgList args,
    int n_args
)
{
    iml_session_t *s = NULL;
    Bool ret;
    IMArg *p = args;
    int i;
    
    iml_desktop_t *desktop = (iml_desktop_t *) 0;
    
    char *user_name = (char *) 0;
    char *host_name = (char *) 0;
    char *display_id = (char *) 0;
    
    for (i = 0; i < n_args; i++, p++) {
        if (p->id == UI_USER_NAME) {
            user_name = p->value;
        } else if (p->id == UI_HOST_NAME) {
            host_name = p->value;
        } else if (p->id == UI_DISPLAY_ID) {
            display_id = p->value;
        }
    }
    
    if (!user_name || !host_name || !display_id) {
        return NULL;
    }
    SUNIM_LOCK(desktop_mutex);
    desktop = find_user(If, user_name, host_name, display_id);
    if (!desktop) {
        desktop = new_user(If, user_name, host_name, display_id);
        ret = If->ifm->if_OpenDesktop(desktop, args, n_args);
        if (ret == False) {
            del_user(desktop);
	    SUNIM_UNLOCK(desktop_mutex);
            return NULL;
        }
        /* Initialize the listener_id and listener_count */
        desktop->listener_id = 0;
        desktop->listener_count = 0;
    }
    /*
     * All LE specific remote file loading and hotkey registration 
     * has to be completed before unlocking desktop, if each connection
     * is assigned to one thread. If each user is assigned to one thread
     * locking is not needed.
     */
    SUNIM_UNLOCK(desktop_mutex);
    s = iml_construct_session(desktop, args, n_args);
    
    if (s) {
        add_session_to_desktop(s);
    }
    return s;
}

Public Bool
if_DestroySC_WithoutDesktopDestruction(
    iml_session_t * s
)
{
    if (s) {
	int i;
        Bool ret;
	IMFeedbackList *flist;
        
        ret = s->If->ifm->if_DestroySC(s);
        
        s->If->m->iml_delete(s);
        s->If->m->iml_delete2(s);
        
        remove_session_from_desktop(s);
        
        if (s->status_cache.text && s->status_cache.text->text.utf_chars) {
            free((char *) s->status_cache.text->text.utf_chars);
	}
        if (s->status_cache.text && s->status_cache.text->feedback) {
	    for(i=0;i<DEFAULTStatusCacheSize;i++){
		flist=&s->status_cache.text->feedback[i];
		if(flist){
		    free(flist->feedbacks);
		}
	    }
	    free(s->status_cache.text->feedback);
	}

        if (s->status_cache.text) {
            free((char *) s->status_cache.text);
	}

        if (s->PreEditTextInfo.text && s->PreEditTextInfo.text->text.utf_chars) {
            free((char *) s->PreEditTextInfo.text->text.utf_chars);
	}
        if (s->PreEditTextInfo.text && s->PreEditTextInfo.text->feedback) {
	    for(i=0;i<s->PreEditTextBufferSize;i++){
		flist=&s->PreEditTextInfo.text->feedback[i];
		if(flist){
		    free(flist->feedbacks);
		}
	    }
	    free(s->PreEditTextInfo.text->feedback);
        }

        if (s->PreEditTextInfo.text) {
            free((char *) s->PreEditTextInfo.text);
	}
        
        free(s);
        
        return ret;
    }
    return False;
}

Public Bool
if_DestroySC(
    iml_session_t * s
)
{
    if (s) {
        Bool ret;
        iml_desktop_t *desktop = s->desktop;
	iml_if_t *iml_if = s->If;

        ret = if_DestroySC_WithoutDesktopDestruction(s);
        if (!desktop->session_list) {
            iml_if->ifm->if_CloseDesktop(desktop);
            del_user(desktop);
        }
        return ret;
    }
    return False;
}

Public Bool
if_SetSCValues(
    iml_session_t * s,
    IMArgList args,
    int n_args
)
{
    if (s) {
        Bool ret = s->If->ifm->if_SetSCValues(s, args, n_args);
        s->If->m->iml_delete(s);
        return ret;
    }
    return False;
}

Public Bool
if_GetSCValues(
    iml_session_t * s,
    IMArgList args,
    int n_args
)
{
    if (s) {
	Bool ret=s->If->ifm->if_GetSCValues(s, args, n_args);
        s->If->m->iml_delete(s);
        return ret;
    }
    return False;
}

Public IMText *
if_ResetSC(
    iml_session_t * s
)
{
    IMText *prs = NULL;
    if (s) {
        s->If->m->iml_delete(s);
        prs = s->If->ifm->if_ResetSC(s);
	if(!prs){
	    s->If->m->iml_delete(s);
	}
    }
    return prs;
}

Public void
if_SetSCFocus(
    iml_session_t * s
)
{
    iml_inst *rv = NULL;
    iml_inst *lp;
    if (s) {
        if (s->status_cache.text->char_length) {
            lp = s->If->m->iml_make_status_start_inst(s);
            s->If->m->iml_link_inst_tail(&rv, lp);
            lp = s->If->m->iml_make_status_draw_inst(s, s->status_cache.text);
            s->If->m->iml_link_inst_tail(&rv, lp);
            lp = s->If->m->iml_execute(s, &rv);
        }
        s->If->ifm->if_SetSCFocus(s);
        s->If->m->iml_delete(s);
    }
    return;
}

Public void
if_UnsetSCFocus(
    iml_session_t * s
)
{
    iml_inst *rv = NULL;
    iml_inst *lp;
    if (s) {
        lp = s->If->m->iml_make_status_done_inst(s);
        s->If->m->iml_execute(s, &lp);
        s->If->ifm->if_UnsetSCFocus(s);
        s->If->m->iml_delete(s);
    }
    return;
}

Public void
if_SendEvent(
    iml_session_t * s,
    IMInputEvent * e
)
{
    if (s) {
        s->If->ifm->if_SendEvent(s, e);
        s->If->m->iml_delete(s);
    }
    return;
}

Public void
if_SendEvent_AuxGet(
    iml_session_t * s,
    IMInputEvent * e
)
{
    if (s) {
        s->If->ifm->if_SendEvent(s, e);
        /* DON'T CALL s->If->m->iml_delete(s) HERE! */
    }
    return;
}

/*
 * generic constructor of imlogic session struct
 */

Public iml_session_t *
iml_construct_session(
    iml_desktop_t * desktop,
    IMArgList args,
    int num_args
)
{
    iml_if_t *If = desktop->If;
    iml_session_t *s;
    int i;
    Bool ret;
    
    s = (iml_session_t *) calloc(1, sizeof(iml_session_t));
    s->desktop = desktop;
    s->If = If;
    s->next = NULL;

#ifdef ENABLE_EIMIL
    if (If->eh != EIMIL_VOID_HANDLE)
	EIMIL_duplicate_handle(&s->eh, If->eh);
#endif

    if ((ret=If->ifm->if_CreateSC(s, args, num_args)) == False){
        free(s);
        return NULL;
    }

    s->status_cache.text = (IMText *) calloc(1, sizeof(IMText));
    s->status_cache.text->encoding = UTF16_CODESET;
    s->status_cache.text->feedback = (IMFeedbackList *) calloc(1, sizeof(IMFeedbackList) * DEFAULTStatusCacheSize);
    s->status_cache.text->text.utf_chars = (UTFCHAR *) calloc(1, sizeof(UTFCHAR) * DEFAULTStatusCacheSize);
    s->status_cache.text->char_length = 0;
    s->status_cache.text->count_annotations = 0;
    s->status_cache.text->annotations = NULL;
    for (i = 0; i < DEFAULTStatusCacheSize; i++) {
        IMFeedbackList *fbl = &s->status_cache.text->feedback[i];
        fbl->feedbacks = (IMFeedback *) calloc(1, sizeof(IMFeedback)*DEFAULTFeedbackSize);
    }

    s->PreEditTextInfo.text = (IMText *) calloc(1, sizeof(IMText));
    s->PreEditTextInfo.text->encoding = UTF16_CODESET;
    s->PreEditTextInfo.text->text.utf_chars = (UTFCHAR *) calloc(1, sizeof(UTFCHAR) * DEFAULTPreEditTextBufferSize);
    s->PreEditTextInfo.text->feedback = (IMFeedbackList *) calloc(1, sizeof(IMFeedbackList) * DEFAULTPreEditAttrBufferSize);
    for (i = 0; i < DEFAULTPreEditTextBufferSize; i++) {
        IMFeedbackList *fbl = &s->PreEditTextInfo.text->feedback[i];
        fbl->feedbacks = (IMFeedback *) calloc(1, sizeof(IMFeedback)*DEFAULTFeedbackSize);
    }

    s->PreEditTextBufferSize = DEFAULTPreEditTextBufferSize;
    s->PreEditAttrBufferSize = DEFAULTPreEditAttrBufferSize;

    return (iml_session_t *) s;
}

iml_desktop_t *
find_user(
    iml_if_t * If,
    char *user_name,
    char *host_name,
    char *display_id
)
{
    iml_desktop_list p, *prev;
    if (If->desktop_list == NULL) {
	return (iml_desktop_t *) NULL;
    }
    for (prev = &If->desktop_list; (p = *prev) != 0; prev = &p->next) {
        if (strcmp(p->user_name, user_name) == 0 &&
	    strcmp(p->host_name, host_name) == 0 &&
	    strcmp(p->display_id, display_id) == 0) {
            return p;
        }
    }
    return (iml_desktop_t *) NULL;
}

void
del_user(
    iml_desktop_t * desktop
)
{
    iml_if_t *If = desktop->If;
    iml_desktop_list p, *prev;
    for (prev = &If->desktop_list; (p = *prev) != 0; prev = &p->next) {
        if (strcmp(p->user_name, desktop->user_name) == 0 &&
	    strcmp(p->host_name, desktop->host_name) == 0 &&
	    strcmp(p->display_id, desktop->display_id) == 0) {
#ifdef DEBUG
            printf("LE %s: %s@%s is deleted\n", If->if_name, p->user_name, p->host_name);
#endif
            *prev = p->next;
            free(p->user_name);
            free(p->host_name);
            free(p->display_id);
            free(p);
            If->desktop_count--;
            break;
        }
        if (p->next == NULL) {
            break;
        }
    }
}

static listener_id_t 
add_listener_to_desktop(
    iml_session_t *s,
    void *listener,
    void *value
)
{
    iml_desktop_t *desktop = s->desktop;

    // Create a Listener and return the unique ID

    iml_listener_t *l = (iml_listener_t *) calloc(1, sizeof(iml_listener_t));
    l->s = s;
    l->listener_name = "";
    desktop->listener_id++;
    l->listener_id = desktop->listener_id;
    l->listener = listener; /* Callback function that needs to be called */
    l->private_data = value;
    l->next = desktop->l_list;
    desktop->l_list = l;
    desktop->listener_count++;
    return l->listener_id;
}

iml_listener_t *
lookup_listener_from_desktop(
    iml_session_t *s,
    int id
)
{
    iml_desktop_t *desktop = s->desktop;
    iml_listener_list p, *prev;

    for (prev = &desktop->l_list; (p = *prev) != 0; prev = &p->next) {
        if (p->listener_id == id) {
            return p;
        }
    }

    return (iml_listener_t *)NULL;
}

void 
delete_listener_from_desktop(
    iml_session_t *s,
    int id
)
{
    iml_desktop_t *desktop = s->desktop;
    iml_listener_list p, *prev;

    for (prev = &desktop->l_list; (p = *prev) != 0; prev = &p->next) {
        if (p->listener_id == id) {
            *prev = p->next;
            free(p->listener_name);
            free(p);
            desktop->listener_count--;
            break;
        }
        if (p->next == NULL) {
	    break;
        }
    }
    return 0;
}

iml_desktop_t *
new_user(
    iml_if_t * If,
    const char *user_name,
    const char *host_name,
    const char *display_id
)
{
    iml_desktop_t *p = (iml_desktop_t *) calloc(1, sizeof(iml_desktop_t));

    p->addListenerToDesktop = add_listener_to_desktop;
    p->lookupListenerFromDesktop = lookup_listener_from_desktop;
    p->deleteListenerFromDesktop = delete_listener_from_desktop;

    p->user_name = strdup(user_name);
    p->host_name = strdup(host_name);
    p->display_id = strdup(display_id);
    p->next = If->desktop_list;
    p->If = If;
    If->desktop_list = p;
    If->desktop_count++;
#ifdef DEBUG
    printf("LE %s: %s@%s is entered\n", If->if_name, user_name, host_name);
#endif
    return (iml_desktop_t *) p;
}

void
remove_session_from_desktop(
    iml_session_t * s
)
{
    iml_session_list p, *prev;
    for (prev = &s->desktop->session_list; (p = *prev) != 0; prev = &p->next) {
        if (p == s) {
            *prev = p->next;
            s->desktop->session_count--;
            break;
        }
        if (p->next == NULL) {
            break;
        }
    }
}

void
add_session_to_desktop(
    iml_session_t * s
)
{
    s->next = s->desktop->session_list;
    s->desktop->session_list = s;
    s->desktop->session_count++;
}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
