/*
 * "p" - A very simple Project Manager using the GIMP Toolkit
 * Copyright (c) 1998, 1999, 2000  Elmar Ludwig
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <sys/types.h>
#include <unistd.h>
#else
extern char *getcwd ();
#endif

#include <Foundation/NSArray.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSPathUtilities.h>
#include <GToolKit/GToolKit.h>
#include "Manager.h"

#define KEY(listitem)	[[listitem child] stringValue]

@implementation Manager

- (id) init
{
    GTKWindow *window = [GTKWindow windowWithType:GTK_WINDOW_TOPLEVEL];
    GTKVBox *vbox = [GTKVBox vBoxWithHomogeneous:NO spacing:0];
    GTKHBox *hbox1 = [GTKHBox hBoxWithHomogeneous:NO spacing:0];
    GTKButton *launch = [GTKButton buttonWithLabel:@"Launch"];
    GTKButton *debug = [GTKButton buttonWithLabel:@"Debug"];
    GTKButton *attrib = [GTKButton buttonWithLabel:@"Attributes"];
    GTKButton *build = [GTKButton buttonWithLabel:@"Build"];
    GTKHSeparator *hsep = [GTKHSeparator hSeparator];
    GTKVSeparator *vsep = [GTKVSeparator vSeparator];
    GTKHBox *hbox2 = [GTKHBox hBoxWithHomogeneous:NO spacing:0];
    GTKMenu *popup = [GTKMenu menu];
    GTKItemFactory *item_factory =
	[GTKItemFactory itemFactoryWithContainerType:GTK_TYPE_MENU_BAR
			path:@"<menu>" accelGroup:nil];
    GTKWidget *menubar;
    GTKScrolledWindow *scroll1 =
	[GTKScrolledWindow scrolledWindowWithHadjustment:nil vadjustment:nil];
    GTKScrolledWindow *scroll2 =
	[GTKScrolledWindow scrolledWindowWithHadjustment:nil vadjustment:nil];
    GTKAction *select =
	[GTKAction actionWithTarget:self selector:@selector(select:)];
    GTKTooltips *tips = [GTKTooltips tooltips];
    char buffer[1024];
    int index;

    static GTKItemFactoryEntry menus[] =
    {
	// target = self
	{{ "/_Project", NULL, 0, 0, "<Branch>" }},
	{{ "/Project/_Info Panel...",	"<control>I" },	@selector(info:) },
	{{ "/Project/_Preferences..." }},
	{{ "/Project/sep1", NULL, 0, 0, "<Separator>" }},

	{{ "/_File", NULL, 0, 0, "<Branch>" }},
	{{ "/File/_Add...",		"<control>A" },	@selector(add:) },
	{{ "/File/sep1", NULL, 0, 0, "<Separator>" }},
	{{ "/File/_Open",		"<control>O" },	@selector(open:) },
	{{ "/File/_Remove",		"<control>R" },	@selector(remove:) },

	// target = window
	{{ "/Project/_Quit",		"<control>Q" },	@selector(destroy) },
    };

    [super init];
#ifdef HAVE_GETCWD
    // this should be replaced with [filemanager currentDirectoryPath]
    if (getcwd(buffer, sizeof buffer))
	path = [[NSString alloc] initWithCString:buffer];
    else
#endif
	path = [@"." retain];
    program = [[path lastPathComponent] retain];

    list1 = [GTKList list];
    list2 = [GTKList list];
    groups = [NSArray arrayWithObjects:
		      [GTKListItem listItemWithLabel:@"Classes"],
		      [GTKListItem listItemWithLabel:@"Headers"],
		      [GTKListItem listItemWithLabel:@"Other Sources"],
		      [GTKListItem listItemWithLabel:@"Images"],
		      [GTKListItem listItemWithLabel:@"Other Resources"],
		      [GTKListItem listItemWithLabel:@"Supporting Files"],
		      [GTKListItem listItemWithLabel:@"Libraries"], nil];
    [list1 appendItems:groups];

    [item_factory createItems:&menus[0] count:9 forTarget:self];
    [item_factory createItems:&menus[9] count:1 forTarget:window];
    [[item_factory getWidget:@"/Project/Preferences..."] setSensitive:NO];

    target = [GTKOptionMenu optionMenu];
    [popup append:[GTKMenuItem menuItemWithLabel:@"(default)"]];
    [popup append:[GTKMenuItem menuItemWithLabel:@"install"]];
    [popup append:[GTKMenuItem menuItemWithLabel:@"clean"]];
    [target setMenu:popup];

    [scroll1 setPolicy:GTK_POLICY_AUTOMATIC vscrollbarPolicy:GTK_POLICY_ALWAYS];
    [scroll2 setPolicy:GTK_POLICY_AUTOMATIC vscrollbarPolicy:GTK_POLICY_ALWAYS];
    [scroll1 addWithViewport:list1];
    [scroll2 addWithViewport:list2];

    [launch connectSignal:@"clicked" withTarget:self sel:@selector(launch:)];
    [debug  connectSignal:@"clicked" withTarget:self sel:@selector(debug:)];
    [attrib connectSignal:@"clicked" withTarget:self sel:@selector(attrib:)];
    [build  connectSignal:@"clicked" withTarget:self sel:@selector(build:)];

    [tips setTip:launch tipText:@"Start the Program" tipPrivate:nil];
    [tips setTip:debug tipText:@"Start the Debugger" tipPrivate:nil];
    [tips setTip:attrib tipText:@"Modify Project Options" tipPrivate:nil];
    [tips setTip:build tipText:@"Build the Project" tipPrivate:nil];
    [tips setTip:target tipText:@"Select Build Target" tipPrivate:nil];

    [hbox1 packStart:launch expand:NO fill:YES padding:4];
    [hbox1 packStart:debug expand:NO fill:YES padding:4];
    [hbox1 packStart:attrib expand:NO fill:YES padding:4];
    [hbox1 packStart:build expand:NO fill:YES padding:4];
    [hbox1 packStart:vsep expand:NO fill:YES padding:4];
    [hbox1 packStart:target expand:NO fill:YES padding:4];
    [hbox2 packStart:scroll1 expand:YES fill:YES padding:4];
    [hbox2 packStart:scroll2 expand:YES fill:YES padding:4];

    menubar = [item_factory getWidget:@"<menu>"];
    [vbox packStart:menubar expand:NO fill:YES padding:0];
    [vbox packStart:hbox1 expand:NO fill:NO padding:8];
    [vbox packStart:hsep expand:NO fill:YES padding:0];
    [vbox packStart:hbox2 expand:YES fill:YES padding:8];

    [window add:vbox];
    [window addAccelGroup:[item_factory accelGroup]];
    [window connectSignal:@"destroy" withTarget:GTKApp
	    sel:@selector(terminate:)];
    [window setUsize:320 height:220];
    [window setTitle:[NSString stringWithFormat:@" %@ - %@ ", program,
				[path stringByAbbreviatingWithTildeInPath]]];
    [window show];

    files = [[NSMutableDictionary alloc] initWithCapacity:8];
    for (index = [groups count]; index;)
    {
	GTKListItem *item = [groups objectAtIndex:--index];

	[files setObject:[NSMutableArray array] forKey:KEY(item)];
	[item connectSignal:@"select" withAction:select];
    }

    [list2 connectSignal:@"selection_changed" withTarget:self
	   sel:@selector(selectFile:)];
    return self;
}

- (void) dealloc
{
    [files release];
    [program release];
    [path release];
    [filename release];
    [super dealloc];
}

- (void) info:(id) sender
{
    GTKRunMessageBox(@"Info Panel", @"This is an example of a very simple\n"
		     @"Project Manager using the GIMP Toolkit.",
		     @"Continue", nil, nil);
}

- (void) select:(id) sender
{
    [list2 removeItems:[list2 children]];
    [list2 appendItems:[files objectForKey:KEY(sender)]];
    if ([GTKApp getCurrentEvent]->type == GDK_2BUTTON_PRESS)
	[self addFile:sender];
}

- (void) selectFile:(id) sender
{
    if ([GTKApp getCurrentEvent]->type == GDK_2BUTTON_PRESS)
	[self open:sender];
}

- (void) add:(id) sender
{
    NSArray *sel = [list1 selection];

    if ([sel count]) [self addFile:[sel objectAtIndex:0]];
    else GTKRunMessageBox(@"Warning", @"You must select a group first",
			  @"Continue", nil, nil);
}

- (void) addFile:(GTKListItem *) group
{
    NSMutableArray *items = [files objectForKey:KEY(group)];
    GTKFileSelection *filesel =
	[[GTKFileSelection alloc] initWithTitle:@"Add File..."];

    [[filesel okButton] connectSignal:@"clicked" withTarget:self
	sel:@selector(ok:)];
    [[filesel cancelButton] connectSignal:@"clicked" withTarget:self
	sel:@selector(cancel:)];
    [filesel connectSignal:@"delete_event" withTarget:GTKApp
	sel:@selector(true)];
    [filesel hideFileopButtons];
    [filesel show];

    if ([GTKApp runModalForWindow:filesel])
    {
	NSString *name = [[filesel getFilename] lastPathComponent];
	GTKListItem *item = [GTKListItem listItemWithLabel:name];

	[items addObject:item];
	[list2 add:item];
	[self save:self];
    }

    [filesel destroy];
    [filesel release];
}

- (void) ok:(id) sender
{
    [GTKApp stopModalWithCode:YES];
}

- (void) cancel:(id) sender
{
    [GTKApp stopModalWithCode:NO];
}

- (void) remove:(id) sender
{
    NSArray *sel1 = [list1 selection];
    NSArray *sel2 = [list2 selection];
    NSMutableArray *items;
    GTKListItem *item;

    if ([sel1 count] == 0 || [sel2 count] == 0) return;
    items = [files objectForKey:KEY([sel1 objectAtIndex:0])];
    item = [sel2 objectAtIndex:0];

    [list2 remove:item];
    [items removeObject:item];
    [self save:self];
}

- (void) open:(id) sender
{
    NSArray *sel2 = [list2 selection];
    NSString *command;

    if ([sel2 count] == 0) return;
    command = [NSString stringWithFormat:@"%@ '%@' &", EDIT_CMD,
			[[[sel2 objectAtIndex:0] child] stringValue]];
    system([command cString]);
}

- (void) launch:(id) sender
{
    NSString *command = [NSString stringWithFormat:@"%@ '%@' &",
				  LAUNCH_CMD, program];

    system([command cString]);
}

- (void) debug:(id) sender
{
    NSString *command = [NSString stringWithFormat:@"%@ '%@' &",
				  DEBUG_CMD, program];

    system([command cString]);
}

- (void) attrib:(id) sender
{
    NSString *command = [NSString stringWithFormat:@"%@ '%@' &",
				  EDIT_CMD, @"Makefile"];

    system([command cString]);
}

- (void) build:(id) sender
{
    static GdkFont *font;
    GTKWindow *output = [GTKWindow windowWithType:GTK_WINDOW_TOPLEVEL];
    GTKHBox *box = [GTKHBox hBoxWithHomogeneous:NO spacing:4];
    GTKVScrollbar *bar = [GTKVScrollbar vScrollbarWithAdjustment:nil];
    GTKText *text = [GTKText textWithHadj:nil vadj:[bar getAdjustment]];
    NSString *command;
    FILE *pipe;
    char buffer[1024];

    if (!font) font = gdk_font_load(OUTPUT_FONT);

    [text setEditable:NO];
    [box packStart:text expand:YES fill:YES padding:0];
    [box packStart:bar expand:NO fill:YES padding:0];
    [output setTitle:@"Build Output Window"];
    [output setBorderWidth:2];
    [output add:box];
    [output setUsize:480 height:240];
    [output show];
    [text grabFocus];

    command = [[target child] stringValue];
    if ([command isEqualToString:@"(default)"]) command = @"";
    command = [MAKE_CMD stringByAppendingFormat:@" %@", command];

    if ((pipe = popen([command cString], "r")))
    {
	while (fgets(buffer, sizeof buffer, pipe))
	    [text insert:font fore:NULL back:NULL
		  chars:[NSString stringWithCString:buffer] length:-1];
	pclose(pipe);
    }

    [text insert:font fore:NULL back:NULL chars:@"--- finished ---" length:-1];
}

- (void) loadFromFile:(NSString *) _filename
{
    const char *name;
    FILE *file;
    NSEnumerator *keys;
    GTKListItem *item;

    [filename autorelease];
    filename = [_filename copy];
    name = [_filename cString];
    file = fopen(name, "r");

    if (!file)
    {
	perror(name);
	return;
    }

    keys = [groups objectEnumerator];
    while ((item = [keys nextObject]))
    {
	NSMutableArray *array = [files objectForKey:KEY(item)];
	char buffer[4096];
	char *ptr, *string;

	[array removeAllObjects];

	if (!fgets(buffer, sizeof buffer, file) || !(ptr = strchr(buffer, '=')))
	    break;
	++ptr;
	while ((string = strtok(ptr, " \t\r\n")))
	{
	    ptr = NULL;
	    item = [GTKListItem listItemWithLabel:
			[NSString stringWithCString:string]];
	    [array addObject:item];
	}
    }

    if (ferror(file)) perror(name);
    fclose(file);
}

- (void) save:(id) sender
{
    if (!filename)
	filename = [[program stringByAppendingString:@".project"] retain];
    [self saveToFile:filename];
}

- (void) saveToFile:(NSString *) _filename
{
    const char *name = [_filename cString];
    FILE *file = fopen(name, "w");
    NSEnumerator *keys, *values;
    GTKListItem *item;
    NSMutableString *command = [[MAKEFILE_GEN mutableCopy] autorelease];
    NSString *string;

    if (!file)
    {
	perror(name);
	return;
    }

    [command appendFormat:@" %@", program];
    keys = [groups objectEnumerator];
    while ((item = [keys nextObject]))
    {
	fprintf(file, "%s =", [KEY(item) cString]);
	values = [[files objectForKey:KEY(item)] objectEnumerator];
	while ((item = [values nextObject]))
	{
	    string = [[item child] stringValue];
	    [command appendFormat:@" %@", string];
	    fprintf(file, " %s", [string cString]);
	}
	fputc('\n', file);
    }

    fflush(file);
    if (ferror(file)) perror(name);
    fclose(file);
    system([command cString]);
}
@end
