/*
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 "tempfile.h"

/* Destroy FileTemp object */
int FILETEMPdelete (mmachine m, int handsys, int objm)
{
    FILE * f;
    if (objm == NIL)
    {
        MMechostr(MSKDEBUG, "FILETEMPdelete : object is nil\n");
        return 0;
    }

    f = (FILE *) MMfetch (m, MTOP (objm), TEMPFILE_HANDLE);
    if (f == NULL)
    {
        MMechostr(MSKDEBUG, "FILETEMPdelete : object already destroyed\n");
        MMset (m, 0, NIL);
        return 0;
    }
    fclose (f);
    MMstore (m, MTOP (objm), TEMPFILE_HANDLE, (int) NULL);
    MMechostr(MSKDEBUG, "FILETEMPdelete: object has been destroyed\n");

    return 0;
}

/** \brief _FILEOpenTtemp : create a temoprarly file */
int SCOL_FILEOpenTemp (mmachine m)
{
    int l, o;
    FILE * f;

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

    if (MMget (m, 0) == NIL)  /* channel is nil */
    {
        MMechostr (MSKDEBUG, "SCOL_FILEOpenTemp error : bad argument\n");
        MMset (m, 0, NIL);
        return 0;
    }

    f = tmpfile ();

    if (!f)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEOpenTemp error : file cannot create : %d : %s\n", errno, strerror (errno));
        MMset (m, 0, NIL);
        return 0;
    }

    l = (sizeof (FILE) + 3) >> 2;
    o = MMmalloc (m, l, TYPETAB);
    if (o == NIL)
	{
	    MMechostr (MSKDEBUG, "SCOL_FILEOpenTemp : memory allocation failed !");
	    MMpull (m); /* channel */
	    MMpush (m, NIL);
	    return 0;
	}
	MMstore (m, o, TEMPFILE_HANDLE, (int) f);
    MMpush (m, PTOM (o));

    return OBJcreate (m, OBJTYPFILETEMP, (int) f, -1, -1);
}

/** \brief _FILECloseTemp : Close a temporarly file = return 0 if ok, else nil */
int SCOL_FILECloseTemp (mmachine m)
{
    int mfiletemp;
    FILE * f;

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

    mfiletemp = MTOP (MMget (m, 0));
    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILECloseTemp error : scol file object is nil\n");
        return 0; /* stack is already at nil ... */
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILECloseTemp error : file is NULL\n");
        MMset (m, 0, NIL);
        return 0;
    }

    OBJdelTH (m, OBJTYPFILETEMP, (int) f);
    MMset (m, 0, 0);
    return 0;
}

/** \brief _FILEReadTemp : Try to read a specific number of bytes, return the result */
int SCOL_FILEReadTemp (mmachine m)
{
    int mfiletemp, msize;
    FILE * f;
    char * s;

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

    msize = MTOI (MMpull (m));
    mfiletemp = MTOP (MMpull (m));

    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEReadTemp error : scol file object is nil\n");
        MMpush (m, NIL);
        return 0;
    }
    if (msize < 0)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEReadTemp error : size number is incorrect\n");
        MMpush (m, NIL);
        return 0;
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEReadTemp error : file is NULL\n");
        MMpush (m, NIL);
        return 0;
    }

    s = (char *) malloc (msize * sizeof (char) + 1);
    if (s == NULL)
        return MERRMEM;

    fread (s, 1, msize, f);
    Mpushstrbloc (m, s);
    free (s);
    return 0;
}

/** \brief _FILESizeTemp : return the size of a file ; size limit = 4 Go under Windows (2 ^ 31 - 1) */
int SCOL_FILESizeTemp (mmachine m)
{
    int mfiletemp;
    FILE * f;
    int postemp, size;

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

    mfiletemp = MTOP (MMpull (m));
    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILESizeTemp error : scol file object is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILESizeTemp error : file is NULL\n");
        MMpush (m, NIL);
        return 0;
    }

/*#if defined linux || defined __linux*/
    postemp = ftell (f);
    fseek (f, 0, SEEK_END);
    size = ftell (f);
    fseek (f, postemp, SEEK_SET);

    MMpush (m, ITOM (size));
    return 0;

/* under Windows, this code can return a size > 4 Go */
/*#elif defined _WIN32 || defined __WIN32__
    WIN32_FILE_ATTRIBUTE_DATA attr;
    int ret = 0;

    if (GetFileAttributesEx(FileName, GetFileExInfoStandard, &attr))
    {
        if (attr.nFileSizeHigh == 0)
        {
            size = attr.nFileSizeLow;
            MMpush (m, ITOM (size));
            return 0;
        }
        else
            MMpush (m, NIL);
    }
    else
        MMpush (m, NIL);
    return 0;
#endif*/

}

/** \brief FILETellTemp : return the current cursor position */
int SCOL_FILETellTemp (mmachine m)
{
    int mfiletemp;
    FILE * f;
    int pos = 0;

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

    mfiletemp = MTOP (MMpull (m));
    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILETellTemp error : scol file object is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILETellTemp error : file is NULL\n");
        MMpush (m, NIL);
        return 0;
    }

    pos = ftell (f);
    MMpush (m, ITOM (pos));
    return 0;
}

/** \brief _FILESeekTemp : move the cursor position. */
int SCOL_FILESeekTemp (mmachine m)
{
    int mfrom, mpos, mfiletemp;
    FILE * f;
    int whence = SEEK_SET;  /* default */

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

    mfrom = MTOI (MMpull (m));
    mpos = MTOI (MMpull (m));
    mfiletemp = MTOP (MMpull (m));
    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILESeekTemp error : scol file object is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILESeekTemp error : file is NULL\n");
        MMpush (m, NIL);
        return 0;
    }

    if (mfrom == 1)
        whence = SEEK_CUR;
    if(mfrom == 2)
        whence = SEEK_END;

    MMpush (m, ITOM (fseek (f, mpos, whence)));
    return 0;
}

/** \brief _FILEWriteTmp : write a string to a temporarly file */
int SCOL_FILEWriteTmp (mmachine m)
{
    int mdata, mfiletemp;
    FILE * f;
    char * data;
    int size, bloc;

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

    mdata = MTOP (MMpull (m));
    mfiletemp = MTOP (MMpull (m));
    if (mfiletemp == NIL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEWriteTmp error : scol file object is nil\n");
        MMpush (m, NIL);
        return 0;
    }
    if (mdata == NIL) /* same thing than _createpack ... */
    {
        MMechostr (MSKDEBUG, "SCOL_FILEWriteTmp error : string is nil\n");
        MMpush (m, NIL);
        return 0;
    }

    f = *(FILE**) MMstart (m, mfiletemp);

    if (f == NULL)
    {
        MMechostr (MSKDEBUG, "SCOL_FILEWriteTmp error : file is NULL\n");
        MMpush (m, NIL);
        return 0;
    }

    data = MMstartstr (m, mdata);
    size = MMsizestr (m, mdata);
    bloc = sizeof (char); /* ok, it's 1 but we could make several case (not only string ?) */

    fwrite (data, bloc, size, f);
    MMpush (m, ITOM (0));
    return 0;
}











/* API definitions */

char* tempfile_name[TEMPFILE_PKG_NB] =
{
    "FileTemp",
    "_FILEOpenTemp",
    "_FILECloseTemp",
    "_FILEReadTemp",
    "_FILESizeTemp",
    "_FILETellTemp",
    "_FILESeekTemp",
    "_FILEWriteTmp"
};

int (*tempfile_fun[TEMPFILE_PKG_NB]) (mmachine m) =
{
    NULL,
    SCOL_FILEOpenTemp,
    SCOL_FILECloseTemp,
    SCOL_FILEReadTemp,
    SCOL_FILESizeTemp,
    SCOL_FILETellTemp,
    SCOL_FILESeekTemp,
    SCOL_FILEWriteTmp
};

int tempfile_narg[TEMPFILE_PKG_NB] =
{
    TYPTYPE,    /* FileTemp */
    1,          /* _FILEOpenTtemp */
    1,          /* _FILECloseTemp */
    2,          /* _FILEReadTemp */
    1,          /* _FILESizeTemp */
    1,          /* _FILETellTemp */
    3,          /* _FILESeekTemp */
    2           /* _FILEWriteTmp */
};

char* tempfile_type[TEMPFILE_PKG_NB] =
{
    NULL,                               /* FileTemp */
    "fun [Chn] FileTemp",               /* _FILEOpenTtemp */
    "fun [FileTemp] I",                 /* _FILECloseTemp */
    "fun [FileTemp I] S",               /* _FILEReadTemp */
    "fun [Filetemp] I",                 /* _FILESizeTemp */
    "fun [FileTemp] I",                 /* _FILETellTemp */
    "fun [FileTemp I I] I",             /* _FILESeekTemp */
    "fun [FileTemp S] I"                /* _FILEWriteTmp */
};

int SCOLinitTEMPFILEclass (mmachine m)
{
    int k = 0;
    MMechostr (MSKDEBUG, "SCOLinitTEMPFILEclass library : entering\n");

    OBJTYPFILETEMP = OBJregister (0, 0, FILETEMPdelete, "OBJTYPFILETEMP");

    k = PKhardpak (m, "TEMPFILEengine", TEMPFILE_PKG_NB, tempfile_name, tempfile_fun, tempfile_narg, tempfile_type);
    return k;
}
