/*
**  ContentIndexer.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 "ContentIndexer.h"
#include "GNUstep.h"
#include "Constants.h"
#include "RegEx.h"

static ContentIndexer *sharedInstance;

@implementation ContentIndexer

/* main search */
- (NSArray *) filesContain: (NSString *) pattern
{
  if ((pattern == nil) || (indexLocation == nil) ||
      (namazuLocation ==  nil)) return;

  if ([pattern isEqualToString: cachePattern])
    return AUTORELEASE([cacheFiles copy]);

  //NSLog(@"search %@", pattern);
  NSTask *task = [[NSTask alloc] init];
  NSPipe *outputPipe = [NSPipe pipe];
  NSFileHandle *handle = [outputPipe fileHandleForReading];
  NSData *data = nil;
  NSMutableArray *args = [NSMutableArray array];
  NSArray *files = nil;
  NSMutableString *result = [[NSMutableString alloc] init];
  NSString *output;
  [args addObject: @"-l"];
  [args addObject: @"-a"];
  [args addObject: pattern];
  [args addObject: indexLocation];
  //NSLog(@"namazuLocation %@", namazuLocation);
  //NSLog(@"indexLocation %@", indexLocation);
  [task setLaunchPath: namazuLocation];
  [task setArguments: args];
  [task setStandardOutput: outputPipe];
  [task launch];
  while((data = [handle availableData]) && ([data length]))
    {
      output = [[NSString alloc] initWithData: data
	                         encoding: [NSString defaultCStringEncoding]];
      [result appendString: output];
      //NSLog(@"Namazu %@", output);
      RELEASE(output);
    }

  //NSLog(@"content index result %@", result);
  files = [result componentsSeparatedByString: @"\n"];
  //NSLog(@"files %@", files);

  RELEASE(result);
  if ((files) && ([files count] > 0))
    {
      ASSIGN(cachePattern, AUTORELEASE([pattern copy]));
      [cacheFiles setArray: files];
      return AUTORELEASE([files copy]);
    }
  else
    return nil;
}


/* window delegate */
- (BOOL) windowShouldClose: (id) sender
{
  [NSApp stopModal];

  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  if (mknmzLocation)
    [defaults setObject: mknmzLocation forKey: MKNMZLocationPath];
  if (namazuLocation)
    [defaults setObject: namazuLocation forKey: NamazuLocationPath];

  return YES;
}

/* accessaries */

- (void) setIndexLocation: (NSString *) path
{
  if ([indexLocation isEqualToString: path]) return;
  ASSIGN(indexLocation, path);
}

/* Action */

- (NSString *) _locationFromOpenPanel
{
  int result;
  NSOpenPanel *panel = [NSOpenPanel openPanel];
  [panel setCanChooseDirectories: NO];
  [panel setAllowsMultipleSelection: NO];
  [panel setResolvesAliases: YES];
  result = [panel runModalForTypes: nil];
  if (result == NSOKButton)
    {
      return [[panel filenames] objectAtIndex: 0];
    }
  else
    return nil;
}

- (IBAction)mknmzFindButtonAction:(id)sender
{
  NSString *path = [self _locationFromOpenPanel];
  if (path)
    {
      [mknmzTextField setStringValue: path];
      ASSIGN(mknmzLocation, path);
    }
}

- (IBAction)mknmzLocationTextFieldAction:(id)sender
{
  BOOL isDir;
  NSString *path = [[sender stringValue] stringByStandardizingPath];
  NSFileManager *fm = [NSFileManager defaultManager];
  if (([fm fileExistsAtPath: path isDirectory: &isDir]) &&
      (isDir == NO))
    {
      ASSIGN(mknmzLocation, path);
    }
  else
    {
      NSBeep();
      [sender setStringValue: @"File does not exist"];
    }
}

- (IBAction)namazuFindButtonAction:(id)sender
{
  NSString *path = [self _locationFromOpenPanel];
  if (path)
    {
      [namazuTextField setStringValue: path];
      ASSIGN(namazuLocation, path);
    }
}

- (IBAction)namazuLocationTextFieldAction:(id)sender
{
  BOOL isDir;
  NSString *path = [[sender stringValue] stringByStandardizingPath];
  NSFileManager *fm = [NSFileManager defaultManager];
  if (([fm fileExistsAtPath: path isDirectory: &isDir]) &&
      (isDir == NO))
    {
      ASSIGN(namazuLocation, path);
    }
  else
    {
      NSBeep();
      [sender setStringValue: @"File does not exist"];
    }
}

- (IBAction)rebuildContentIndexButtonAction:(id)sender
{
  if (indexLocation == nil) return;
  //NSLog(@"rebuild index %@:", indexLocation);
  //NSLog(@"1. create index directory");
  BOOL result;
  NSFileManager *fm = [NSFileManager defaultManager];
  [rebuildLabel setStringValue: @""];
  [rebuildLabel display];
  [rebuildProgressIndicator startAnimation: self];
  [rebuildLabel setStringValue: @"Creating index directory..."];
  [rebuildLabel display];
  if ([fm fileExistsAtPath: indexLocation])
    {
      /* isDir is checked in -makeIndexKeyAndOrderFront */
      [rebuildLabel setStringValue: @"Index directory existed."];
      [rebuildLabel display];
    }
  else 
    {
      if ([fm createDirectoryAtPath: indexLocation attributes: nil] == NO)
        {
          [rebuildLabel setStringValue: @"Failed to create index directory."]; 
          [rebuildLabel display];
	  return;
        }
      else
        {
	  [rebuildLabel setStringValue: @"Index directory is created."];
          [rebuildLabel display];
        }
    }

  [rebuildLabel setStringValue: @"Creating indexes..."];
  [rebuildLabel display];
  //NSLog(@"2. create index");
  NSTask *task = [[NSTask alloc] init];
  NSPipe *outputPipe = [NSPipe pipe];
  NSFileHandle *handle = [outputPipe fileHandleForReading];
  NSData *data = nil;
  NSMutableArray *args = [NSMutableArray array];
  NSMutableDictionary *env = [NSMutableDictionary dictionaryWithDictionary: [[NSProcessInfo processInfo] environment]];
  [env setObject: @"0" forKey: @"PERL_HASH_SEED"];
#ifndef GNUSTEP 
  /* Add local path for xpdf */
  NSString *path = [[env objectForKey: @"PATH"] stringByAppendingString: @":/usr/local/bin"];
  [env setObject: path forKey: @"PATH"];
#endif
  NSString *output;
  NSRange range;
  [args addObject: @"-z"]; /* Check filesize changed */
  [args addObject: @"-O"];
  [args addObject: indexLocation];
  [args addObject: [indexLocation stringByDeletingLastPathComponent]];
  //NSLog(@"mknmzLocation %@", mknmzLocation);
  [task setLaunchPath: mknmzLocation];
  [task setArguments: args];
  [task setStandardOutput: outputPipe];
  [task setEnvironment: env];
  //[env writeToFile: @"/tmp/MyLibrar.env" atomically: YES];
  [task launch];
  while((data = [handle availableData]) && ([data length]))
    {
      output = [[NSString alloc] initWithData: data
	                         encoding: [NSString defaultCStringEncoding]];
      //NSLog(@"MKNMZ %@", output);
      range = [RegExParser rangeOfString: @"[0-9]*/[0-9]* -"
	                        inString: output];
      if (range.location != NSNotFound)
        {
          [rebuildLabel setStringValue: [output substringWithRange: range]];
          [rebuildLabel display];
	}
      RELEASE(output);
    }
  /* Check validaion of index */
  NSString *NMZ_i = [indexLocation stringByAppendingPathComponent: @"NMZ.i"];
  if ([fm fileExistsAtPath: NMZ_i])
    {
      [rebuildLabel setStringValue: @"Successfully created indexes."];
      [rebuildLabel display];
    }
  else
    {
      [rebuildLabel setStringValue: @"Failed to create indexes."];
      [rebuildLabel display];
    }

  /* garbage index */
  //NSLog(@"3. garbage clean index");
  task = [[NSTask alloc] init];
  outputPipe = [NSPipe pipe];
  handle = [outputPipe fileHandleForReading];
  data = nil;
  args = [NSMutableArray array];
  [rebuildLabel setStringValue: @"Clean up indexes..."];
  [rebuildLabel display];
  [args addObject: indexLocation];
  [task setLaunchPath: [[mknmzLocation stringByDeletingLastPathComponent] stringByAppendingPathComponent: @"gcnmz"]];
  [task setArguments: args];
  [task setStandardOutput: outputPipe];
  [task setEnvironment: env];
  [task launch];
  while((data = [handle availableData]) && ([data length]))
    {
      output = [[NSString alloc] initWithData: data
	                         encoding: [NSString defaultCStringEncoding]];
      //NSLog(@"MKNMZ %@", output);
      RELEASE(output);
    }
  [rebuildLabel setStringValue: @"Indexes are successfully built"];
  [rebuildLabel display];

  /* Clean cache */
  DESTROY(cachePattern);
  [cacheFiles removeAllObjects];

  [rebuildProgressIndicator stopAnimation: self];
}

- (IBAction)removeContentIndexButtonAction:(id)sender
{
  if (indexLocation == nil) return;
  //NSLog(@"remove index");
  NSFileManager *fm = [NSFileManager defaultManager];
  [removeProgressIndicator startAnimation: self];
  BOOL result = [fm removeFileAtPath: indexLocation
	                     handler: nil];
  if (result == NO)
    {
      NSLog(@"Internal Error: Cannot remove index at %@", indexLocation);
      [removeLabel setStringValue: @"Failed to remove index directory"];
    }

  /* Clean cache */
  DESTROY(cachePattern);
  [cacheFiles removeAllObjects];

  [removeProgressIndicator stopAnimation: self];
}

- (IBAction)namazuButtonAction:(id)sender
{
  /* Open namazu website */
  [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: @"http://www.namazu.org"]];
}

- (void) awakeFromNib
{
  if (indexLocation == nil)
    {
      [indexLocationLabel setStringValue: @"No indexes"];
    }
  else
    {
      [indexLocationLabel setStringValue: indexLocation];
    }

  [rebuildProgressIndicator setUsesThreadedAnimation: YES];
  [removeProgressIndicator setUsesThreadedAnimation: YES];
}

- (void) makeIndexerKeyAndOrderFront: (id)sender
{
  if (indexPanel == nil)
    {
      BOOL result = [NSBundle loadNibNamed: @"ContentIndexingPanel" owner: self];
      if (result == NO)
        {
	  NSLog(@"Unable to load ContentIndexingPanel");
	  return;
        }
    }

  if (mknmzLocation)
    {
      [mknmzTextField setStringValue: mknmzLocation];
    }
  else
    {
      [mknmzTextField setStringValue: @"/usr/local/bin/mknmz"];
    }

  if (namazuLocation)
    {
      [namazuTextField setStringValue: namazuLocation];
    }
  else
    {
      [namazuTextField setStringValue: @"/usr/local/bin/namazu"];
    }

  /* Hide result */
  [rebuildLabel setStringValue: @""];
  [removeLabel setStringValue: @""];

  /* Check index directory */
  NSString *s;
  NSFileManager *fm = [NSFileManager defaultManager];
  BOOL isDir;
  if (indexLocation)
    {
      if ([fm fileExistsAtPath: indexLocation isDirectory: &isDir])
	{
          if (isDir == YES)
            {
	      s = indexLocation;
            }
          else
            {
	      NSRunAlertPanel(sFileError, [NSString stringWithFormat: sIndexDirectoryInvalid_, indexLocation], sOK, nil, nil, nil);
	      return;
	    }
        }
      else
        {
	  s = [NSString stringWithFormat: @"%@ (No indexes yet)", indexLocation];
        }
    }

  //NSLog(@"s %@", s);
  [indexLocationLabel setStringValue: s];
  [indexPanel makeKeyAndOrderFront: sender];
  [NSApp runModalForWindow: indexPanel];
}

+ (ContentIndexer *) sharedIndexer
{
  if (sharedInstance == nil)
    {
      sharedInstance = [[ContentIndexer alloc] init];
    }
  return sharedInstance;
}

- (id) init
{
  self = [super init];

  cacheFiles = [[NSMutableArray alloc] init];

  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSString *path = [defaults stringForKey: MKNMZLocationPath];
  if (path)
    {
      ASSIGN(mknmzLocation, path);
    }
  else
    {
      ASSIGN(mknmzLocation, @"/usr/local/bin/mknmz");
    }

  path = [defaults stringForKey: NamazuLocationPath];
  if (path)
    {
      ASSIGN(namazuLocation, path);
    }
  else
    {
      ASSIGN(namazuLocation, @"/usr/local/bin/namazu");
    }

  return self;
}

- (void) dealloc
{
  RELEASE(cacheFiles);
  [super dealloc];
}

@end
