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

   Copyright (C) 2002-2004 Free Software Foundation

   Authors: This code is heavily based on Ali Ozers TextFinder. 
             All credits belong to Ali!

   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 "PCTextFinder.h"
#include "PCDefines.h"

#include "PCTextFinder+UInterface.h"

#define Forward YES
#define Backward NO

#define ReplaceAllScopeEntireFile 42
#define ReplaceAllScopeSelection 43

@interface NSString (NSStringTextFinding)

- (NSRange)findString:(NSString *)string 
        selectedRange:(NSRange)selectedRange 
	      options:(unsigned)options 
	         wrap:(BOOL)wrap;

@end

@implementation NSString (NSStringTextFinding)

- (NSRange)findString:(NSString *)string 
        selectedRange:(NSRange)selectedRange 
	      options:(unsigned)options 
	         wrap:(BOOL)wrap
{
    BOOL forwards = (options & NSBackwardsSearch) == 0;
    unsigned length = [self length];
    NSRange searchRange, range;

    if (forwards) 
    {
        searchRange.location = NSMaxRange(selectedRange);
        searchRange.length = length - searchRange.location;
        range = [self rangeOfString:string options:options range:searchRange];

        if ((range.length == 0) && wrap) 
	{
            searchRange.location = 0;
            searchRange.length = selectedRange.location;
            range=[self rangeOfString:string options:options range:searchRange];
        }
    } 
    else 
    {
        searchRange.location = 0;
        searchRange.length = selectedRange.location;
        range = [self rangeOfString:string options:options range:searchRange];

        if ((range.length == 0) && wrap) 
	{
            searchRange.location = NSMaxRange(selectedRange);
            searchRange.length = length - searchRange.location;
            range=[self rangeOfString:string options:options range:searchRange];
        }
    }
    return range;
}

@end

@implementation PCTextFinder

static PCTextFinder *_finder = nil;

+ (PCTextFinder*)sharedFinder
{
    if( _finder == nil )
    {
        _finder = [[PCTextFinder alloc] init];
    }

    return _finder;
}

- (id)init
{
    if((self = [super init]))
    {
	shouldReplaceAll = YES;
        shouldIgnoreCase = YES;

        [self setFindString:@""];
	[self loadFindStringFromPasteboard];
    }
    return self;
}

- (void)dealloc
{
    if( panel != nil )
    {
        RELEASE(panel);
    }

    [super dealloc];
}

- (NSPanel *)findPanel
{
    if( panel == nil )
    {
        [self _initUI];
    }

    return panel;
}

- (void)showFindPanel:(id)sender
{
    if( panel == nil )
    {
        [self _initUI];
    }

    [panel orderFront:self];
}

- (void)buttonPressed:(id)sender
{
    switch([[sender selectedCell] tag] )
    {
        case 0:
	    [self replaceAll:sender];
	    break;
        case 1:
	    [self replace:sender];
	    break;
        case 2:
	    [self findPrevious:sender];
	    break;
        case 3:
	    [self findNext:sender];
	    break;
    }
}

- (void)setReplaceAllScope:(id)sender
{
    switch([[sender selectedCell] tag] )
    {
        case 0:
	    shouldReplaceAll = YES;
	    break;
        case 1:
	    shouldReplaceAll = NO;
	    break;
    }
}

- (void)setIgnoreCase:(id)sender
{
    if( [ignoreCaseButton state] )
    {
        shouldIgnoreCase = YES;
    }
    else
    {
        shouldIgnoreCase = NO;
    }
}

- (BOOL)find:(BOOL)direction
{
    NSTextView *text = [self textObjectToSearchIn];
    lastFindWasSuccessful = NO;

    if (text) 
    {
        NSString *textContents = [text string];
        unsigned textLength;

        if (textContents && (textLength = [textContents length])) 
        {
            NSRange range;
            unsigned options = 0;

            if (direction == Backward) options |= NSBackwardsSearch;
            if (shouldIgnoreCase) options |= NSCaseInsensitiveSearch;

            range = [textContents findString:[self findString] 
                               selectedRange:[text selectedRange] 
                                     options:options 
                                        wrap:YES];

            if (range.length) 
            {
                [text setSelectedRange:range];
                [text scrollRangeToVisible:range];
                lastFindWasSuccessful = YES;
            }
        }
    }

    if (!lastFindWasSuccessful) 
    {
        NSBeep();

        [statusField setStringValue:@"Not found"];
    } 
    else 
    {
        [statusField setStringValue:@""];
    }

    return lastFindWasSuccessful;
}

- (NSTextView *)textObjectToSearchIn
{
    id obj = [[NSApp mainWindow] firstResponder];
    return (obj && [obj isKindOfClass:[NSTextView class]]) ? obj : nil;
}

- (NSString *)findString
{
    return findString;
}

- (void)setFindString:(NSString *)string
{
    if ([string isEqualToString:findString]) return;

    AUTORELEASE(findString);
    findString = [string copyWithZone:[self zone]];

    if (findTextField) 
    {
        [findTextField setStringValue:string];
        [findTextField selectText:nil];
    }
    findStringChangedSinceLastPasteboardUpdate = YES;
}
    
- (void)loadFindStringFromPasteboard
{
    NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];

    if ([[pasteboard types] containsObject:NSStringPboardType]) 
    {
        NSString *string = [pasteboard stringForType:NSStringPboardType];

        if (string && [string length]) 
	{
            [self setFindString:string];
            findStringChangedSinceLastPasteboardUpdate = NO;
        }
    }
}

- (void)loadFindStringToPasteboard
{
    NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];

    if (findStringChangedSinceLastPasteboardUpdate) 
    {
        [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] 
	                   owner:nil];
        [pasteboard setString:[self findString] forType:NSStringPboardType];

        findStringChangedSinceLastPasteboardUpdate = NO;
    }
}


- (void)findNext:(id)sender
{
    if (findTextField) 
    {
        [self setFindString:[findTextField stringValue]];        
    }

    [self find:Forward];
}

- (void)findPrevious:(id)sender
{
    if (findTextField) 
    {
        [self setFindString:[findTextField stringValue]];        
    }

    [self find:Backward];
}

- (void)findNextAndOrderFindPanelOut:(id)sender;
{
}

- (void)replace:(id)sender
{
    NSTextView *text = [self textObjectToSearchIn];

    if (!text) 
    {
        NSBeep();
	[statusField setStringValue:@"No text!"];
    } 
    else 
    {
        [[text textStorage] replaceCharactersInRange:[text selectedRange] 
                                          withString:[replaceTextField stringValue]];
        [text didChangeText];
    }

    [statusField setStringValue:@""];
}

- (void)replaceAll:(id)sender;
{
    NSTextView *text = [self textObjectToSearchIn];

    if (!text) 
    {
        NSBeep();
    } 
    else 
    {
        NSTextStorage *textStorage = [text textStorage];
        NSString *textContents = [text string];
        NSString *replaceString = [replaceTextField stringValue];
        unsigned replaced = 0;
        NSRange replaceRange = shouldReplaceAll 
	                       ? NSMakeRange(0, [textStorage length]) 
			       : [text selectedRange];
        unsigned options = NSBackwardsSearch |
	                   ( shouldIgnoreCase ? NSCaseInsensitiveSearch : 0);

        if (findTextField) [self setFindString:[findTextField stringValue]];

        while (1) 
	{
            NSRange foundRange = [textContents rangeOfString:[self findString] 
	                                             options:options 
						       range:replaceRange];

            if (foundRange.length == 0) 
	    {
	        break;
	    }

            if ([text shouldChangeTextInRange:foundRange replacementString:replaceString]) 
	    {
                if (replaced == 0) 
		{
		    [textStorage beginEditing];
		}

                replaced++;
                [textStorage replaceCharactersInRange:foundRange 
		                           withString:replaceString];
                replaceRange.length = foundRange.location - replaceRange.location;
            }
        }

	// There was at least one replacement
        if (replaced > 0) 
	{     
            [textStorage endEditing];   
            [text didChangeText];       

            [statusField setStringValue:[NSString stringWithFormat:@"%d replaced", replaced]];
        } 
	else 
	{        
            NSBeep();
            [statusField setStringValue:@"Not found"];
        }
    }
}

@end
