//
// IMediaD is an abstract superclass.  It supports instance variables and
// methods for media classes such as ITiffD, ISndD, IEpsD.  The embedded
// graphic objects in a rich Text object references a media object.  It is the
// media object that provides the image that is displayed by the embedded
// graphic object and performs the double click action.  Multiple embedded
// graphic objects may reference the same media object.  In the default
// read/write methods provided by IMediaD the stream, tar stream and 
// pasteboard representations are identical.  the tar stream representation
// was orginally meant to support a special compressed representation.  This
// however is at present only done for text data.  Tar stream representation
// is used to post data to the NNTP server.
//
// A Media Class must implement these class methods:
//
//     + (NXAtom)rtfControlWord - returns the RTF control word eg "MMtiff"
//     + (NXAtom)fileExtension  - returns the file extension eg "tiff"
//     + (NXAtom)pasteboardType - returns the pasteboard type
//                                     eg NXTIFFPboardtype
//
// A Media Class must implement these instance methods:
//     - initWithKey:(char *)pathName
//     - (BOOL)readFromFile:(const char *)pathName
//            This method maps the file into a stream which the receiver will
//            own.  Then [self readFromStream:] is called.
//     - (BOOL)readFromPasteboard:(Pasteboard *)pasteboard
//            This method maps the pasteboard data into a stream which the
//            receiver will own.  Then [self readFromStream:] is called.
//     - (NXImage *)image
//            This method returns the image to be displayed in the document.
//            This method must be defined in the media subclass.
//     - setReferenceCount:(int)newCount
//     - (int)referenceCount
//            The number of embedded objects in the document that references
//            this media object.
//     - (int)incrementReferenceCount
//     - (int)decrementReferenceCount
//     - (BOOL)readFromStream:(NXStream *)inStream
//            This method first copies the stream if the receiver does not own
//            the stream.  Then the stream is read.  The implementation in
//            IMediaD only does the copying of the stream the actual reading
//            must be defined in the media subclass.
//     - (BOOL)readFromTarStream:(NXStream *)tarStream
//            This is implemented as [self readFromStream:].  Override this
//            implementation if the media class has a different representation
//            for the tar format.  For example, the ITextD class uses a
//            special compressed format when it writes to a tar stream.
//     - (void)writeToTarStream:(NXStream *)tarStream
//            This is implemented as [self writeFromStream:].  Override this
//            implementation if the media class has a different representation
//            for the tar format.
//     - (void)writeToStream:(NXStream *)outStream
//            This method copies the stream owned by the receiver to outStream.
//            (A more efficient implementation should just assign the stream
//            pointer.)
//     - free
//     - performDoubleClickAction:sender
//            This method does nothing in IMediaD's implementation.
//
// IMediaD provides default methods for most of the above.  Only these
// instance methods must be implemented by the media subclass
//     - (BOOL)readFromStream:(NXStream *)inStream
//		The subclass implementation of this method should super
//              the IMediaD implementation first.  This will copy inStream if
//              the receiver does not own inStream;  IMediaD uses this method
//              is to read tar streams and pasteboard data, also.  You must 
//              implement your own methods if you have special tar stream
//              or pasteboard representations. 
//     - (NXImage *)image
//     - performDoubleClickAction:sender
//              This method need not be implemented if there is no double click
//              action.
//
// Please make an entry in Tables.c for each new media class.

#import "IMediaD.h"
#import <appkit/Application.h>
#import <appkit/Listener.h>
#import <appkit/Speaker.h>
#import <objc/hashtable.h>
#import <sys/param.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <mach.h>
#import <libc.h>

@implementation IMediaD

+ getFileIconFor:(Class)Media
{
    char path[256];
    int fd;
    char *data;
    int length;
    int flag;
    NXStream *iconStream;
    NXImage *icon;

    sprintf(path, "/tmp/x.%.248s", [(id)Media fileExtension]);
    if ((fd = open(path, O_WRONLY | O_CREAT)) != -1) {
        close(fd);
    }
    [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
    [[NXApp appSpeaker] getFileIconFor:path TIFF:&data TIFFLength:&length
        ok:&flag];
    unlink(path);
    if (flag == YES) {
        iconStream = NXOpenMemory(data, length, NX_READONLY);
        icon = [[NXImage alloc] initFromStream:iconStream];
        NXCloseMemory(iconStream, NX_FREEBUFFER);
    } else {
        // ## REWRITE! ## should provide a default icon
        icon = nil;
    }
    [(id)Media setIcon:icon];
    return(self);
}

- initWithKey:(char *)pathName
{
    const char *fileName;

    [super init];
    if ((fileName = rindex(pathName, '/')) == 0) {
        fileName = pathName;
    } else {
        ++fileName;
    }
    key = NXCopyStringBufferFromZone(fileName, [self zone]);
    stream = NULL;
    count = 0;
    return self;
}

- (BOOL)readFromFile:(const char *)pathName
{
    if ((stream = NXMapFile(pathName, NX_READONLY)) != NULL) {
        if ([self readFromStream:stream] == YES) {
            return(YES);
        } else {
            NXCloseMemory(stream, NX_FREEBUFFER);
            stream = NULL;
            return(NO);
        }
    } else {
        return(NO);
    }
}

- (BOOL)readFromPasteboard:(Pasteboard *)pasteboard
{
    char *data;
    int length;

    if ([pasteboard readType:[[self class] pasteboardType] data:&data
        length:&length] != nil) {
        if ((stream = NXOpenMemory(data, length, NX_READONLY)) != NULL) {
            if ([self readFromStream:stream] == YES) {
                return(YES);
            } else {
                NXCloseMemory(stream, NX_FREEBUFFER);
                stream = NULL;
                return(NO);
            }
        } else {
            return(NO);
        }
    } else {
        return(NO);
    }
}

- (NXImage *)image
{
    /* defined in subclasses */
    return nil;
}

- setReferenceCount:(int)newCount
{
    count = newCount;
    return self;
}

- (int)referenceCount
{
    return count;
}

- (int)incrementReferenceCount
{
    return(++count);
}

- (int)decrementReferenceCount
{
    return(--count);
}

- (int)size
{
    return size;
}

- (BOOL)readFromTarStream:(NXStream *)tarStream
{
    return([self readFromStream:tarStream]);
}

- (void)writeToTarStream:(NXStream *)tarStream
{
    [self writeToStream:tarStream];
}


- (BOOL)readFromStream:(NXStream *)inStream
{
    char *data;
    int len, maxLen;

    NXSeek(inStream, (long)0, NX_FROMEND);
    size = NXTell(inStream);
    NXSeek(inStream, (long)0, NX_FROMSTART);
    // Do we own this stream?
    if (inStream != stream) {
        // No.  Make a copy that we will own.
        if (stream != NULL) {
            NXCloseMemory(stream, NX_FREEBUFFER);
        }
        NXGetMemoryBuffer(inStream, &data, &len, &maxLen);
        stream = NXOpenMemory(NULL, 0, NX_READWRITE);
        NXWrite(stream, data, size);
        NXSeek(stream, (long)0, NX_FROMSTART);
    }   
    return(YES);
    /* actual read is defined in subclasses */
}

- (void)writeToStream:(NXStream *)outStream
{
    char *data;
    int len, maxLen;

    // ## REWRITE! ## really should just assign NXStream pointers instead
    // Just copy the in stream which we should own
    if (stream != NULL) {
        NXGetMemoryBuffer(stream, &data, &len, &maxLen);
        NXWrite(outStream, data, size);
    }
}

- free
{
    int len, maxlen;
    char *buffer;

    /* may be redefined in subclasses */
    if (stream != NULL) {
        NXGetMemoryBuffer(stream, &buffer, &len, &maxlen);
        NXCloseMemory(stream, NX_FREEBUFFER);
//      vm_deallocate(task_self(), buffer, maxlen);
    }
    return([super free]);
}

- performDoubleClickAction:sender
{
    /* may be redefined in subclasses */
    // images should return nil; media objects that are not just images
    // i.e, icons should return self.
    return(nil);
}
@end

