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

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

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

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

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

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

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

#include "../../../src/juce_core/basics/juce_StandardHeader.h"
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include <Carbon/Carbon.h>

BEGIN_JUCE_NAMESPACE


#include "../../../src/juce_core/text/juce_String.h"
#include "../../../src/juce_core/basics/juce_Time.h"
#include "../../../src/juce_core/basics/juce_SystemStats.h"
#include "../../../src/juce_core/containers/juce_MemoryBlock.h"


//==============================================================================
bool juce_isOnLine()
{
    return true;
}

class JUCE_URLStream
{
    URLReference urlRef;
    MemoryBlock reservoir;

public:
    bool refOk, streamOk;
    int position;

    JUCE_URLStream (const String& url,
                    const String& post,
                    const bool isPost)
    {
        position = 0;
        refOk = URLNewReference ((const char*)url, &urlRef) == noErr;
        streamOk = refOk;

        if (refOk)
        {
            if (isPost)
            {
                streamOk = URLSetProperty (urlRef, kURLHTTPRequestMethod, (void*) "POST", 4) == noErr
                            && URLSetProperty (urlRef, kURLHTTPRequestBody, (void*)(const char*)post, post.length()) == noErr;
            }

            if (streamOk)
                streamOk = URLOpen (urlRef, 0, 0, 0, 0, (void*)this) == noErr;
        }
    }

    ~JUCE_URLStream()
    {
        if (refOk)
            URLDisposeReference (urlRef);
    }

    int readNextBlock (void* buffer, int size)
    {
        while (reservoir.getSize() < size)
        {
            if (! fetchNextBlock())
                break;
        }

        size = jmin (jmax (0, size), reservoir.getSize());

        reservoir.copyTo (buffer, 0, size);
        reservoir.removeSection (0, size);

        position += size;
        return size;
    }

private:
    bool fetchNextBlock()
    {
        const uint32 timeOut = Time::getMillisecondCounter() + 10000;

        do
        {
            URLIdle();

            void* buffer = 0;
            Size size = 0;

            if (URLGetBuffer (urlRef, &buffer, &size) != noErr)
                break;

            if (size > 0)
                reservoir.append (buffer, size);

            if (buffer != 0)
                URLReleaseBuffer (urlRef, buffer);

            if (size > 0)
                return true;

            URLState state;
            if (URLGetCurrentState (urlRef, &state) != noErr)
                break;

            if (state == kURLTransactionCompleteState
                 || state == kURLAbortingState
                 || state ==  kURLCompletedState
                 || state == kURLErrorOccurredState)
                break;

        }
        while (Time::getMillisecondCounter() < timeOut);

        return false;
    }
};

//==============================================================================
void* juce_openInternetFile (const String& url,
                             const String& optionalPostText,
                             const bool isPost)
{
    JUCE_URLStream* s = new JUCE_URLStream (url, optionalPostText, isPost);

    if (! s->streamOk)
    {
        delete s;
        s = 0;
    }

    return s;
}

void juce_closeInternetFile (void* handle)
{
    JUCE_URLStream* const s = (JUCE_URLStream*) handle;

    if (s != 0)
    {
        delete s;
    }
}

int juce_getStatusCodeFor (void* handle)
{
    return 0;
}

int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead)
{
    JUCE_URLStream* const s = (JUCE_URLStream*) handle;

    if (s != 0)
        return s->readNextBlock (buffer, bytesToRead);

    return 0;
}

int juce_seekInInternetFile (void* handle, int newPosition)
{
    JUCE_URLStream* const s = (JUCE_URLStream*) handle;

    if (s != 0)
        return s->position;

    return 0;
}

//==============================================================================
static bool GetEthernetIterator (io_iterator_t* matchingServices)
{
    mach_port_t masterPort;

    if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS)
    {
        CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass);

        if (dict != 0)
        {
            CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault,
                                                                         0,
                                                                         &kCFTypeDictionaryKeyCallBacks,
                                                                         &kCFTypeDictionaryValueCallBacks);

            if (propDict != 0)
            {
                CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue);

                CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict);
                CFRelease (propDict);
            }
        }

        return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS;
    }

    return false;
}

int SystemStats::getMACAddresses (int64* addresses, int maxNum)
{
    int numResults = 0;
    io_iterator_t it;

    if (GetEthernetIterator (&it))
    {
        io_object_t i;

        while ((i = IOIteratorNext (it)) != 0)
        {
            io_object_t controller;

            if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS)
            {
                CFTypeRef data = IORegistryEntryCreateCFProperty (controller,
                                                                  CFSTR (kIOMACAddress),
                                                                  kCFAllocatorDefault,
                                                                  0);
                if (data != 0)
                {
                    UInt8 addr [kIOEthernetAddressSize];
                    zeromem (addr, sizeof (addr));

                    CFDataGetBytes ((CFDataRef)data, CFRangeMake (0, sizeof (addr)), addr);
                    CFRelease (data);

                    int64 a = 0;
                    for (int i = 6; --i >= 0;)
                        a = (a << 8) | addr[i];

                    if (numResults < maxNum)
                    {
                        *addresses++ = a;
                        ++numResults;
                    }
                }

                IOObjectRelease (controller);
            }

            IOObjectRelease (i);
        }

        IOObjectRelease (it);
    }

    return numResults;
}

END_JUCE_NAMESPACE
