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

 This file is part of the JUCETICE project - Copyright 2009 by Lucio Asnaghi.

 JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions"
 Copyright 2007 by Julian Storer.

 ------------------------------------------------------------------------------

 JUCE and JUCETICE can be redistributed and/or modified 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.

 JUCE and JUCETICE are distributed in the hope that they 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 JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to
 Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 Boston, MA 02111-1307 USA

 ==============================================================================

 This file is based around Niall Moody's OSC/UDP library, but was modified to
 be more independent and modularized, using the best practices from JUCE.

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

#ifdef WIN32
  #include <winsock.h>
#else
  #include <netinet/in.h>
#endif

BEGIN_JUCE_NAMESPACE

//------------------------------------------------------------------------------
OpenSoundMessage::OpenSoundMessage()
  : OpenSoundBase(),
    bufferSize(0),
    outgoingSize(0),
    buffer(0)
{
    typeTag = ",";
}

//------------------------------------------------------------------------------
OpenSoundMessage::OpenSoundMessage(char *data, const int size)
  : OpenSoundBase(),
    bufferSize(0),
    outgoingSize(0),
    buffer(0)
{
    int i;
    GetTheBytes tempBytes;
    int32 tempint;
    float *tempf;
    String tempstr;
    //This represents which part of the Message we're currently reading from.
    int currentSection = 0;
    int currentTag = 1; //Because Type Tags start with ','.

    for(i=0;i<size;++i)
    {
        //Address.
        if(currentSection == 0)
        {
            if((data[i] != 0) && (data[i] != ','))
                address += data[i];
            else if(data[i] == ',')
            {
                typeTag = ",";
                ++currentSection;
            }
        }
        //Type Tag.
        else if(currentSection == 1)
        {
            if(data[i] != 0)
                typeTag += data[i];
            else if((i%4) == 3) //i.e. we're on the last byte of a 4 byte boundary.
                ++currentSection;
        }
        //The actual data.
        else
        {
            if(currentTag >= static_cast<int>(typeTag.length()))
                break;
            else if(typeTag[currentTag] == 'f')
            {
                tempBytes.a = data[i];
                ++i;
                tempBytes.b = data[i];
                ++i;
                tempBytes.c = data[i];
                ++i;
                tempBytes.d = data[i];

                tempint = ntohl(*(reinterpret_cast<int32 *>(&tempBytes)));
                tempf = reinterpret_cast<float *>(&tempint);
                floatArray.add (*tempf);

                ++currentTag;
            }
            else if(typeTag[currentTag] == 'i')
            {
                tempBytes.a = data[i];
                ++i;
                tempBytes.b = data[i];
                ++i;
                tempBytes.c = data[i];
                ++i;
                tempBytes.d = data[i];

                tempint = ntohl(*(reinterpret_cast<int32 *>(&tempBytes)));
                intArray.add (tempint);

                ++currentTag;
            }
            else if(typeTag[currentTag] == 's')
            {
                tempstr = "";
                while(data[i] != 0)
                {
                    tempstr += data[i];

                    ++i;
                }

				//@TODO
				//Not sure with this, it was
				//tempstr[tempstr.length()] = static_cast<char>(NULL); //Terminator.

				tempstr = tempstr.replaceCharacter (tempstr.getLastCharacter(),
													static_cast<char>(NULL)); //Terminator.

                stringArray.add (tempstr);

                //Handle any padding bytes.
                while((i%4) != 0)
                    ++i;

                ++currentTag;
            }
        }
    }
}

//------------------------------------------------------------------------------
OpenSoundMessage::~OpenSoundMessage()
{
    if(buffer)
    {
        delete [] buffer;
        buffer = 0;
    }
}

//------------------------------------------------------------------------------
int32 OpenSoundMessage::getSize()
{
    int32 i;
    int32 tempint;

    outgoingSize = 0;
    if((address.length()+1)%4)
    {
        //Because we need space for the padding bytes.
        tempint = (4 - ((address.length()+1)%4));
        outgoingSize += address.length() + 1;
        outgoingSize += tempint;
    }
    else
        outgoingSize += address.length() + 1; //+1 = Null terminator.
    if((typeTag.length()+1)%4)
    {
        //Because we need space for the padding bytes.
        tempint = (4 - ((typeTag.length()+1)%4));
        outgoingSize += typeTag.length() + 1;
        outgoingSize += tempint;
    }
    else
        outgoingSize += typeTag.length() + 1; //+1 = Null terminator.

    outgoingSize += intArray.size() * sizeof(int32);
    outgoingSize += floatArray.size() * sizeof(float);

    for(i=0;i<static_cast<int32>(stringArray.size());++i)
    {
        if((stringArray[i].length()+1)%4)
        {
            //Because we need space for the padding bytes.
            tempint = (4 - ((stringArray[i].length()+1)%4));
            outgoingSize += stringArray[i].length() + 1;
            outgoingSize += tempint;
        }
        else
            outgoingSize += stringArray[i].length() + 1; //+1 = Null terminator.
    }

    if(outgoingSize > bufferSize)
    {
        bufferSize = outgoingSize;
        if(buffer)
        {
            delete [] buffer;
            buffer = 0;
        }

        buffer = new char[bufferSize];
    }

    return outgoingSize;
}

//------------------------------------------------------------------------------
char *OpenSoundMessage::getData()
{
    int32 addressCount = 0;
    int32 typeTagCount = 0;
    int32 intCount = 0;
    int32 floatCount = 0;
    int32 stringCount = 0;
    int32 currentTypeTag = 1;
    int32 i, j;
    GetTheBytes *tempBytes;
    int32 tempInt;
    float tempFloat;
    int32 addressLength = static_cast<int32>(address.length());
    int32 typeTagLength = static_cast<int32>(typeTag.length());
    int32 stringLength;

    if(!buffer)
        return 0;

    for(i=0;i<outgoingSize;++i)
    {
        //If we're currently writing the address to the buffer.
        if(addressCount < (addressLength + 1))
        {
            buffer[i] = address[addressCount];
            ++addressCount;

            if(addressCount == addressLength)
            {
                ++i;
                buffer[i] = static_cast<char>(NULL);
                ++addressCount;
                //Add any packing bytes.
                while(addressCount%4)
                {
                    ++i;
                    buffer[i] = static_cast<char>(NULL);
                    ++addressCount;
                }
            }
        }
        //If we're currently writing the type tag to the buffer.
        else if(typeTagCount < (typeTagLength + 1))
        {
            buffer[i] = typeTag[typeTagCount];
            ++typeTagCount;

            if(typeTagCount == typeTagLength)
            {
                ++i;
                buffer[i] = static_cast<char>(NULL);
                ++typeTagCount;
                //Add any packing bytes.
                while(typeTagCount%4)
                {
                    ++i;
                    buffer[i] = static_cast<char>(NULL);
                    ++typeTagCount;
                }
            }
        }
        //If we're currently writing part of the data to the buffer.
        else if(currentTypeTag < typeTagLength)
        {
            //Determine what kind of data we should be writing to the buffer.
            switch(typeTag[currentTypeTag])
            {
                //If we're writing an integer to the buffer.
                case 'i':
                    tempInt = htonl(intArray[intCount]);
                    tempBytes = reinterpret_cast<GetTheBytes *>(&tempInt);
                    buffer[i] = tempBytes->a;
                    ++i;
                    buffer[i] = tempBytes->b;
                    ++i;
                    buffer[i] = tempBytes->c;
                    ++i;
                    buffer[i] = tempBytes->d;

                    ++intCount;
                    break;
                //If we're writing a float to the buffer.
                case 'f': {
                    tempFloat = floatArray[floatCount];
                    int32* const _i((int32*)&tempFloat);
                    tempInt = htonl(*_i);
                    tempBytes = reinterpret_cast<GetTheBytes *>(&tempInt);
                    buffer[i] = tempBytes->a;
                    ++i;
                    buffer[i] = tempBytes->b;
                    ++i;
                    buffer[i] = tempBytes->c;
                    ++i;
                    buffer[i] = tempBytes->d;

                    ++floatCount;
                    break;
                }
                //If we're writing an OSC String to the buffer.
                case 's':
                    stringLength = static_cast<int32>(stringArray[stringCount].length());
                    for(j=1;j<stringLength;++j)
                    {
                        buffer[i] = stringArray[stringCount][j-1];
                        ++i;
                    }
                    buffer[i] = static_cast<char>(NULL);

                    //Add any packing bytes.
                    while(j%4)
                    {
                        ++i;
                        buffer[i] = static_cast<char>(NULL);
                        ++j;
                    }

                    ++stringCount;
                    break;
            }
            //Move on to the next tag in the message's type tag list.
            ++currentTypeTag;
        }
    }

    i = i;

    return buffer;
}

//------------------------------------------------------------------------------
void OpenSoundMessage::clearMessage()
{
    outgoingSize = 0;

    typeTag = ",";
    intArray.clear();
    floatArray.clear();
    stringArray.clear();
}

//------------------------------------------------------------------------------
void OpenSoundMessage::setAddress(const String& val)
{
    address = val;
}

//------------------------------------------------------------------------------
void OpenSoundMessage::addInt32(const int32 val)
{
    typeTag += 'i';
    intArray.add (val);
}

//------------------------------------------------------------------------------
void OpenSoundMessage::addFloat32(const float val)
{
    typeTag += 'f';
    floatArray.add (val);
}

//------------------------------------------------------------------------------
void OpenSoundMessage::addOscString(const String& val)
{
    typeTag += 's';
    stringArray.add (val);
}

//------------------------------------------------------------------------------
bool OpenSoundMessage::isMessage(char *data, const int size)
{
    int i;
    bool retval = false;

    //All OSC Messages start with the OSC address, which always starts with '/'.
    if(data[0] == '/')
    {
        for(i=1;i<size;++i)
        {
            //The comma marks the start of the Type Tag section of the message.
            //It'll always come at the start of a 4-byte boundary.
            if((data[i] == ',')&&((i%4) == 0))
            {
                retval = true;
                break;
            }
        }
    }

    return retval;
}

END_JUCE_NAMESPACE
