/*
**  ObjCHandler.m
**
**  Copyright (c) 2003
**
**  Author: Yen-Ju  <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 "ObjCHandler.h"
#include <CodeEditorView/RulesetManager.h>
#include <AppKit/AppKit.h>

@implementation ObjCHandler

+ (NSString *) language
{
  return @"Objective-C";
}

+ (Class) blockHandlerClass
{
  return NSClassFromString(@"ObjCBlockHandler");
}

+ (Class) indentationHandlerClass
{
  return NSClassFromString(@"ObjCIndentHandler");
}

+ (Class) methodHandlerClass
{
  return NSClassFromString(@"ObjCMethodHandler");
}

#define STRCMP(str) (!strcmp(cstr, str))

- (void) string: (NSString *) element
{
  const char *cstr = [element cString];
  unsigned int len = [element length];
  NSRange attributeRange = NSMakeRange(position, len);
  BOOL changeAttribute = NO;
  NSDictionary *attr = normalAttr;

  [super string: element];

  /* Comments */
  if (_commentType != NoComment)
    {
      changeAttribute = YES;
      attr = commentsAttr;
    }
  else if (_stringBegin == YES)
    {
      changeAttribute = YES;
      attr = stringsAttr;
    }
  else if ( (*cstr > 0x40) && (*cstr < 0x58) ) /* Capital */
    {
      /* KnownType */
      if (  STRCMP("SEL") || STRCMP("BOOL") ||
            STRCMP("Class") || STRCMP("Nil") || STRCMP("IBAction") ||
            STRCMP("IBOutlet")
         )

       {
         changeAttribute = YES;
         attr = knowntypesAttr;
       }
     /* Strings */
     else if ( 
               STRCMP("FALSE") || STRCMP("NO") || STRCMP("TRUE") ||
               STRCMP("YES")
             )
       {
         changeAttribute = YES;
         attr = stringsAttr;
       }
     else if ( (*cstr == 'N') && (*(cstr+1) == 'S') )
       {
         changeAttribute = YES;
         attr = knowntypesAttr;
       }
    }
  else if (len < 10)
    {
      /* Preprocessor */
      if (_preChar == '#')
        {
          if (
               STRCMP("import") || STRCMP("include") || STRCMP("ifdef") ||
               STRCMP("ifndef") || /*STRCMP("if defined") ||*/
               STRCMP("else") ||
               STRCMP("endif") || STRCMP("pragma") || STRCMP("define") ||
               STRCMP("warning") || STRCMP("error")
             )
           {
             attributeRange = NSMakeRange(position-1, len+1);
             changeAttribute = YES;
             attr = preprocessorAttr;
           }
        }
      else if (_preChar == '@')
        {
          if (
               STRCMP("class") || STRCMP("selector") ||
               STRCMP("interface") ||
               STRCMP("end") || STRCMP("encode") ||
               STRCMP("private") || STRCMP("protected")
             )
           {
             changeAttribute = YES;
             attr = keywordsAttr;
           }
        }
      /* Keyword */
      else if ( 
           STRCMP("break") || STRCMP("extern") || STRCMP("continue") || 
           STRCMP("_cmd") || STRCMP("self") || STRCMP("super") || 
           STRCMP("return") || STRCMP("sizeof") || STRCMP("break") || 
           STRCMP("case") || STRCMP("default") || STRCMP("goto") || 
           STRCMP("switch") || STRCMP("if") || STRCMP("else") ||
           STRCMP("do") || STRCMP("shile") || STRCMP("for") || 
           STRCMP("while")
         )
       {
          changeAttribute = YES;
          attr = keywordsAttr;
        }
      /* KnownType */
      else if ( 
                STRCMP("int") || STRCMP("long") || STRCMP("short") ||
                STRCMP("char") || STRCMP("float") || STRCMP("double") ||
                STRCMP("void") || STRCMP("union") || STRCMP("unichar") ||
                STRCMP("const") || STRCMP("signed") || STRCMP("unsigned") ||
                STRCMP("static") || STRCMP("volatile") ||
                STRCMP("enum") || STRCMP("id")
               )
        {
          changeAttribute = YES;
          attr = knowntypesAttr;
        }
      /* Strings */
      else if ( STRCMP("nil") )
        {
          changeAttribute = YES;
          attr = stringsAttr;
        }
    }
  else if ((len == 14) && (_preChar == '@'))
    {
      if ( STRCMP("implementation") )
        {
           changeAttribute = YES;
           attr = keywordsAttr;
        }
    }

  position += len;
  _preChar = 0;

  if (changeAttribute)
    {
      attributeRange.location += _startRange.location;
#if 1
      [_origin addAttributes: attr 
                       range: attributeRange];
#else
      [_origin setAttributes: attr
                       range: attributeRange];
#endif
    }
}

- (void) number: (NSString *) element 
{
  [super number: element];
  position += [element length];
  _preChar = 0;
}

- (void) spaceAndNewLine: (unichar) element 
{
  [super spaceAndNewLine: element];
  position++;
  _preChar = element;
}

- (void) symbol: (unichar) element 
{
  NSRange attributeRange;
  NSDictionary *attr;

  [super symbol: element];

  if ( ((_preChar == '/') && ((element == '*') || (element == '/'))) ||
       ((_preChar == '*') && (element == '/'))
     )
    {
      attr = commentsAttr;
      attributeRange = NSMakeRange(position-1, 2);
      attributeRange.location += _startRange.location;
      [_origin addAttributes: attr
                       range: attributeRange];
    }
  else
    {
      if (_commentType != NoComment)
        {
          attr = commentsAttr;
          attributeRange = NSMakeRange(position, 1);
          attributeRange.location += _startRange.location;
          [_origin addAttributes: attr
                           range: attributeRange];
        }
    }
  position++;
  _preChar = element;
}

- (void) invisible: (unichar) element
{
  [super invisible: element];
  position ++;
  _preChar = element;
}

- (void) setString: (NSMutableAttributedString *) string
{
  ASSIGN(_origin, string);
  _startRange = NSMakeRange(0, [_origin length]);
}

- (void) setRange: (NSRange) range
{
  _startRange = range;
}

- (id) init
{
  NSFont *preprocessorFont, *keywordsFont, *commentsFont;
  NSFont *stringsFont, *knowntypesFont, *normalFont;
  NSColor *preprocessorColor, *keywordsColor, *commentsColor;
  NSColor *stringsColor, *knowntypesColor, *normalColor;

  self = [super init];
  _rulesetManager = [RulesetManager sharedRulesetManager];
  _startRange = NSMakeRange(0, 0); 
  position = 0;

  /* cache */
  preprocessorColor = [_rulesetManager colorForType: @"Preprocessor"];
  keywordsColor = [_rulesetManager colorForType: @"Keywords"];
  commentsColor = [_rulesetManager colorForType: @"Comments"];
  stringsColor = [_rulesetManager colorForType: @"Strings"];
  knowntypesColor = [_rulesetManager colorForType: @"KnownTypes"];
  normalColor = [_rulesetManager colorForType: @"Normal"];
  normalFont = [_rulesetManager normalFont];
  preprocessorFont = [_rulesetManager fontForType: @"Preprocessor"];
  keywordsFont = [_rulesetManager fontForType: @"Keywords"];
  commentsFont = [_rulesetManager fontForType: @"Comments"];
  stringsFont = [_rulesetManager fontForType: @"Strings"];
  knowntypesFont = [_rulesetManager fontForType: @"KnownTypes"];

#define MAKE_ATTRIBUTES(color, font) \
	[[NSDictionary alloc] initWithObjectsAndKeys: \
	color, NSForegroundColorAttributeName, \
	font, NSFontAttributeName, nil]; 

  normalAttr = MAKE_ATTRIBUTES(normalColor, normalFont);
  preprocessorAttr = MAKE_ATTRIBUTES(preprocessorColor, preprocessorFont);
  keywordsAttr = MAKE_ATTRIBUTES(keywordsColor, keywordsFont);
  commentsAttr = MAKE_ATTRIBUTES(commentsColor, commentsFont);
  stringsAttr = MAKE_ATTRIBUTES(stringsColor, stringsFont);
  knowntypesAttr = MAKE_ATTRIBUTES(knowntypesColor, knowntypesFont);

  return self;
}

- (void) dealloc
{
  RELEASE(_origin);

  RELEASE(preprocessorAttr);
  RELEASE(keywordsAttr);
  RELEASE(commentsAttr);
  RELEASE(stringsAttr);
  RELEASE(knowntypesAttr);
  RELEASE(normalAttr);

  [super dealloc];
}

@end

