//
#import <math.h>
#include <sys/stat.h>

#import "Background.h"

#import <dpsclient/wraps.h>
#import <appkit/Application.h>
#import <objc/hashtable.h>
#import <defaults/defaults.h>
#import "PSsplat.h"
#import <appkit/OpenPanel.h>
#import <appkit/Control.h>
#import <appkit/Menu.h>
#import <appkit/MenuCell.h>
#import <appkit/Matrix.h>
#import <appkit/nextstd.h>

#define PSsplat( r, p)	_PSsplat( (r)->origin.x, (r)->origin.y, \
		(r)->size.width, (r)->size.height, (p)->x, (p)->y)

//
@implementation Background:View
//
//
 void handler(DPSTimedEntry teNumber, double now, id background)
{
    [background next:nil];
}

- initFrame:(const NXRect *)frameRect
{
    char *d,*f;
    int len;
    
    [super initFrame:frameRect];
    imageNames = NULL;
    imageTimes = NULL;
    images = NULL;
    imageCount = 0;
    max = 0;
    current = 0;
    d = getenv("HOME"); if (!d) d="";
    f = ".background";
    len = strlen(d) + 1 + strlen(f) + 1;
    defaultfile = NX_ZONEMALLOC([self zone], defaultfile, char, len);
    strcpy(defaultfile, d);
    strcat(defaultfile,"/");
    strcat(defaultfile,f);
    timer = NULL;
    repeat = 600;
    permanent = NO;
    updateMenu = YES;
    return self;
}



- reset
{
    int i;
  
    for (i=0; i<imageCount; i++) {
	NXZoneFree([self zone], imageNames[i]);
	if(images[i]) [images[i] free];
    }
    if (imageCount) {
        NXZoneFree([self zone], imageNames);
        NXZoneFree([self zone], imageTimes);
        NXZoneFree([self zone], images);
    }
    imageCount=0;
    current=0;
    return self;
}

- free
{
    [self reset];
    if (timer) DPSRemoveTimedEntry(timer);
    return [super free];
}

//
- loadImageNamed:(const char *)aName
{
 //    printf("Loading %s.\n", aName);
    if ([self getImage:aName]<0) return nil;
    return self;
}

- setImageNamed:(const char *)aName
{
    int i;
    
    i = [self getImage:aName];
    if (i>=0) [self setImage:i]; else return nil;
    return self;
}

- setRepeatValue:(float)r
{
    char str[7];
    
    repeat = r;
    if (timer) DPSRemoveTimedEntry(timer);
    if (repeat<1) {
        strcpy(str,"off");
	timer=NULL;
    } else {
        if (r<60) sprintf(str,"%2ds",(int)r);
	else if (r<3600) sprintf(str,"%2dmin",(int)(r/60));
	else sprintf(str,"%2dh",(int)(r/3600));
	timer = DPSAddTimedEntry(repeat, 
	    (DPSTimedEntryProc)handler, self, NX_BASETHRESHOLD);
    }
    [repeatText setStringValue:str];
    [repeatSlider setFloatValue:log(r+1)/12];
 //    printf("Setting repeat to %s.\n", str);
    [self writeDefault];
    return self;
}

- setRepeatText:aText
{
    repeatText = aText;
    return self;
}

- setRepeatSlider:aSlider
{
    repeatSlider = aSlider;
    return self;
}

- setPermanent:(BOOL)flag
{
    permanent = flag;
    return self;
}
//
//	target/action
//
- open:sender
{
    OpenPanel *op=[OpenPanel new];
    char *types[]={ "tiff", "eps", NULL};
    char *directory, *filename, *fn;
    const char *d;
    const char * const *files;
    int l, len, ind;
    
    if( !imageCount)
    {
	directory=getenv( "HOME");
	if( !directory)	directory="/";
	filename=NXCopyStringBufferFromZone("", [self zone]);
    } else {
        fn=NXCopyStringBufferFromZone(imageNames[current], [self zone]);
	if (filename=rindex(fn, '/')) *filename++='\0', directory=fn;
	else
	{
	    filename=imageNames[current];
	    directory=getenv( "HOME");
	    if( !directory) directory="/";
	}
    }
    ind = current;
    [op allowMultipleFiles:YES];
    if( [op runModalForDirectory:directory file:filename types:types]) {
        len = 0;
	for (files=[op filenames]; *files; files++) if ((l=strlen(*files))>len) len=l;
	len += strlen(d=[op directory])+2;
	NX_ZONEMALLOC([self zone], fn, char, len);
	strcpy(fn,d);
	strcat(fn,"/");
	filename=rindex(fn, '/')+1;
	for (files=[op filenames]; *files; files++) {
	    strcpy(filename,*files);
	    if ((l=[self getImage:fn])>0) ind = l;
	}
    }
    [self setImage:ind];
    [self writeDefault];
    return self;
}

- next:sender
{
    if (++current>=imageCount) current=0;
    [self setImage:current];
    return self;
}

- resize:sender
{
    char *p;

    p = imageNames[current];
    if (p[0]=='*') {
	NX_ZONEMALLOC([self zone], imageNames[current], char, strlen(p));
	strcpy(imageNames[current], p+1);
	NXZoneFree([self zone], p);
	[images[current] free];
	images[current]=nil;
    } else {
	NX_ZONEMALLOC([self zone], imageNames[current], char, strlen(p)+2);
	strcpy(imageNames[current],"*");
	strcat(imageNames[current],p);
	NXZoneFree([self zone], p);
    }
    [self resizeImage:current];
    [self setImage:current];
    [self writeDefault];
    return self;
}

- previous:sender
{
    if (--current<0) current=imageCount-1;
    [self setImage:current];
    return self;
}

- redraw:sender
{
    [self display];
    return self;
}
- select:sender
{
    sender = [sender selectedCell];
    updateMenu=NO;
    [self setImageNamed:[sender title]];
    updateMenu=YES;
    return self;
}
- toFront:sender
{
    PSsetwindowlevel( NX_DOCKLEVEL+1, [window windowNum]);
    [window orderFront:sender];
    [window addToEventMask:NX_MOUSEDOWNMASK];
    return self;
}
- toTop:sender
{
    int i;
    char *n;
    time_t t;
    id img;
    
    n = imageNames[current];
    t = imageTimes[current];
    img = images[current];
    for (i=current; i>0; i--) {
        imageNames[i] = imageNames[i-1];
        imageTimes[i] = imageTimes[i-1];
        images[i] = images[i-1];
    }
    imageNames[0] = n;
    imageTimes[0] = t;
    images[0] = img;
    current = 0;
    [self setImage:current];
    [self writeDefault];
    return self;
}

- delete:sender
{
    int i;
    
    for (i=current; i<imageCount; i++) {
        imageNames[i] = imageNames[i+1];
        imageTimes[i] = imageTimes[i+1];
        images[i] = images[i+1];
    }
    imageCount--;
    if (current>=imageCount && current>0) current--;
    [self setImage:current];
    [self writeDefault];
    return self;
}

- deleteAll:sender
{
    [self reset];
    [self setImage:current];
    return self;
}

- takeRepeatValueFrom:sender
{
    [self setRepeatValue:exp(12*[sender floatValue])-1];
    return self;
}


///
///	Menu generation
/// 
- setMenu:aMenuCell
{
    menu = [aMenuCell target];
    [self updateMenu];
    return self;
}

- updateMenu
{
    id cell;
    id matrix;
    int i;
    
    if (!updateMenu) return self;
    matrix = [menu itemList];
    while([matrix cellCount]) [matrix removeRowAt:0 andFree:YES];
    [menu setItemList:matrix];
    for (i=0; i<imageCount; i++) {
	cell = [menu addItem:imageNames[i] action:@selector(select:) keyEquivalent:0];
	[cell setTarget:self];
    }
    [menu display];
    return self;
}
//
//	private:
//
- (int)getImage:(const char *)aName
{
    int i;
    
    if (aName==NULL) return -1;
    for (i=0; i<imageCount; i++) if (strcmp(aName, imageNames[i])==0) break;
    if (i>=imageCount) {
	if (++imageCount>max) {
	    if (!max) {
		imageNames = NX_ZONEMALLOC([self zone], imageNames, char *, 1);
		imageTimes = NX_ZONEMALLOC([self zone], imageTimes, time_t, 1);
		images = NX_ZONEMALLOC([self zone], images, NXImage *, 1);
		max = 1;
	    } else {
		max *= 2;
		imageNames = NX_ZONEREALLOC([self zone], imageNames, char *, max);
		imageTimes = NX_ZONEREALLOC([self zone], imageTimes, time_t, max);
		images = NX_ZONEREALLOC([self zone], images, NXImage *, max);
	    }
	}
	imageNames[i]=NXCopyStringBufferFromZone(aName, [self zone]);
	images[i] = nil;
    }
    return i;
}

- setImage:(int)i
{
    struct stat sb;
    char *p;
    int flag;
    
    if (i>=0 && i<imageCount) {
	if (imageNames[i][0]=='*') p=imageNames[i]+1; else p=imageNames[i];
	flag = stat(p, &sb);
	if (!images[i] || (flag==0 && imageTimes[i]<sb.st_mtime)) {
	    images[i]=[[NXImage allocFromZone:[self zone]] initFromFile:p];
	    imageTimes[i]=sb.st_mtime;
	    if (imageNames[i][0]=='*') [self resizeImage:i];
	}
	current = i;
    }
    [self updateMenu];
    [self display];
    return self;
}

- resizeImage:(int)i
{
    const NXScreen *screen;
    NXSize size;

    if (i>=0 && i<imageCount && images[i]) {
	screen = [window screen];
	size = screen->screenBounds.size;
	[images[i] setScalable:YES];
	[images[i] setSize:&size];
    }
    return self;
}
 
//
//	read and write defaults
//
- readDefault
{
    char p[256], f[256];
    NXStream *s;
		FILE *temp;
    int ret;
   
 //    pictures==NXReadDefault( "Background", "Image");
    if (s=NXMapFile(defaultfile, NX_READONLY)) {
	while (NXScanf(s, " %255[^ 	\n=]%1[ 	\n]", p, f)==2) [self loadImageNamed:p];
	ret = 1;
	while (ret && (ret=NXScanf(s," = \"%255[^\n\"]\" ", f))!=EOF) {
 //	    printf("Default read: %s = %s.\n", p, f);
	    if (strncmp(p, "rep", 3)==0) [self setRepeatValue:atof(f)];
	    else printf("Illegal default in %s: %s=%s.\n", defaultfile, p, f);
	    ret=NXScanf(s," %255[^=\n] ", p);
	}
	NXCloseMemory(s, NX_FREEBUFFER);
	[self setImage:0];
    } else {
        fprintf(stderr, "Could not read from %s - creating it.\n", defaultfile);
				temp = fopen(defaultfile, "w");
				fclose(temp);
    }
    return self;
}

- writeDefault
{
    int i;
    NXStream *s;
    
    s = NXOpenMemory(NULL, 0, NX_WRITEONLY) ;
    for (i=0; i<imageCount; i++) NXPrintf(s,"%s ",imageNames[i]);
    NXPrintf(s, " \nrepeat=\"%g\"\n", repeat);
    if (NXSaveToFile(s, defaultfile)<0) {
        printf("Could not write to %s.\n", defaultfile);
	exit(1);
    }
    NXCloseMemory(s, NX_FREEBUFFER);
 //    NXWriteDefault( "Background", "Image", p);  // Crashes default database
    return self;
}

//
- mouseDown:(NXEvent *)event
{
    PSsetwindowlevel( -1, [window windowNum]);
    [window orderWindow:NX_ABOVE relativeTo:0];
    [window removeFromEventMask:NX_MOUSEDOWNMASK];
    return self;
}

- (BOOL)acceptsFirstMouse
{
    return YES;
}

- drawSelf:(NXRect *)r :(int)count
{
    PSsetgray( NX_DKGRAY);
    NXRectFill( &bounds);
    if (permanent) { printf("Making permanent.\n"); PSputback(); }
    if(imageCount) {
	int i;
	NXSize s;
	NXSize screenSize;
	NXPoint p={ 0, 0};
	NXRect r={ p};
	
	[NXApp getScreenSize:&screenSize];
	[images[current] getSize:&s];
	r.size=s;
 //	printf("Drawing image %s: [%g,%g].\n",imageNames[current],s.width,s.height);
	if (s.width>0 && s.height>0) {
	    [images[current] composite:NX_COPY toPoint:&p];
	    for( i=s.width; i<screenSize.width; i+=s.width) {
		p.x=i;
		PSsplat( &r, &p);
	    }
	    p.x=0;
	    r.size.width=screenSize.width;
	    for( i=s.height; i<screenSize.height; i+=s.height) {
		p.y=i;
		PSsplat( &r, &p);
	    }
	}
    }
    return self;
}
//
- windowDidBecomeKey:sender
{
    PSsetwindowlevel( NX_DOCKLEVEL+1, [sender windowNum]);
    return self;
}

@end
