/*
**  Sources.m
**
**  Copyright (c) 2004
**
**  Author: Yen-Ju Chen <yjchenx@hotmail.com>
**
**  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "Sources.h"
#include "FolderSource.h"
#include "SmartFolderSource.h"
#include "LibrarySource.h"

/* Source array of dictionary */
static NSString *SourceTypeKey = @"SourceTypeKey";
  static NSString *LibrarySourceType = @"LibrarySourceType";
  static NSString *NoteSourceType = @"NoteSourceType";
  static NSString *FolderSourceType = @"FolderSourceType";
  static NSString *SmartFolderSourceType = @"SmartFolderSourceType";
  static NSString *SearchSourceType = @"SearchSourceType";
  static NSString *UnknownSourceType = @"UnknownSourcetype";

static NSString *SourceResourcePathKey = @"SourceResourcePathKey";
static NSString *SourceContainerKey = @"SourceContainerKey";
static NSString *SourceIndexPathKey = @"SourceIndexPathKey";

static Sources *sharedInstance = nil;

SourceType SourceTypeFromString (NSString *type)
{
  if ([type isEqualToString: LibrarySourceType])
    return SourceLibraryType;
  else if ([type isEqualToString: NoteSourceType])
    return SourceNoteType;
  else if ([type isEqualToString: FolderSourceType])
    return SourceFolderType;
  else if ([type isEqualToString: SmartFolderSourceType])
    return SourceSmartFolderType;
  else if ([type isEqualToString: SearchSourceType])
    return SourceSearchType;
  else 
    return SourceUnknownType;
}

@interface NSDictionary (SourceTypeComparison)
- (NSComparisonResult) sourceCompareType: (NSDictionary *) dictionary;
@end

@implementation NSDictionary (SourceTypeComparison)
- (NSComparisonResult) sourceCompareType: (NSDictionary *) dictionary
{
  /* Compare type first */
  SourceType selfType = SourceTypeFromString([self objectForKey: SourceTypeKey]);
  SourceType otherType = SourceTypeFromString([dictionary objectForKey: SourceTypeKey]);

  if (selfType == otherType)
    {
      return [(NSString *)[self objectForKey: TitleKey] caseInsensitiveCompare: [dictionary objectForKey: TitleKey]];
    }
  else if (selfType < otherType)
    {
      return NSOrderedAscending;
    }
  else
    {
      return NSOrderedDescending;
    }
}
@end

@implementation Sources

- (NSString *) _stringFromType: (SourceType) type
{
  NSString *typeString;
  switch(type) 
    {
      case SourceLibraryType:
        typeString = LibrarySourceType;
	break;
      case SourceNoteType:
        typeString = NoteSourceType;
	break;
      case SourceFolderType:
	typeString = FolderSourceType;
	break;
      case SourceSmartFolderType:
	typeString = SmartFolderSourceType;
	break;
      case SourceSearchType:
	typeString = SearchSourceType;
	break;
      default:
	typeString = nil;
    }
  return typeString;
}

/* override */

- (BOOL) isDuplicatedTitle: (NSString *) title skipIndex: (unsigned int) index
{
  if ([title isEqualToString: sSearch])
    {
      return YES;
    }
  return [super isDuplicatedTitle: title skipIndex: index];
}

- (void) setTitle: (NSString *) title atIndex: (unsigned int) index
{
  [super setTitle: title atIndex: index];
  [self sort];
}

/* sorting */
- (void) sort
{
  [container sortUsingSelector: @selector(sourceCompareType:)];
}

/* write accessory */
- (void) newSource: (NSString *) title ofType: (SourceType) type
{
  /*8 Check the vaoid type */
  NSString *typeString = [self _stringFromType: type];
  if ((typeString == nil) || (title == nil))return;

  unsigned int index = [self newTitle: title];
  [self setType: type atIndex: index];
  if (type != SourceNoteType)
    {
      [self setUniqueNumber: NSNotFound atIndex: index];
    }
  [self sort];
}

- (void) newSource: (NSString *) title ofType: (SourceType) type
                                      atIndex: (unsigned int) row
{
  /*8 Check the vaoid type */
  NSString *typeString = [self _stringFromType: type];
  if ((typeString == nil) || (title == nil))return;

  if (row >= [self count])
    {
      [self newSource: title ofType: type];
      return;
    }
  unsigned int index = [self newTitle: title atIndex: row];
  [self setType: type atIndex: index];
  if (type != SourceNoteType)
    {
      [self setUniqueNumber: NSNotFound atIndex: index];
    }
}

- (void) setType: (SourceType) type atIndex: (unsigned int) index
{
  NSString *typeString = [self _stringFromType: type];
  if (typeString == nil) return;

  [self setObject: [self _stringFromType: type]
 	  atIndex: index forKey: SourceTypeKey];
  if (type != SourceNoteType)
    {
      [self setUniqueNumber: NSNotFound atIndex: index];
    }
}	

- (void) setResourcePath: (NSString *) resource atIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count]) ||
      (resource == nil)) 
    return;

  [self setObject: resource atIndex: index forKey: SourceResourcePathKey];
}	

- (void) setIndexPath: (NSString *) absolutePath atIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count]) ||
      (absolutePath == nil)) 
    return;

  [self setObject: absolutePath atIndex: index forKey: SourceIndexPathKey];
}

- (void) removeContainerAtIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count])) return;
    [(NSMutableDictionary *)[container objectAtIndex: index] removeObjectForKey: SourceContainerKey];
}

/* read accessory */
- (SourceType) typeOfSourceAtIndex: (unsigned int) index
{
  if (index >= [self count]) return SourceErrorType;
  NSString *type = [self objectAtIndex: index forKey: SourceTypeKey];
  return SourceTypeFromString(type);
}

- (NSString *) resourcePathAtIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count])) return;
  return [self objectAtIndex: index forKey: SourceResourcePathKey];
}

- (NSString *) resourceFullPathAtIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count])) return;
  NSString *path = [self objectAtIndex: index forKey: SourceResourcePathKey];
  return [[[self path] stringByDeletingLastPathComponent] stringByAppendingPathComponent: path];
}

- (NSString *) indexPathAtIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count])) return;
  return [self objectAtIndex: index forKey: SourceIndexPathKey];
}

- (void) setContainer: (id) object atIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count]) ||
      (object == nil)) return;
  [self setObject: object atIndex: index forKey: SourceContainerKey];
}

- (id) containerAtIndex: (unsigned int) index
{
  if ((index == NSNotFound) || (index >= [self count])) return nil;
  return [(NSDictionary *)[container objectAtIndex: index] objectForKey: SourceContainerKey];
}

/* Basic methods */
+ (Sources *) sharedSources
{
  if (sharedInstance == nil)
    {
      sharedInstance = [[Sources alloc] init];
    }
  return sharedInstance;
}

- (void) _loadOtherSources
{
  /* Load Folder */
  unsigned int i, count = [self count];
  SourceType type;
  id object;
  NSString *loadPath;
  for(i = 0; i < count; i++)
    {
      type = [self typeOfSourceAtIndex: i];
      loadPath = [self resourceFullPathAtIndex: i];
      switch(type)
        {
          case SourceFolderType:
            object = [[FolderSource alloc] init];      
            break;
          case SourceSmartFolderType:
            object = [[SmartFolderSource alloc] init];      
            break;
	  case SourceNoteType:
	    /* Fix old version */
	    if ([self uniqueNumberAtIndex: i] == NSNotFound)
	      {
		LibrarySource *library = [LibrarySource sharedSource];
		unsigned int index = [library indexOfTitle: [self titleAtIndex: i]];
	        unsigned int uid = [library uniqueNumberAtIndex: index];
		[self setUniqueNumber: uid atIndex: i];
	      }
	    continue;
          case SourceSearchType: /* never load */
          default:
            continue;
        }
      [object loadSourceAtPath: loadPath];
      [self setContainer: object atIndex: i];
    } 
}

/* called in applicationWillFinishLaunching */
- (BOOL) loadSourceAtPath: (NSString *) path
{
  BOOL loadResult, isDir, rebuildSource = YES;
  NSFileManager *fm = [NSFileManager defaultManager];

  if ([super loadSourceAtPath: path] == NO) return NO;
  NSString *sourcePath = [self path];
  if (sourcePath == nil) return NO;

  if ([fm fileExistsAtPath: sourcePath isDirectory: &isDir])
    {
      if (isDir == YES)
        {
          int result = NSRunAlertPanel(sFileError, [NSString stringWithFormat: sSourceNotValid_, sourcePath], sRebuild, sCancelAndQuit, nil, nil);
	  if (result != NSAlertDefaultReturn)
	    {
	      return NO;
      	    }
	  if ([fm removeFileAtPath: sourcePath handler: nil] == NO)
	    {
      	      NSRunAlertPanel(sFileError, [NSString stringWithFormat: sCannotRebuildSource_, sourcePath], sQuit, nil, nil, nil);
	      return NO;
	    }
	  rebuildSource = YES;        
	}
      else        
        {
	  BOOL loadResult = [self readFromFile: sourcePath];
	  if (loadResult == NO)            
	    {              
	      int result = NSRunAlertPanel(sFileError, [NSString stringWithFormat: sSourceNotValid_, sourcePath], sRebuild, sCancelAndQuit, nil, nil);
	      if (result != NSAlertDefaultReturn)                
	        {
		  return NO;
		}              
	      if ([fm removeFileAtPath: sourcePath handler: nil] == NO)  
	        {                  
		  NSRunAlertPanel(sFileError, [NSString stringWithFormat: sCannotRebuildSource_, sourcePath], sQuit, nil, nil, nil);                  
		  return NO;               
		}            
	    }
          else            
	    {              
	      /* make a backup */              
	      makeBackup(sourcePath);              
	      rebuildSource = NO;            

	      [self _loadOtherSources];
	    }        
	}    
    }  
  else    
    {      
      /* Check backup file */
      NSString *backup = [sourcePath stringByAppendingString: @"~"];
      loadResult = [self readFromFile: backup];
      if (loadResult == YES)
        {
          int result = NSRunAlertPanel(sFileError, [NSString stringWithFormat: sFileNotExistWantBackup____, sSource, sourcePath, sSource, sSource], sYES, sNO, nil, nil);
          if (result == NSAlertDefaultReturn)
            {
	      [self _loadOtherSources];
	      return YES;
	    }
        }
      rebuildSource = YES;    
    }

  if (rebuildSource == YES)    
    {      
      [container removeAllObjects];
      [self newSource: sLibrary ofType: SourceLibraryType];
    }

  return YES;
}

/* Called in shouldTerminate */
- (BOOL) unloadSourceAtPath: (NSString *) absolutePath
{
  /* unload each Folder */
  unsigned i, count = [self count];
  unsigned int index;
  SourceType type;
  BOOL result;
  id object;
  NSString *path;

  /* remove search first */
  index = [self indexOfTitle: sSearch];
  [self removeObjectAtIndex: index];

  for (i = 0; i < count; i++)
    {
      type = [self typeOfSourceAtIndex: i];
      switch(type)
        {
          case SourceFolderType:
	  case SourceSmartFolderType:
            object = [self containerAtIndex: i];
	    if (object == nil)
	      {
	        NSLog(@"internal Error: SourceResourceName exist, but there is no SourceContainer.");
	        continue;
	      }
	    path = [self resourceFullPathAtIndex: i];
	    result = [object unloadSourceAtPath: path];
	    if (result == NO) return NO;
	    [self removeContainerAtIndex: i];
	    break;
	}
    }

  /* Always saved to fixed file */
  if ([super unloadSourceAtPath: nil] == NO) 
    {
      int result = NSRunAlertPanel(sFileErrorWrite, [NSString stringWithFormat: sCannotSaveSource_, [self path]], sDontQuit, sQuit, nil, nil);
      if (result == NSAlertDefaultReturn)
        return NO;
    }
  return YES;
}

@end

