/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    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 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#pragma once

#include "SC_Endian.h"
#include "SC_Types.h"
#include <stdexcept>
#include <cstring>
#include <string>

template <int MaxPacketSize = 8192> struct scpacket {
    static const int kBufSize = MaxPacketSize / sizeof(int32); // round down
    int32 *wrpos, *endpos, *msgsizepos;
    char* tagwrpos;
    int inbundle;
    int32 buf[kBufSize];

    void throw_overflow_exception() { throw std::runtime_error(std::string("buffer overflow")); }

    scpacket() { reset(); }
    void reset() {
        wrpos = buf;
        endpos = buf + kBufSize;
        inbundle = 0;
    }
    void addi(int i) {
        if (wrpos >= endpos)
            throw_overflow_exception();
        *wrpos++ = sc_htonl(i);
    }
    void addii(int64 ii) {
        int i;
        i = (int)(ii >> 32);
        addi(i);
        i = (int)ii;
        addi(i);
    }
    void addf(float f) {
        if (wrpos >= endpos)
            throw_overflow_exception();
        elem32 slot;
        slot.f = f;
        *wrpos++ = sc_htonl(slot.i);
    }
    void addd(double f) {
        if (wrpos >= endpos)
            throw_overflow_exception();
        elem64 slot;
        slot.f = f;
        *wrpos++ = sc_htonl(slot.i >> 32);
        *wrpos++ = sc_htonl(slot.i & 0x00000000FFFFFFFF);
    }
    void adds(const char* src) {
        size_t len = strlen(src);
        size_t len4 = (len + 4) >> 2;
        if (wrpos + len4 > endpos)
            throw_overflow_exception();
        wrpos[len4 - 1] = 0;
        memcpy(wrpos, src, len);
        wrpos += len4;
    }
    void adds_slpre(const char* src) // prepends a slash
    {
        size_t len = strlen(src);
        size_t len4 = (len + 5) >> 2;
        if (wrpos + len4 > endpos)
            throw_overflow_exception();
        wrpos[len4 - 1] = 0;
        char* wrpos_c = (char*)wrpos;
        *wrpos_c = '/';
        memcpy(wrpos_c + 1, src, len);
        wrpos += len4;
    }
    void adds(const char* src, size_t len) {
        size_t len4 = (len + 4) >> 2;
        if (wrpos + len4 > endpos)
            throw_overflow_exception();
        wrpos[len4 - 1] = 0;
        memcpy(wrpos, src, len);
        wrpos += len4;
    }
    void addb(uint8* src, size_t len) {
        size_t len4 = (len + 3) >> 2;
        if (wrpos + (len4 + 1) > endpos)
            throw_overflow_exception();
        wrpos[len4 - 1] = 0;
        int32 swaplen = len;
        *wrpos++ = sc_htonl(swaplen);
        memcpy(wrpos, src, len);
        wrpos += len4;
    }
    void addtag(char c) { *tagwrpos++ = c; }
    void skip(int n) {
        if (wrpos + n > endpos)
            throw_overflow_exception();
        wrpos += n;
    }
    void maketags(int n) {
        int size4 = (n + 4) >> 2;
        tagwrpos = (char*)wrpos;
        skip(size4);
        wrpos[-1] = 0;
    }
    size_t size() { return (char*)wrpos - (char*)buf; }
    char* data() { return (char*)buf; }

    void OpenBundle(int64 time) {
        inbundle++;
        adds("#bundle");
        addii(time);
    }
    void CloseBundle() {
        if (inbundle)
            inbundle--;
    }

    void BeginMsg() {
        if (inbundle) {
            msgsizepos = wrpos;
            addi(0);
        }
    }
    void EndMsg() {
        if (inbundle) {
            *msgsizepos = sc_htonl(((wrpos - msgsizepos) - 1) * sizeof(int32));
        }
    }
};

typedef scpacket<> small_scpacket;
typedef scpacket<65516> big_scpacket;
