/*
**  SmartFolderSource.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 "SmartFolderSource.h"
#include "Sources.h"
#include "LibrarySource.h"
#include "AttributesSource.h"
#include "GNUstep.h"
#include "Criterion.h"
#include "ContentIndexer.h"
#include "TimeStamp.h"

@implementation SmartFolderSource

- (void) _refreshWithSources: (BOOL) withSource
{
  Sources *sources = [Sources sharedSources];
  SourceType sourceType;
  LibrarySource *library = [LibrarySource sharedSource];
  AttributesSource *attributes = [AttributesSource sharedSource];
  NSString *title, *path, *key, *object;
  NSDictionary *attr;
  NSEnumerator *e;
  id o, value;
  BOOL matchSuccessfully;

  unsigned int index;
  SourceType type;
  unsigned int i, icount;
  unsigned int j, jcount;
  unsigned int uid;
  unsigned attrIndex;
  NSMutableDictionary *allInformation = [[NSMutableDictionary alloc] init];
  NSMutableArray *allSources;
  id source;

  icount = [library count];
  //NSLog(@"icount %d", icount);
  for(i = 0; i < icount; i++)
    {
      [allInformation removeAllObjects];
      /* Title */
      title = [library titleAtIndex: i];
      uid = [library uniqueNumberAtIndex: i];
      [allInformation setObject: title forKey: sTitle];
      index = i;

      //if ([from isEqualToString: sLibrary] == NO)
      if (withSource)
        {
	  allSources = [NSMutableArray array];
	  jcount = [sources count];
	  for(j = 0; j < jcount; j++)
	    {
	      sourceType = [sources typeOfSourceAtIndex: j];
	      switch(sourceType)
	        {
		  case SourceFolderType:
		  case SourceSmartFolderType:
		    source = [sources containerAtIndex: j];
		    if ([source indexOfTitle: title] != NSNotFound)
		      {
		        [allSources addObject: [sources titleAtIndex: j]];
		      }
		    break;
		  default: 
		    continue;
	        }
	    }
          [allInformation setObject: allSources forKey: sSource];
	}

      /* Note and Render*/
      object = [library noteAtIndex: index];
      if (object)
        {
	  object = [object stringByAppendingString: [[library renderedNoteAtIndex: index] string]];
          [allInformation setObject: object forKey: sNote];
	}

      /* File name */
      path = [library pathOfItemAtIndex: index];
      if (path)
        {
	  attrIndex = [attributes indexOfTitle: path];
          object = [path lastPathComponent];
          if (object)
            [allInformation setObject: object forKey: sFilename];

	  /* File content, send full path */
	  object = [library fullPathOfItemAtIndex: index];
	  if (object)
            [allInformation setObject: object forKey: sFileContent];
	}

      /* type */
      object = [library typeStringOfItemAtIndex: index];
      if (object)
        [allInformation setObject: object forKey: sFiletype];

      /* All attributes */
      attr = [attributes attributesAtIndex: attrIndex];
      e = [[attr allKeys] objectEnumerator];
      while ((o = [e nextObject]))
        {
	  key = nameOfAttributeKey(o);
          /* Add prefix "- " to indicates it is attribute */
          key = [@"- " stringByAppendingString: key];
	  value = [attr objectForKey: o];
	  [allInformation setObject: value forKey: key];
        }

      //NSLog(@"SmartSource match %@", title);
      matchSuccessfully = [criterion match: allInformation];
      if (matchSuccessfully == YES)
        {
	  //NSLog(@"success");
	  if ([self isDuplicatedTitle: title] == NO)
	    {
	      index = [self newTitle: title];
	      [self setUniqueNumber: uid atIndex: index];
	    }
        }
    }
  RELEASE(allInformation);
}

- (void) refreshAll
{
  /* Check whether it is refreshed already */
  if ([[TimeStamp timeStampWithIdentifier: LibraryEditedTimeStamp] isBeforeTime: lastRefreshTime])
    {
      //NSLog(@"Library doesn't change");
      return;
    }

  if ([lock tryLock] == NO) return;

  ASSIGN(lastRefreshTime, [NSDate date]);

  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSArray *subjectsToMatch;
  [container removeAllObjects];

  subjectsToMatch = [criterion subjectsToMatch];

  if ([subjectsToMatch containsObject: sFileContent])
    {
      /* Set up content index */
      NSString *path = [MyLibraryPath(nil) stringByAppendingPathComponent: ContentIndexDirectory];
      //NSLog(@"SmartFolderSource: setLocation %@", path);
      [[ContentIndexer sharedIndexer] setIndexLocation: path];
    }

  if ([subjectsToMatch containsObject: sSource])
    {
      /* refresh other source first */
      id source= nil;
      SourceType type;
      Sources *sources = [Sources sharedSources];
      unsigned int i, count = [sources count];
      for (i = 0; i < count; i++)
        {
	  type = [sources typeOfSourceAtIndex: i];
	  if (type == SourceSmartFolderType)
	    {
	      source= [sources containerAtIndex: i];
	      if ((source) && (source != self))
	        [source refreshAll];
	    }
        }
      [self _refreshWithSources: YES];
    }
  else
    {
      [self _refreshWithSources: NO];
    }

  RELEASE(pool);
  [lock unlock];
}

- (BOOL) readFromFile: (NSString *) absolutePath
{
  NSData *data = [NSData dataWithContentsOfFile: absolutePath];
  if (data == nil)
    return NO;
       
  NSString *err;
  id propertyList = [NSPropertyListSerialization propertyListFromData: data
                                mutabilityOption: NSPropertyListImmutable
                                format: NULL
                                errorDescription: &err];
  if (err)                      
    {
       NSLog(@"General container: Read Error \"%@\"", err);
       RELEASE(err);
       return NO;
    }

  if (propertyList)
    {
      [criterion loadPropertyList: propertyList];
      addAvailableSubjectsIntoCriterion(criterion);
      return YES;
    }
  else
    return NO;
}

- (BOOL) loadSourceAtPath: (NSString *) path
{
  if ([super loadSourceAtPath: path] == NO) return NO;
  NSString *folderPath = [self path];
  BOOL loadResult;

  loadResult = [self readFromFile: folderPath];
  if (loadResult == YES) 
    {
      makeBackup(folderPath);
      /* Remove old one in case the name changes */
      NSFileManager *fm = [NSFileManager defaultManager];
      [fm removeFileAtPath: folderPath handler: nil];
      return YES;
    }
  else
    {
      /* Check backup file */
      NSFileManager *fm = [NSFileManager defaultManager];
      BOOL isDir;
      NSString *backup = [folderPath stringByAppendingString: @"~"];
      loadResult = [self readFromFile: backup];
      if (loadResult == YES)
        {
          int result = NSRunAlertPanel(sFileError, [NSString stringWithFormat: sFileNotExistWantBackup____, sSmartFolder, folderPath, sSmartFolder, sSmartFolder], sYES, sNO, nil, nil);
          if (result == NSAlertDefaultReturn)
            {
              return YES;
            }
	}
    }

  /* nil will clean all criterions */
  [criterion loadPropertyList: nil];
  return YES;
}

- (BOOL) writeToFile: (NSString *) absolutePath
{
  id object = [criterion propertyList];
  if (object == nil)
    {
      NSLog(@"Internal Error: No criterion property list");
      return NO;
    }

  NSString *err = nil;
  NSData *data = [NSPropertyListSerialization dataFromPropertyList: object
                               format: NSPropertyListXMLFormat_v1_0
                               errorDescription: &err];
  if (err)
    {
       NSLog(@"SmartFolderSource: Write Error %@", err);
       RELEASE(err);
       return NO;
    }
  return [data writeToFile: absolutePath atomically: YES];
}

- (BOOL) unloadSourceAtPath: (NSString *) path
{
  BOOL loadResult = [self writeToFile: path];
  if (loadResult == NO)
    {
      NSLog(@"Unload failed %@", path);
      return NO;
    }

  if (([self path]) && ([[self path] isEqualToString: path] == NO))
    {
      /* Load and save file in different name.
       * Move the backup file to new name */
      NSString *extension = [[self path] pathExtension];
      NSString *oldPath = [[self path] stringByAppendingString: @"~"];
      NSString *newPath = [[self path] stringByDeletingLastPathComponent];
      newPath = [newPath stringByAppendingPathComponent: [[path lastPathComponent] stringByAppendingString: @"~"]];
      //NSLog(@"Move backup %@", newPath);

      NSFileManager *fm = [NSFileManager defaultManager];
      return [fm movePath: oldPath toPath: newPath handler: nil];
    }
  return YES;
  /* Don't call superclass because smart fonder
   * don't really save the list of notes and files.
   * It use criterion to dynamically generate new one.
   *
   * return [super unloadSourceAtPath: path];
   */
}

- (Criterion *) criterion
{
  return criterion;
}

- (id) init
{
  self = [super init];
  criterion = [[Criterion alloc] init];
  lock = [[NSLock alloc] init];
  ASSIGN(lastRefreshTime, [NSDate date]);
  return self;
}

- (void) dealloc
{
  RELEASE(criterion);
  RELEASE(lock);
  RELEASE(lastRefreshTime);
  [super dealloc];
}

@end
