/*
  Copyright (C) 2010-2011  Charles Pence <charles@charlespence.net>
  Copyright (C) 1996-1997  Id Software, Inc.

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "pch.h"
#include "quakedef.h"
#include "filesystem/filesystem.h"

char    com_token[1024];


//============================================================================


// ClearLink is used for new headnodes
void ClearLink (link_t *l)
{
    l->prev = l->next = l;
}

void RemoveLink (link_t *l)
{
    l->next->prev = l->prev;
    l->prev->next = l->next;
}

void InsertLinkBefore (link_t *l, link_t *before)
{
    l->next = before;
    l->prev = before->prev;
    l->prev->next = l;
    l->next->prev = l;
}
void InsertLinkAfter (link_t *l, link_t *after)
{
    l->next = after->next;
    l->prev = after;
    l->prev->next = l;
    l->next->prev = l;
}


/*
============================================================================

                    BYTE ORDER FUNCTIONS

============================================================================
*/

qboolean        bigendien;

short   (*BigShort) (short l);
short   (*LittleShort) (short l);
int     (*BigLong) (int l);
int     (*LittleLong) (int l);
float   (*BigFloat) (float l);
float   (*LittleFloat) (float l);

short   ShortSwap (short l)
{
    byte    b1,b2;

    b1 = l&255;
    b2 = (l>>8)&255;

    return (b1<<8) + b2;
}

short   ShortNoSwap (short l)
{
    return l;
}

int    LongSwap (int l)
{
    byte    b1,b2,b3,b4;

    b1 = l&255;
    b2 = (l>>8)&255;
    b3 = (l>>16)&255;
    b4 = (l>>24)&255;

    return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
}

int     LongNoSwap (int l)
{
    return l;
}

float FloatSwap (float f)
{
    union
    {
        float   f;
        byte    b[4];
    } dat1, dat2;


    dat1.f = f;
    dat2.b[0] = dat1.b[3];
    dat2.b[1] = dat1.b[2];
    dat2.b[2] = dat1.b[1];
    dat2.b[3] = dat1.b[0];
    return dat2.f;
}

float FloatNoSwap (float f)
{
    return f;
}

/*
==============================================================================

            MESSAGE IO FUNCTIONS

Handles byte ordering and avoids alignment errors
==============================================================================
*/

//
// writing functions
//

void MSG_WriteChar (sizebuf_t *sb, int c)
{
    byte    *buf;

    buf = (byte *)SZ_GetSpace (sb, 1);
    buf[0] = c;
}

void MSG_WriteByte (sizebuf_t *sb, int c)
{
    byte    *buf;

    buf = (byte *)SZ_GetSpace (sb, 1);
    buf[0] = c;
}

void MSG_WriteShort (sizebuf_t *sb, int c)
{
    byte    *buf;

    buf = (byte *)SZ_GetSpace (sb, 2);
    buf[0] = c&0xff;
    buf[1] = c>>8;
}

void MSG_WriteLong (sizebuf_t *sb, int c)
{
    byte    *buf;

    buf = (byte *)SZ_GetSpace (sb, 4);
    buf[0] = c&0xff;
    buf[1] = (c>>8)&0xff;
    buf[2] = (c>>16)&0xff;
    buf[3] = c>>24;
}

void MSG_WriteFloat (sizebuf_t *sb, float f)
{
    union
    {
        float   f;
        int     l;
    } dat;


    dat.f = f;
    dat.l = LittleLong (dat.l);

    SZ_Write (sb, &dat.l, 4);
}

void MSG_WriteString (sizebuf_t *sb, char *s)
{
    if (!s)
        SZ_Write (sb, "", 1);
    else
        SZ_Write (sb, s, strlen(s)+1);
}

void MSG_WriteCoord (sizebuf_t *sb, float f)
{
    MSG_WriteLong(sb, (int)(f*1024));    
}

void MSG_WriteAngle (sizebuf_t *sb, float f)
{
    MSG_WriteLong(sb, (int)(f*1024));
}

//
// reading functions
//
int                     msg_readcount;
qboolean        msg_badread;

void MSG_BeginReading (void)
{
    msg_readcount = 0;
    msg_badread = false;
}

// returns -1 and sets msg_badread if no more characters are available
int MSG_ReadChar (void)
{
    int     c;

    if (msg_readcount+1 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = (signed char)net_message.data[msg_readcount];
    msg_readcount++;

    return c;
}

int MSG_ReadByte (void)
{
    int     c;

    if (msg_readcount+1 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = (unsigned char)net_message.data[msg_readcount];
    msg_readcount++;

    return c;
}

int MSG_ReadShort (void)
{
    int     c;

    if (msg_readcount+2 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = (short)(net_message.data[msg_readcount]
    + (net_message.data[msg_readcount+1]<<8));

    msg_readcount += 2;

    return c;
}

int MSG_ReadLong (void)
{
    int     c;

    if (msg_readcount+4 > net_message.cursize)
    {
        msg_badread = true;
        return -1;
    }

    c = net_message.data[msg_readcount]
    + (net_message.data[msg_readcount+1]<<8)
    + (net_message.data[msg_readcount+2]<<16)
    + (net_message.data[msg_readcount+3]<<24);

    msg_readcount += 4;

    return c;
}

float MSG_ReadFloat (void)
{
    union
    {
        byte    b[4];
        float   f;
        int     l;
    } dat;

    dat.b[0] =      net_message.data[msg_readcount];
    dat.b[1] =      net_message.data[msg_readcount+1];
    dat.b[2] =      net_message.data[msg_readcount+2];
    dat.b[3] =      net_message.data[msg_readcount+3];
    msg_readcount += 4;

    dat.l = LittleLong (dat.l);

    return dat.f;
}

char *MSG_ReadString (void)
{
    static char string[2048];
    int c;
    size_t l;

    l = 0;
    do
    {
        c = MSG_ReadChar ();
        if (c == -1 || c == 0)
            break;
        string[l] = c;
        l++;
    } while (l < sizeof(string)-1);

    string[l] = 0;

    return string;
}

float MSG_ReadCoord (void)
{
    if (cl.protocol_version == NEW_PROTOCOL_VERSION)
        return MSG_ReadLong() * (1.0 / 1024);
    else
        return MSG_ReadShort() * (1.0 / 8);
}

float MSG_ReadAngle (void)
{
    if (cl.protocol_version == NEW_PROTOCOL_VERSION)
        return MSG_ReadLong() * (1.0 / 1024);
    else
        return MSG_ReadChar() * (360.0/256);
}



//===========================================================================

void SZ_Alloc (sizebuf_t *buf, int startsize)
{
    if (startsize < 256)
        startsize = 256;
    buf->data = (byte *)Hunk_AllocName (startsize, "sizebuf");
    buf->maxsize = startsize;
    buf->cursize = 0;
}


void SZ_Free (sizebuf_t *buf)
{
//      Z_Free (buf->data);
//      buf->data = NULL;
//      buf->maxsize = 0;
    buf->cursize = 0;
}

void SZ_Clear (sizebuf_t *buf)
{
    buf->cursize = 0;
}

void *SZ_GetSpace (sizebuf_t *buf, int length)
{
    void    *data;

    if (buf->cursize + length > buf->maxsize)
    {
        if (!buf->allowoverflow)
            Sys_Error ("SZ_GetSpace: overflow without allowoverflow set");

        if (length > buf->maxsize)
            Sys_Error ("SZ_GetSpace: %i is > full buffer size", length);

        buf->overflowed = true;
        Con_Printf ("SZ_GetSpace: overflow");
        SZ_Clear (buf);
    }

    data = buf->data + buf->cursize;
    buf->cursize += length;

    return data;
}

void SZ_Write (sizebuf_t *buf, const void *data, int length)
{
    memcpy (SZ_GetSpace(buf,length),data,length);
}

void SZ_Print (sizebuf_t *buf, const std::string &data)
{
    size_t len = data.length() + 1;

    if (buf->data[buf->cursize-1])
        memcpy ((byte *)SZ_GetSpace(buf, len), data.c_str(), len); // no trailing 0
    else
        memcpy ((byte *)SZ_GetSpace(buf, len-1)-1, data.c_str(), len); // write over trailing 0
}


//============================================================================

/*
==============
COM_Parse

Parse a token out of a string
==============
*/
char *COM_Parse (char *data)
{
    int             c;
    int             len;

    len = 0;
    com_token[0] = 0;

    if (!data)
        return NULL;

// skip whitespace
skipwhite:
    while ( (c = *data) <= ' ')
    {
        if (c == 0)
            return NULL;                    // end of file;
        data++;
    }

// skip // comments
    if (c=='/' && data[1] == '/')
    {
        while (*data && *data != '\n')
            data++;
        goto skipwhite;
    }


// handle quoted strings specially
    if (c == '\"')
    {
        data++;
        while (1)
        {
            c = *data++;
            if (c=='\"' || !c)
            {
                com_token[len] = 0;
                return data;
            }
            com_token[len] = c;
            len++;
        }
    }

// parse single characters
    if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
    {
        com_token[len] = c;
        len++;
        com_token[len] = 0;
        return data+1;
    }

// parse a regular word
    do
    {
        com_token[len] = c;
        data++;
        len++;
        c = *data;
    if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
            break;
    } while (c>32);

    com_token[len] = 0;
    return data;
}


void COM_Path_f (void);


/*
================
COM_Init
================
*/
void COM_Init (char *basedir)
{
    byte    swaptest[2] = {1,0};

// set the byte swapping variables in a portable manner
    if ( *(short *)swaptest == 1)
    {
        bigendien = false;
        BigShort = ShortSwap;
        LittleShort = ShortNoSwap;
        BigLong = LongSwap;
        LittleLong = LongNoSwap;
        BigFloat = FloatSwap;
        LittleFloat = FloatNoSwap;
    }
    else
    {
        bigendien = true;
        BigShort = ShortNoSwap;
        LittleShort = ShortSwap;
        BigLong = LongNoSwap;
        LittleLong = LongSwap;
        BigFloat = FloatNoSwap;
        LittleFloat = FloatSwap;
    }

    Cvar_RegisterVariable("registered", "1");
}


/*
============
va

does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char    *va(char *format, ...)
{
    va_list         argptr;
    static char             string[1024];

    va_start (argptr, format);
    vsprintf (string, format,argptr);
    va_end (argptr);

    return string;
}


