/*
   GNUstep ProjectCenter - http://www.gnustep.org

   Copyright (C) 2000-2004 Free Software Foundation

   Authors: Philippe C.D. Robert
            Serg Stoyan

   This file is part of GNUstep.

   This application 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 application 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
   Library General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include "PCDefines.h"
#include "PCFileManager.h"
#include "PCProjectManager.h"
#include "PCProject.h"
#include "PCProjectBrowser.h"
#include "PCProjectEditor.h"

#include "PCLogController.h"

NSString *PCBrowserDidSetPathNotification = @"PCBrowserDidSetPathNotification";

@implementation PCProjectBrowser

// ============================================================================
// ==== Intialization & deallocation
// ============================================================================

- (id)initWithProject:(PCProject *)aProject
{
  if ((self = [super init]))
    {
      project = aProject;

      browser = [[NSBrowser alloc] initWithFrame:NSMakeRect(-1,251,562,128)];
      [browser setRefusesFirstResponder:YES];
      [browser setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin];
      [browser setTitled:NO];
      [browser setMaxVisibleColumns:4];
      [browser setSeparatesColumns:NO];
      [browser setAllowsMultipleSelection:YES];
      [browser setDelegate:self];
      [browser setTarget:self];
      [browser setAction:@selector(click:)];
      [browser setDoubleAction:@selector(doubleClick:)];
      [browser setRefusesFirstResponder:YES];
      [browser loadColumnZero];

      [[NSNotificationCenter defaultCenter] 
	addObserver:self 
	   selector:@selector(projectDictDidChange:)
	       name:PCProjectDictDidChangeNotification 
	     object:nil];
    }

  return self;
}

- (void)dealloc
{
#ifdef DEVELOPMENT
  NSLog (@"PCProjectBrowser: dealloc");
#endif

  [[NSNotificationCenter defaultCenter] removeObserver:self];

  RELEASE(browser);

  [super dealloc];
}

// ============================================================================
// ==== Accessory methods
// ============================================================================

- (NSView *)view
{
  return browser;
}

// Returns nil if multiple files selected
- (NSString *)nameOfSelectedFile
{
  NSString *name = nil;

  if ([[browser selectedCells] count] == 1)
    {
      name = [[browser path] lastPathComponent];
      if ([name isEqualToString:[self nameOfSelectedCategory]])
	{
	  return nil;
	}
    }
    
  return name;
}

// Returns nil if multiple files selected
- (NSString *)pathToSelectedFile
{
  NSString *name = nil;
  NSString *path = nil;

  if ([[browser selectedCells] count] == 1)
    {
      name = [[browser path] lastPathComponent];
      if ([name isEqualToString:[self nameOfSelectedCategory]])
	{
	  path = nil;
	}
      else
	{
	  path = [browser path];
	}
    }
  
  return path;
}

// Returns nil of multiple categories selected
- (NSString *)nameOfSelectedCategory
{
  NSString  *name = nil;
  NSArray   *pathArray = [[browser path] componentsSeparatedByString:@"/"];
  PCProject *activeProject = [[project projectManager] activeProject];
  NSArray   *rootCategories = [activeProject rootCategories];
  int       i;

  if ([rootCategories containsObject:[pathArray lastObject]]
      && [[browser selectedCells] count] > 1)
    {
      return nil;
    }

  for (i = [pathArray count] - 1; i >= 0; i--)
    {
      if ([rootCategories containsObject:[pathArray objectAtIndex:i]])
	{
	  name = [pathArray objectAtIndex:i];
	  break;
	}
    }
  
  return name;
}

// Returns nil of multiple categories selected
- (NSString *)pathToSelectedCategory
{
  NSString       *path = nil;
  NSString       *selectedCategory = [self nameOfSelectedCategory];
  NSMutableArray *bPathArray = nil;
  int            i;
 
  if (selectedCategory)
    {
      bPathArray = [NSMutableArray arrayWithArray:[[browser path]
	componentsSeparatedByString:@"/"]];
      i = [bPathArray count] - 1;
      while (![[bPathArray objectAtIndex:i] isEqualToString:selectedCategory])
	{
	  [bPathArray removeObjectAtIndex:i];
	  i = [bPathArray count] - 1;
	}
      path = [bPathArray componentsJoinedByString:@"/"];
    }
  
  return path;
}

- (NSString *)nameOfSelectedRootCategory
{
  NSString *categoryPath = [self pathToSelectedCategory];
  NSArray  *pathComponents = nil;

  if ([categoryPath isEqualToString:@"/"] || [categoryPath isEqualToString:@""])
    {
      return nil;
    }
    
  pathComponents = [categoryPath componentsSeparatedByString:@"/"];

  return [pathComponents objectAtIndex:1];
}

- (NSArray *)selectedFiles
{
  NSArray        *cells = [browser selectedCells];
  NSMutableArray *files = [[NSMutableArray alloc] initWithCapacity: 1];
  int            i;
  int            count = [cells count];
  PCProject      *activeProject = [[project projectManager] activeProject];

  // Return nil if categories selected
  if ([cells count] == 0
      || [[activeProject rootCategories] 
      containsObject:[[cells objectAtIndex:0] stringValue]])
    {
      return nil;
    }

  for (i = 0; i < count; i++)
    {
      [files addObject: [[cells objectAtIndex: i] stringValue]];
    }
    
  return AUTORELEASE((NSArray *)files);
}

- (NSString *)path
{
  return [browser path];
}

- (BOOL)setPath:(NSString *)path
{
  BOOL     res;

  if ([[browser path] isEqualToString: path])
    {
      return YES;
    }

//  PCLogInfo(self, @"[setPath]: %@", path);

  res = [browser setPath:path];

  [[NSNotificationCenter defaultCenter]
    postNotificationName:PCBrowserDidSetPathNotification
                  object:self];

  return res;
}

- (void)reloadLastColumnAndNotify:(BOOL)yn
{
  int       column = [browser lastColumn];
  NSString  *category = [self nameOfSelectedCategory];
  int       selectedColumn = [browser selectedColumn];
  NSMatrix  *colMatrix = [browser matrixInColumn:selectedColumn];
  int       rowCount = 0, colCount = 0, spCount = 0;
  PCProject *activeProject = [[project projectManager] activeProject];
  NSString  *selCellTitle = [[browser selectedCell] stringValue];

  if ([category isEqualToString:@"Subprojects"]
      && ![selCellTitle isEqualToString:@"Subprojects"])
    { // /Subprojects/Name selected
      if ([selCellTitle isEqualToString:[activeProject projectName]])
	{
	  activeProject = [activeProject superProject];
	}
      [colMatrix getNumberOfRows:&rowCount columns:&colCount];
      spCount = [[[activeProject projectDict] 
	objectForKey:PCSubprojects] count];
    }

  if ([category isEqualToString:@"Subprojects"] && rowCount != spCount
      && ![[[browser selectedCell] stringValue] isEqualToString:@"Subprojects"])
    {
      column = selectedColumn;
    }
  
  [browser reloadColumn:column];

  if (yn)
    {
      [[NSNotificationCenter defaultCenter]
	postNotificationName:PCBrowserDidSetPathNotification
                      object:self];
    }
}

- (void)reloadLastColumnAndSelectFile:(NSString *)file
{
  PCProject *p = [[project projectManager] activeProject];
  NSString  *catKey = [p keyForCategory:[self nameOfSelectedCategory]];
  NSArray   *array = [[p projectDict] objectForKey:catKey];
  NSString  *path = [self path];
  NSString  *tmp = nil;

  // Determine last column with files (removing classes and methods from path)
  tmp = [[path lastPathComponent] substringWithRange:NSMakeRange(0,1)];
  while ([tmp isEqualToString:@"@"]     // classes
	 || [tmp isEqualToString:@"+"]  // factory methods
	 || [tmp isEqualToString:@"-"]) // instance methods
    {
      path = [path stringByDeletingLastPathComponent];
      tmp = [[path lastPathComponent] substringWithRange:NSMakeRange(0,1)];
    }

  NSLog(@"PCBrowser set path: %@", path);
  [self setPath:[path stringByDeletingLastPathComponent]];
  [self reloadLastColumnAndNotify:NO];

  [browser selectRow:[array indexOfObject:file] inColumn:[browser lastColumn]];

  // Notify
  [[NSNotificationCenter defaultCenter]
    postNotificationName:PCBrowserDidSetPathNotification
                  object:self];
}

// ============================================================================
// ==== Actions
// ============================================================================

- (void)click:(id)sender
{
  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
  NSFileManager  *fm = [NSFileManager defaultManager];
  NSString       *category = nil;
  NSString       *fileName = nil;
  NSString       *filePath = nil;
  NSString       *key = nil;
  PCProject      *activeProject = nil;
  BOOL           isDir;

  if (sender != browser)
    {
      return;
    }

  category = [self nameOfSelectedCategory];
  activeProject = [[project projectManager] activeProject];

  // [[sender selectedCell] isLeaf]

  if ([activeProject isEditableCategory:category]
      && [[self selectedFiles] count] == 1)
    {
      fileName = [[sender selectedCell] stringValue];
      key = [activeProject keyForCategory:category];
      filePath = [activeProject dirForCategoryKey:key];
      filePath = [filePath stringByAppendingPathComponent:fileName];
  
/*      PCLogInfo(self, @"[click] category: %@ filePath: %@",
		category, filePath);*/

      if ([fm fileExistsAtPath:filePath isDirectory:&isDir] && !isDir
	  && [activeProject isEditableFile:filePath])
	{
	  if (![[ud objectForKey:SeparateEditor] isEqualToString:@"YES"])
	    {
	      NSString *path = [self path];
	      [[project projectEditor] editorForFile:filePath
		                        categoryPath:[browser path]
					    windowed:NO];
	      [self reloadLastColumnAndNotify:NO];
	      [self setPath:path];
	    }
	}
    }

  [[NSNotificationCenter defaultCenter]
    postNotificationName:PCBrowserDidSetPathNotification 
                  object:self];
}

- (void)doubleClick:(id)sender
{
  id selectedCell;
  
  if (sender != browser)
    {
      return;
    }

  selectedCell = [sender selectedCell];

  if ([selectedCell isLeaf]) 
    {
      NSString  *category = [self nameOfSelectedCategory];
      NSString  *fileName = [[sender selectedCell] stringValue];
      NSString  *filePath = nil;
      NSString  *key = nil;
      PCProject *activeProject = nil;
      
      activeProject = [[project projectManager] activeProject];
      key = [activeProject keyForCategory:category];
      filePath = [activeProject dirForCategoryKey:key];
      filePath = [filePath stringByAppendingPathComponent:fileName];

      PCLogInfo(self, @"{doubleClick} filePath: %@", filePath);

      if ([activeProject isEditableCategory:category])
	{
	  [[project projectEditor] editorForFile:filePath
	                            categoryPath:[browser path]
					windowed:YES];
	}
      else if (![[self nameOfSelectedCategory] isEqualToString:@"Libraries"]
	       && [[NSWorkspace sharedWorkspace] openFile:filePath] == NO) 
	{
	  NSRunAlertPanel(@"Attention!",
			  @"Could not open %@.",
			  @"OK",nil,nil,filePath);
	}
    }
  else 
    {
      if ([[selectedCell title] isEqualToString:@"Subprojects"]) 
	{
	  [[project projectManager] addSubproject];
	}
      else 
	{
	  [[project projectManager] addProjectFiles];
	}
    }
}

// ============================================================================
// ==== Notifications
// ============================================================================

- (void)projectDictDidChange:(NSNotification *)aNotif
{
  NSDictionary *notifObject = [aNotif object];
  PCProject    *changedProject = [notifObject objectForKey:@"Project"];
  NSString     *changedAttribute = [notifObject objectForKey:@"Attribute"];

  if (!browser)
    {
      return;
    }

  if (changedProject != project 
      && changedProject != [project activeSubproject]
      && [changedProject superProject] != [project activeSubproject])
    {
      return;
    }

  if ([[changedProject sourceFileKeys] containsObject:changedAttribute]
      || [[changedProject resourceFileKeys] containsObject:changedAttribute]
      || [[changedProject otherKeys] containsObject:changedAttribute])
    {
      [self reloadLastColumnAndNotify:YES];
    }
}

@end

@implementation PCProjectBrowser (ProjectBrowserDelegate)

- (void)browser:(NSBrowser *)sender createRowsForColumn:(int)column
                                               inMatrix:(NSMatrix *)matrix
{
  NSString  *pathToCol = nil;
  NSArray   *files = nil;
  int       i = 0;
  int       count = 0;

  if (sender != browser || !matrix || ![matrix isKindOfClass:[NSMatrix class]])
    {
      return;
    }

  pathToCol = [sender pathToColumn:column];
  files = [project contentAtCategoryPath:pathToCol];
  if (files)
    {
      count = [files count];
    }

  for (i = 0; i < count; ++i) 
    {
      NSMutableString *categoryPath = nil;
      id              cell;
      
      categoryPath = [NSMutableString stringWithString:pathToCol];
      
      [matrix insertRow:i];

      cell = [matrix cellAtRow:i column:0];
      [cell setStringValue:[files objectAtIndex:i]];

      if (![categoryPath isEqualToString:@"/"])
	{
	  [categoryPath appendString:@"/"];
	}
      [categoryPath appendString:[files objectAtIndex:i]];

      [cell setLeaf:![project hasChildrenAtCategoryPath:categoryPath]];
    }
}

@end
