/* 
   Project: MPDCon

   Copyright (C) 2004

   Author: Daniel Luederwald

   Created: 2004-05-12 17:59:14 +0200 by flip
   
   Find Controller

   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 <AppKit/AppKit.h>
#include "FindController.h"

/* ---------------------
   - Private Interface -
   ---------------------*/
@interface FindController(Private)
- (void) _doFindForward: (BOOL)forward;
- (BOOL) _findString: (NSString *)searchString in: (NSArray *)strings 
	     options: (int)options wholeString: (int)whole;
- (void) _fetchTracksFrom: (NSString *)target;
@end

@implementation FindController

/* --------------------------
   - Initialization Methods -
   --------------------------*/

+ (id) sharedFindController
{
  static FindController *_sharedFindController = nil;

  if (! _sharedFindController) 
    {
      _sharedFindController = [[FindController allocWithZone: 
						 [self zone]] init];
    }

  return _sharedFindController;
}

- (id) init
{
  self = [self initWithWindowNibName: @"FindPanel"];

  if (self)
    {
      [self setWindowFrameAutosaveName: @"FindPanel"];
    }

  return self;
}

/* ---------------
   - Gui Methods -
   ---------------*/

- (void) awakeFromNib
{
  NSNotificationCenter *defCenter;

  defCenter = [NSNotificationCenter defaultCenter];

  [defCenter addObserver: self
	        selector: @selector(playlistChanged:)
	            name: PlaylistChangedNotification
	          object: nil];

  [defCenter addObserver: self
	        selector: @selector(collectionChanged:)
	            name: ShownCollectionChangedNotification
	          object: nil];
  
  [defCenter addObserver: self
	        selector: @selector(doFind:)
	            name: DoFindNotification
	          object: nil];
  
  [defCenter addObserver: self
	        selector: @selector(doFind:)
	            name: DoFindNotification
	          object: nil];
  
  [defCenter addObserver: self
	        selector: @selector(tracksReceived:)
	            name: TracksSendNotification
	          object: nil];
  
  lastSearch = -1;
  
  [self _fetchTracksFrom: GetTracksFromPlaylist];
}

- (void) findNext: (id)sender
{
  [self _doFindForward: YES];
}


- (void) findPrevious: (id)sender
{
  [self _doFindForward: NO];
}

- (void) optionsChanged: (id) sender
{
  NSNotification *aNotif;

  lastSearch = -1;
  [notFoundField setStringValue: @""];

  aNotif = [NSNotification notificationWithName: FindPerformedNotification
			                 object: 
			                     [NSNumber numberWithBool: NO]];

  [[NSNotificationCenter defaultCenter] postNotification: aNotif];

  if (sender == targetMatrix) 
    {
      if ([targetMatrix selectedRow] == 1) 
	{
	  [self _fetchTracksFrom: GetTracksFromPlaylist];
	} 
      else 
	{
	  [self _fetchTracksFrom: GetTracksFromCollection];
	}
    }
}

/* ------------------------
   - Notification Methods -
   ------------------------*/

- (void) playlistChanged: (NSNotification *)aNotif
{
  if ([targetMatrix selectedRow] == 1) 
    {
      NSNotification *aNotif;

      aNotif = [NSNotification notificationWithName: FindPerformedNotification
			                     object:
				               [NSNumber numberWithBool: NO]];

      [[NSNotificationCenter defaultCenter] postNotification: aNotif];
      
      lastSearch = -1;
      
      [self _fetchTracksFrom: GetTracksFromPlaylist];
    }
}

- (void) collectionChanged: (NSNotification *)aNotif
{
  if ([targetMatrix selectedRow] == 0) 
    {
      NSNotification *aNotif;

      aNotif = [NSNotification notificationWithName: FindPerformedNotification 
			                     object: 
				 [NSNumber numberWithBool: NO]];
			       
      [[NSNotificationCenter defaultCenter] postNotification: aNotif];

      lastSearch = -1;
      [self _fetchTracksFrom: GetTracksFromCollection];
    }
}

- (void) controlTextDidBeginEditing: (NSNotification *)aNotif
{
  NSNotification *oNotif;

  oNotif = [NSNotification notificationWithName: FindPerformedNotification 
			                 object: 
			                   [NSNumber numberWithBool: NO]];

  [[NSNotificationCenter defaultCenter] postNotification: oNotif];

  lastSearch = -1;
}

- (void) tracksReceived: (NSNotification *)aNotif
{
  allTracks = RETAIN([aNotif object]);
}

- (void) doFind: (NSNotification *)aNotif
{
  if ([[aNotif object] isEqual: FindNext])
    {
      [self findNext: self];
    }
  else
    {
      [self findPrevious: self];
    }
}
@end

/* -------------------
   - Private Methods -
   -------------------*/

@implementation FindController(Private)
- (void) _doFindForward: (BOOL)forward
{
  int target;
  int foundNr;

  int scope;
  int ignoreCase, wholeWord;

  int start, end, step;
  int i;

  if (([findField stringValue] == nil) 
      || ([[findField stringValue] isEqual: @""]))
    {
      return;
    }

  target = [targetMatrix selectedRow];

  if (allTracks == nil) 
    {
      [notFoundField setStringValue: _(@"Not found")];
      return;
    }

  foundNr = -1;

  scope = [[scopeMatrix selectedCell] tag];
  
  ignoreCase = ([[optionsMatrix cellWithTag: 0] state] == 1) 
    ? NSLiteralSearch: NSCaseInsensitiveSearch;
  
  wholeWord = [[optionsMatrix cellWithTag: 1] state];
  
  start = 0;
  end = 0;
  step = 0;
  i = 0;

  if (forward) 
    {
      if (lastSearch == [allTracks count]) 
	{
	  start = lastSearch;
	}
      else
	{
	  start = lastSearch+1;
	}
      
      end = [allTracks count];
      step = 1;
    } 
  else 
    {
      if (lastSearch <= 0 )
	{
	  start = -1;
	}
      else
	{
	  start = lastSearch-1;
	}
      
      end = -1;
      step = -1;
    }
  
  for (i = start; i != end; i+=step) 
    {
      NSMutableArray *tmpStrings;
      
      tmpStrings = [[NSMutableArray alloc] init];
    
      if ((scope == 0) || (scope == 3)) 
	{
	  [tmpStrings addObject: [[allTracks objectAtIndex: i] getTitle]];
	}
      
      if ((scope == 0) || (scope == 2))
	{
	  [tmpStrings addObject: [[allTracks objectAtIndex: i] getAlbum]];
	}
    
      if ((scope == 0) || (scope == 1)) 
	{
	  [tmpStrings addObject: [[allTracks objectAtIndex: i] getArtist]];
	}

    if ([self _findString: [findField stringValue] 
	               in: AUTORELEASE(tmpStrings) 
	          options: ignoreCase 
	      wholeString: wholeWord])
      {
	foundNr = i;
	break;
      }				     
    }
  
  if (foundNr != -1) 
    {
      NSNotification *aNotif;

      lastSearch = i;
    
      if (target == 0) 
	{
	  aNotif = [NSNotification notificationWithName: 
				     FindCollectionNotification 
				                 object: 
				                  [NSNumber numberWithInt: i]];

	  [[NSNotificationCenter defaultCenter]  postNotification: aNotif];
	} 
      else 
	{
	  aNotif = [NSNotification notificationWithName: 
				     FindPlaylistNotification 
				                 object: 
				                  [NSNumber numberWithInt: i]];
	  
	  [[NSNotificationCenter defaultCenter] postNotification: aNotif];
	}
      
      [notFoundField setStringValue: @""];
      
      aNotif = [NSNotification notificationWithName: FindPerformedNotification 
			                     object: 
				              [NSNumber numberWithBool: YES]];
      
      [[NSNotificationCenter defaultCenter] postNotification: aNotif];
    }
  else
    {
      [notFoundField setStringValue: _(@"Not found")];
    }
}

- (BOOL) _findString: (NSString *)searchString in: (NSArray *)strings 
	     options: (int)options wholeString: (int)whole
{
  BOOL found ;
  int i;

  RETAIN(strings);

  found = NO;

  for (i = 0; i < [strings count]; i++) 
    {
      if (whole == 0) 
	{
	  NSRange range;

	  range = [[strings objectAtIndex: i] rangeOfString: searchString 
					            options: options];

	  if (range.location != NSNotFound) 
	    {
	      found = YES;
	      break;
	    }
	} 
      else 
	{
	  if ([[strings objectAtIndex: i] compare: searchString 
					  options: options] == NSOrderedSame)
	    {
	      found = YES;
	      break;
	    }
	}
    }
  
  RELEASE(strings);

  return found;
}

- (void) _fetchTracksFrom: (NSString *)target
{
  NSNotification *aNotif;
  if (allTracks)
    {
      RELEASE(allTracks);
    }
  
  aNotif = [NSNotification notificationWithName: GetTracksNotification 
			                 object: target];

  [[NSNotificationCenter defaultCenter] postNotification: aNotif];
}
@end
