/*
This source file is part of Scol
For the latest info, see http://www.scolring.org

Copyright (c) 2010 Stephane Bisaro, aka Iri <iri@irizone.net>

This program 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 of the License, or (at your option) any later
version.

This program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

For others informations, please contact us from http://www.scolring.org/
*/


#include "stringtab.h"

/* Scol function STABconvert : fun [Chn S] STab */
int SCOLSTABconvert2 (mmachine m)
{
    int mstring;
    char * string;
    /*char * tabstring[1];*/
    int size, i = 0;

    MMechostr (MSKDEBUG, "SCOLSTABconvert : entering\n");

    mstring = MTOP (MMpull (m));
    if (mstring == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABconvert error : string is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    size = MMsizestr (m, mstring);
    string = MMstartstr (m, mstring);

    /*string = malloc (sizeof (char) * size+1);
    strncpy (string, MMstartstr (m, mstring),size);*/

    while (string != '\0')
    {
        /*tabstring[i] = malloc (sizeof (char));*/ /* je devrais tester le malloc ... */
        /*memset (tabstring[i], string[i], sizeof (char));*/
        /*Mpushstrbloc (m, (char) string[i]);*/
        MMpush (m, PTOM (string[i]));
        i++;
    }

    MMpush (m, ITOM (size));
    MBdeftab (m);
    return 0;
}



int SCOLSTABconvert4 (mmachine m)
{
    int mstring;
    char * string;
    /*char c[2];*/
    char * c;
    /*char * tabstring[1];*/
    int size, i = 0;
    int tabmem;

    MMechostr (MSKDEBUG, "SCOLSTABconvert : entering\n");

    mstring = MTOP (MMpull (m));
    /*mstring = MTOP (MMget (m, 0));*/
    if (mstring == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABconvert error : string is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    size = MMsizestr (m, mstring);
    string = MMstartstr (m, mstring);

    tabmem = MMmalloc (m, size, TYPETAB);
    if (tabmem == NIL)
        return MERRMEM;


c = malloc (sizeof (char) * 2);

m->tape[tabmem+SizeHeader] = size;
    for (; i < size; i++)
    {
        /*memset (c, string[i], 1);*/
        /*c[0] = string[i];
        c[1] = 0;
        MMechostr (MSKDEBUG, "SCOLSTABconvert : %s    %d\n", c, i);
        MMstore (m, tabmem, i, c);*/

        c = (char *) &m->tape[i];
        c[0] = string[i];
        c[1] = 0;
        /*m->tape[tabmem+SizeHeader+i] = (int) c;*/
        MMstore (m, tabmem, i, (int) c);
        MMechostr (MSKDEBUG, "SCOLSTABconvert : %s    %d\n", c, i);

        /*m->top[m->pp] = tabmem+tabmem+1;*/
/*
m->tape[s+SizeHeader]=l;
cr=(char*)&m->tape[s+SizeHeader+1];
strcpy(cr,buf);*/
    }

    MMpush (m, tabmem+tabmem+1);
    /*free (c);*/
    /*m->pp--;
    m->top[m->pp]=tabmem+tabmem+1;*/
    /*MMset (m, 0, tabmem+tabmem+1);*/
    return 0;
}

#if defined _WIN32 || defined __WIN32__
int SCOLSTABconvert3 (mmachine m)
{
    int mstring;
    char * string;
    /*char c[2];*/
    char * c;
    /*char * tabstring[1];*/
    int size, i = 0;
    int tabmem;

    MMechostr (MSKDEBUG, "SCOLSTABconvert : entering\n");

    mstring = MTOP (MMpull (m));
    /*mstring = MTOP (MMget (m, 0));*/
    if (mstring == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABconvert error : string is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    size = MMsizestr (m, mstring);
    string = MMstartstr (m, mstring);

    tabmem = MMmalloc (m, size, TYPETAB);
    if (tabmem == NIL)
        return MERRMEM;
MMechostr (MSKDEBUG, "SCOLSTABconvert : 0\n");
    for (i = 0; i < size; i++)
    {
        m->tape[tabmem+SizeHeader]=1;
        c = (char *) &m->tape[tabmem+SizeHeader+i+2];
        c[0] = string[i];
        c[1] = 0;
        m->top[m->pp]=tabmem+tabmem+1;
    }
MMechostr (MSKDEBUG, "SCOLSTABconvert : 4\n");
    m->top[m->pp]=tabmem+tabmem+1;MMechostr (MSKDEBUG, "SCOLSTABconvert : 5\n");
    return 0;
}
#endif





















static int ObjSPushChar (mmachine m, char c)
{
    /*int s;
    char cr;

    if (c == NIL)
    {
        MMechostr (0, "ObjSPushChar : char is null\n");
        MMpush (m, NIL);
        return 1;
    }

    s = MMmalloc (m, sizeof (char), TYPEBUF);
    if (s == NIL)
    {
        MMechostr (0, "ObjSPushChar : memory error\n");
        MMpush (m, NIL);
        return 1;
    }
    m->tape[s+SizeHeader] = sizeof (char);

    cr=(char)&m->tape[s+SizeHeader+1];
    cr = s;
    return MMpush(m,s+s+1);*/

    m->pp--;
    m->top[m->pp] = c;

    return MMneedMemory(m, 0, sizeof (char));
}




/**
 * \brief Destroy any object
 *
 * \return int : 0
 */
static int ObjSDestroy (mmachine m, int handsys, int mobj)
{
    char *c;

    MMechostr (MSKDEBUG, "ObjSDestroy: entering ...\n");

    c = (char *) MMfetch (m, MTOP (mobj), STAB_HANDLE);
    if (c == NULL)
    {
        MMechostr (MSKDEBUG, "ObjSDestroy : object already destroyed\n");
        return 0;
    }

    MMstore (m, MTOP (mobj), STAB_HANDLE, (int) NULL);
    MMechostr (MSKDEBUG, "ObjSDestroy: object has been destroyed\n");

    return 0;
}

/**
 * \brief Create an new array from a string : fun [Chn S] ObjS
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : a string to convert
 * stage 1 : the channel
 * return : the new object
 */
int SCOLSTABcreate (mmachine m)
{
    int mstring, mchannel;
    char *string, *tabstring;
    int size, i = 0;
    int tabmem;

    MMechostr (MSKDEBUG, "SCOLSTABcreate : entering\n");

    mstring = MTOP (MMpull (m));
    mchannel = MMget (m, 0);

    if (mchannel == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABcreate error : channel is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    if (mstring == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABcreate error : string is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    size = MMsizestr (m, mstring);
    string = MMstartstr (m, mstring);

    tabmem = MMmalloc (m, size+1, TYPEBUF);
    tabstring = (char *) malloc (sizeof (char) * size +1);

    if ((tabmem == NIL) || (tabstring == (char *) NULL))
    {
        MMechostr (MSKDEBUG, "SCOLSTABcreate memory error\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    for (i = 0; i < size; i++)
    {
        tabstring[i] = string[i];
    }
    tabstring[size] = '\0';

    MMstore (m, tabmem, STAB_HANDLE, (int) tabstring);
    MMpush (m, PTOM (tabmem));
    OBJcreate (m, ObjS, (int) tabstring, -1, -1);

    return 0;
}

/**
 * \brief Display a character in the console : fun [ObjS I] ObjS
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : a position : the position of the character, if negative, the position is from the end, if nil, all are displayed
 * return : the same object
 */
int SCOLfooTabS (mmachine m)
{
    int mtab, mpos;
    char *stab;

    MMechostr (MSKDEBUG, "SCOLfooTabS : entering\n");

    mpos = MTOI (MMpull (m));
    mtab = MTOP (MMget (m, 0));

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLfooTabS error : object is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);

    if (mpos == NIL)
        0;
    else if (mpos < 0)
        mpos = strlen (stab) + mpos;
    else if (mpos >= strlen (stab))
        mpos = strlen (stab) - 1;

    if (mpos == NIL)
        printf ("%s\n", stab);
    else
        printf ("%c\n", stab[mpos]);

    return 0;
}

/**
 * \brief Display the first character in the console from a string : fun [S] S
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : a string
 * return : the same string
 */
int SCOLSTABfooC (mmachine m)
{
    int mc;
    char *c;

    MMechostr (MSKDEBUG, "SCOLSTABfooC : entering\n");

    mc = MTOP (MMget (m, 0));

    if (mc == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLSTABfooC error : object is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    c = (char*) MMstartstr (m, mc);

    printf ("%c\n", c[0]);
    return 0;
}

/**
 * \brief Get a character from an object : fun [ObjS I] S
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : a position : the position of the character, if negative, position from the end, if too large, the last character
 * return : a string with one character (c\0)
 */
int SCOLtabsGet (mmachine m)
{
    int mtab, mpos;
    char *stab, *s;

    MMechostr (MSKDEBUG, "SCOLtabsGet : entering\n");

    mpos = MTOI (MMpull (m));
    mtab = MTOP (MMpull (m));

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsGet error : object is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);
    s = malloc (sizeof (char) * 2);

    if (mpos == NIL)
        mpos = 0;
    else if (mpos < 0)
        mpos = strlen (stab) + mpos;
    else if (mpos >= strlen (stab))
        mpos = strlen (stab) - 1;

    s[0] = stab[mpos];
    s[1] = '\0';

    /* ObjSPushChar (m, stab[mpos]);*/
    Mpushstrbloc (m, s);
    free (s);

    return 0;
}

/**
 * \brief Set a character : fun [ObjS S I] ObjS
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : a string (only the first character will be added)
 * stage 1 : a position : the position of the character, if negative, position from the end, if too large, the last character
 * return : the same object
 */
int SCOLtabsSet (mmachine m)
{
    int mtab, ms, mpos;
    char *stab, *s;

    MMechostr (MSKDEBUG, "SCOLtabsSet : entering\n");

    mpos = MTOI (MMpull (m));
    ms = MTOP (MMpull (m));
    mtab = MTOP (MMget (m, 0));

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsSet error : object is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    if (ms == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsSet error : no character to add\n");
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);

    if (stab == NULL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsSet error : object is null\n");
        MMpush (m, NIL);
        return 0;
    }

    s = (char*) MMstartstr (m, ms);

    if (mpos < 0)
        mpos = strlen (stab) + mpos;
    else if (mpos >= strlen (stab))
        mpos = strlen (stab) - 1;

    stab[mpos] = s[0];

    MMset (m, 0, PTOM (mtab));

    return 0;
}

/**
 * \brief internal function : add or remove any character(s)
 *
 * \param mmachine
 * \param int : 1 to add, -1 to remove
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : a string to add (if flag is 1) or an integer, the number of the characters to remove
 * stage 2 : a position : the position of the character, if negative, position from the end, if too large, the last character
 * return : the same object
 */
static int tabsAddDel (mmachine m, int flag)
{
    int mtab, ms, mpos;
    int len, i, j, k;
    char *stab, *oldstab, *s;

    MMechostr (MSKDEBUG, "tabsAddDel : entering\n");

    mpos = MTOI (MMpull (m));
    if (flag == 1)
        ms = MTOP (MMpull (m)); /* chaine à ajouter */
    else
        ms = MTOI (MMpull (m)); /* nombre de caractères à supprimer */
    mtab = MTOP (MMget (m, 0));
    /*mtab = MTOP (MMpull (m));*/

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "tabsAddDel error : object is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    if ((ms == NIL) && (flag == 1))
    {
        MMechostr (MSKDEBUG, "tabsAddDel error : no string given\n");
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);

    if (stab == NULL)
    {
        MMechostr (MSKDEBUG, "tabsAddDel error : object is null\n");
        MMpush (m, NIL);
        return 0;
    }

    if (mpos < 0)
        mpos = strlen (stab) + mpos;
    else if (mpos >= strlen (stab))
        mpos = strlen (stab) - 1;

    /*MMstore (m, MTOP (mtab), STAB_HANDLE, (int) NULL);*/

    if (flag == 1)
    {
        s = (char*) MMstartstr (m, ms);
        len = sizeof (char) * strlen (s);
    }
    else
    {
        if (ms < 0)
            ms = strlen (stab) + ms - mpos;
        len = ms;
    }

    oldstab = (char *) malloc (strlen (stab) + 1);
    strcpy (oldstab, stab);

    len = sizeof (char) * strlen (stab) + len * flag;
    /*MMsetsizestr (m, mtab, len+1);*/
    stab = (char *) realloc (stab, len+1);
    if (flag == 1)
        strncpy (stab, oldstab, len);
    stab[len] = '\0';

    /*m->tape[mtab] = len+1;*/
    if (stab == (char *) NULL)
    {
        MMechostr (MSKDEBUG, "tabsAddDel memory error\n");
        MMset (m, 0, NIL);
        return 0;
    }

    k = mpos+1;

    if (flag == 1)
    {
        for (i = mpos+1; i < len-1; i++)
        {
            for (j = 0; j < strlen (s); j++)
            {
                stab[i] = s[j];
                i++;
            }
            if (i < len-1)
                stab[i] = oldstab[k];
            k++;
        }
    }
    else if (flag == -1)
    {
        for (i = mpos+1+ms;i < strlen (oldstab); i++)
        {
            stab[k] = oldstab[i];
            k++;
        }
    }

    stab[len] = '\0';

    MMsetsizestr (m, mtab, len+1); /* utile ou pas ? MMsize retourne toujours l'ancienne valeur et je ne comprends la valeur retournée par MMsizestr... */
    MMstore (m, (mtab), STAB_HANDLE, (int) stab);

    /* MMset (m, 0, PTOM (mtab));*/
    free (oldstab);

    return 0;
}

/**
 * \brief Add a string to an object : fun [ObjS S] ObjS
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : a string. All string will be added from the position. The array will be reallocated
 * stage 2 : a position : the position of the character, if negative, position from the end, if too large, the last character
 * return : the same object
 */
int SCOLtabsAdd (mmachine m)
{
    return tabsAddDel (m, 1);
}

/**
 * \brief Remove any character(s) to an object : fun [ObjS I] ObjS
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * stage 1 : an integer. This number of the characters will be removed from the position. The array will be reallocated
 * stage 2 : a position : the position of the character, if negative, position from the end, if too large, the last character
 * return : the same object
 */
int SCOLtabsDel (mmachine m)
{
    return tabsAddDel (m, -1);
}

/**
 * \brief Get the size : fun [ObjS] I
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * return : the size.
 */
int SCOLtabsSize (mmachine m)
{
    int mtab;
    char *stab;

    MMechostr (MSKDEBUG, "SCOLtabsSize : entering\n");

    mtab = MTOP (MMpull (m));

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsSize error : object is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);

    MMpush (m, ITOM (strlen (stab)));
    return 0;
}

/**
 * \brief Convert an object to a string : fun [ObjS] S
 *
 * \param mmachine
 * \return int : 0
 *
 * Stack :
 * stage 0 : the object
 * return : a string
 */
int SCOLtabsToS (mmachine m)
{
    int mtab;
    char *stab;

    MMechostr (MSKDEBUG, "SCOLtabsToS : entering\n");

    mtab = MTOP (MMpull (m));

    if (mtab == NIL)
    {
        MMechostr (MSKDEBUG, "SCOLtabsToS error : object is nil\n");
        MMpull (m);
        MMpush (m, NIL);
        return 0;
    }

    stab = (char *) MMfetch (m, mtab, STAB_HANDLE);

    Mpushstrbloc (m, stab);
    return 0;
}








/* API definitions */

char* stringtab_name[STRINGTAB_PKG_NB] =
{
    "ObjS",

    "STABcreate",
    "STABfoo",
    "STABfooC",
    "STABget",
    "STABset",
    "STABadd",
    "STABdel",
    "STABsize",
    "STABtoS"
};

int (*stringtab_fun[STRINGTAB_PKG_NB]) (mmachine m) =
{
    NULL,

    SCOLSTABcreate,
    SCOLfooTabS,
    SCOLSTABfooC,
    SCOLtabsGet,
    SCOLtabsSet,
    SCOLtabsAdd,
    SCOLtabsDel,
    SCOLtabsSize,
    SCOLtabsToS
};

int stringtab_narg[STRINGTAB_PKG_NB] =
{
    TYPTYPE,

    2,           /* STABcreate */
    2,           /* STABfoo */
    1,          /* STABfooC */
    2,           /* STABget */
    3,           /* STABset */
    3,          /* STABadd */
    3,          /* STABdel */
    1,           /* STABsize */
    1           /* STABtoS */
};

char* stringtab_type[STRINGTAB_PKG_NB] =
{
    NULL,

    "fun [Chn S] ObjS",         /* STABcreate */
    "fun [ObjS I] ObjS",         /* STABfoo */
    "fun [S] S",          /* STABfooC */
    "fun [ObjS I] S",            /* STABget */
    "fun [ObjS S I] ObjS",       /* STABset */
    "fun [ObjS S I] ObjS",       /* STABadd */
    "fun [ObjS I I] ObjS",       /* STABdel */
    "fun [ObjS] I",              /* STABsize */
    "fun [ObjS] S"              /* STABtoS */
};

int SCOLinitSTRINGTABclass (mmachine m)
{
    int k = 0;
    MMechostr (MSKDEBUG, "SCOLinitSTRINGTABclass library : entering\n");

    ObjS = OBJregister (STRINGTAB_RFL_NB, 1, ObjSDestroy, "ObjSType");
    k = PKhardpak (m, "STRINGTABengine", STRINGTAB_PKG_NB, stringtab_name, stringtab_fun, stringtab_narg, stringtab_type);
    return k;
}

