/*
    AggregateProjectType.m

    Implementation of the AggregateProjectType class for the
    ProjectManager application.

    Copyright (C) 2005  Saso Kiselkov

    This program is free software; you can redistribute it and/or modify
    it 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#import "AggregateProjectType.h"

#import <Foundation/NSString.h>
#import <Foundation/NSBundle.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSSet.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSValue.h>

#import <AppKit/NSPanel.h>

#import "../../ProjectDocument.h"
#import "../../ProjectModule.h"
#import "../../ProjectModules/MakeBuilder/MakeBuilder.h"
#import "../../ProjectModules/SubprojectsManager/SubprojectsManager.h"

#import "../../NSImageAdditions.h"
#import "../../CommonFunctions/MakefileChecking.h"

static inline void
InsertMakefileVariable(NSMutableString * makefileString,
                       NSString * name,
                       NSString * value)
{
  [makefileString appendString: [NSString stringWithFormat:
    @"\n"
    @"%@ = %@\n", name, value]];
}

static void
InsertMakefileEnumerationVariable(NSMutableString * makefileString,
                                  NSString * name,
                                  NSArray * values)
{
  NSEnumerator * e;
  NSString * value;

  [makefileString appendString: [NSString stringWithFormat:
    @"\n"
    @"%@ = ", name]];
  e = [values objectEnumerator];
  while ((value = [e nextObject]) != nil)
    {
      [makefileString appendString: [NSString stringWithFormat: @"%@ \\\n",
        value]];
    }
}

@interface AggregateProjectType (Private)

- (BOOL) prepareMakefile;

@end

@implementation AggregateProjectType (Private)

/**
 * Prepares the project's makefile.
 */
- (BOOL) prepareMakefile
{
  NSString * buildDir = [owner projectDirectory];
  NSString * makefile = [buildDir stringByAppendingPathComponent:
    @"GNUmakefile"];
  NSMutableString * makefileString = [NSMutableString string];

  if (oldMakeHash != 0)
    {
      if (oldMakeHash != ComputeHashFromMakefile(makefile))
        {
          switch (NSRunAlertPanel(_(@"Warning: GNUmakefile changed"),
            _(@"It seems the build settings in the GNUmakefile have been\n"
              @"edited since I've last regenerated it. You shouldn't do\n"
              @"this. Please, put build customizations in GNUmakefile."
                @"preamble\n"
              @"or GNUmakefile.postamble. For now, I can backup your current\n"
              @"GNUmakefile (it will be named GNUmakefile~) or overwrite it."),
            _(@"Back it up"), _(@"Overwrite"), _(@"Cancel")))
            {
            case NSAlertDefaultReturn:
              if (![[NSFileManager defaultManager]
                movePath: makefile
                  toPath: [makefile stringByAppendingString: @"~"]
                 handler: nil])
                {
                  NSRunAlertPanel(_(@"Backup failed"),
                    _(@"Failed to move the old GNUmakefile to GNUmakefile~.\n"
                      @"Safety stop."), nil, nil, nil);

                  return NO;
                }
              break;
            case NSAlertAlternateReturn:
              break;
            case NSAlertOtherReturn:
              return NO;
            }
        }
    }

  [makefileString appendString: [NSString stringWithContentsOfFile:
    [[NSBundle bundleForClass: [self class]] pathForResource:
    @"MakefileEditWarning" ofType: @"txt"]]];

  [makefileString appendString:
    @"\n"
    @"include $(GNUSTEP_MAKEFILES)/common.make\n"];

  // SUBPROJECTS
  {
    SubprojectsManager * subprojectsManager = [owner
      projectModuleWithName: @"SubprojectsManager"];
    NSArray * subprojectNames = [subprojectsManager subprojectNames];
    NSEnumerator * e;
    NSString * subprojectName;
    NSMutableArray * subprojects = [NSMutableArray arrayWithCapacity:
      [subprojectNames count]];

    e = [subprojectNames objectEnumerator];
    while ((subprojectName = [e nextObject]) != nil)
      {
        [subprojects addObject: [@"Subprojects"
          stringByAppendingPathComponent: subprojectName]];
      }

    InsertMakefileEnumerationVariable(makefileString,
                                      @"SUBPROJECTS",
                                      subprojects);
  }

  [makefileString appendString:
    @"\n"
    @"-include GNUmakefile.preamble\n"
    @"include $(GNUSTEP_MAKEFILES)/aggregate.make\n"
    @"-include GNUmakefile.postamble\n"];

  oldMakeHash = ComputeHashFromMakeString(makefileString);

  return [makefileString writeToFile: makefile atomically: NO];
}

@end

@implementation AggregateProjectType

+ (NSString *) projectTypeID
{
  return @"Aggregate";
}

+ (NSString *) humanReadableProjectTypeName
{
  return _(@"Aggregate");
}

+ (NSString *) projectTypeDescription
{
  return [NSString stringWithContentsOfFile:
    [[NSBundle bundleForClass: self]
    pathForResource: @"Description" ofType: @"txt"]];
}

+ (NSImage *) projectTypeIcon
{
  static NSImage * icon = nil;

  if (icon == nil)
    {
      ASSIGN(icon, [NSImage imageNamed: @"AggregateProjectType" owner: self]);
    }

  return icon;
}

+ (NSArray *) projectModules
{
  return [NSArray arrayWithObjects:
    @"ProjectAttributes",
    @"MakeBuilder",
    @"SubprojectsManager",
    nil];
}

+ (NSDictionary *) projectTemplateDescriptions
{
  NSBundle * myBundle = [NSBundle bundleForClass: self];

  return [NSDictionary dictionaryWithObjectsAndKeys:
    PMCreateTemplateDescription(_(@"An empty aggregate project."), [NSImage
      imageNamed: @"AggregateProjectType" owner: self]), _(@"Empty Aggregate"),
    nil];
}

+ (NSString *) pathToProjectTemplate: (NSString *) templateName
{
  NSBundle * myBundle = [NSBundle bundleForClass: self];

  return [myBundle pathForResource: @"EmptyAggregate" ofType: @"template"];
}

- initWithDocument: (ProjectDocument *) anOwner
    infoDictionary: (NSDictionary *) infoDict
    projectModules: (NSArray *) projectModules
{
  if ((self = [self init]) != nil)
    {
      NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];

      owner = anOwner;

      ASSIGN(builder, [owner projectModuleWithName: @"MakeBuilder"]);

      oldMakeHash = [[infoDict objectForKey: @"MakefileHash"] intValue];
    }

  return self;
}

- (void) dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver: self];

  TEST_RELEASE(builder);

  [super dealloc];
}

- (NSDictionary *) infoDictionary
{
  return [NSDictionary
    dictionaryWithObject: [NSNumber numberWithInt: oldMakeHash]
                  forKey: @"MakefileHash"];
}

- (BOOL) regenerateDerivedFiles
{
  return [self prepareMakefile];
}

- (NSString *) pathToSubprojectsDirectory
{
  return [[owner projectDirectory]
    stringByAppendingPathComponent: @"Subprojects"];
}

- (NSArray *) tabIdentifiersForProjectAttributes: (id) sender
{
  return nil;
}

- (NSString *)  projectAttributes:  (id) sender
 toolbarItemLabelForTabIdentifier: (NSString *) tab
{
  return nil;
}

- (NSString *)    projectAttributes:  (id) sender
 toolbarItemToolTipForTabIdentifier: (NSString *) tab
{
  return nil;
}

- (NSImage *)  projectAttributes:  (id) sender
 toolbarItemIconForTabIdentifier: (NSString *) tab
{
  return nil;
}

- (NSView *) projectAttributes: (id) sender
          viewForTabIdentifier: (NSString *) tab
{
  return nil;
}

- (NSArray *) buildTargetsForMakeBuilder: (id) sender
{
  return [NSArray arrayWithObjects:
    _(@"Default"),
    _(@"Debug"),
    _(@"Profile"),
    _(@"Install"),
    nil];
}

- (BOOL) prepareForBuildByBuilder: (id) sender
                           target: (NSString *) aTarget
{
  return [self prepareMakefile];
}

- (NSArray *) buildArgumentsForBuilder: (id) sender
                                target: (NSString *) aTarget
{
  if ([aTarget isEqualToString: _(@"Default")])
    {
      return nil;
    }
  else if ([aTarget isEqualToString: _(@"Debug")])
    {
      return [NSArray arrayWithObject: @"debug=yes"];
    }
  else if ([aTarget isEqualToString: _(@"Profile")])
    {
      return [NSArray arrayWithObject: @"profile=yes"];
    }
  else if ([aTarget isEqualToString: _(@"Install")])
    {
      return [NSArray arrayWithObject: @"install"];
    }
  else
    {
      NSLog(@"Unknown target \"%@\" passed to "
        @"-prepareForBuildByBuilder:target:", aTarget);

      return nil;
    }
}

- (BOOL) prepareForCleanByBuilder: (id) sender
                           target: (NSString *) aTarget
{
  return [self prepareMakefile];
}

- (NSArray *) cleanArgumentsForBuilder: (id) sender
                                target: (NSString *) aTarget
{
  if ([aTarget isEqualToString: _(@"Default")] ||
      [aTarget isEqualToString: _(@"Install")])
    {
      return [NSArray arrayWithObject: @"clean"];
    }
  else
    {
      return [NSArray arrayWithObject: @"distclean"];
    }
}

@end
