/*
**  RulesetManager.m
**
**  Copyright (c) 2002, 2003
**
**  Author: Yen-Ju Chen <yjchenx@hotmail.com>
**          Bjoern Giesler <bjoern@giesler.de>
**
**  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 "RulesetManager.h"
#include <AppKit/AppKit.h>

@implementation NSColor (RulesetManager)
+ (NSColor *)colorWithRGBAArray: (NSArray *) array
{
  if([array count] != 4)
    {
      [NSException raise: @"NSColor Creation Error"
		   format: @"RGBA array needs 4 elements, "
		   @"is: %@", [array description]];
      return nil;
    }
  return [NSColor colorWithCalibratedRed: [[array objectAtIndex: 0]
					    floatValue]
		  green: [[array objectAtIndex: 1] floatValue]
		  blue: [[array objectAtIndex: 2] floatValue]
		  alpha: [[array objectAtIndex: 3] floatValue]];
}
@end

@implementation NSArray (RulesetManager)
+ (NSArray *) arrayWithColor: (NSColor *) color
{
  return [NSArray arrayWithObjects:
                    [NSNumber numberWithFloat: [color redComponent]],
                    [NSNumber numberWithFloat: [color greenComponent]],
                    [NSNumber numberWithFloat: [color blueComponent]],
                    [NSNumber numberWithFloat: [color alphaComponent]],
                    nil]; 
}
@end

static RulesetManager *sharedManager = nil;

@implementation RulesetManager

+ (RulesetManager *) sharedRulesetManager
{
  if (!sharedManager)
    {
      sharedManager = [[RulesetManager alloc] init];
      [sharedManager loadFontAttributesFromUserDefaults];
    }
  return sharedManager;
}

- (NSDictionary *) attributesForType: (NSString *) type
{
  if (type == nil)
    return nil;
  return [fontAttributes objectForKey: type];
}

- (void) setColor: (NSColor *) color forType: (NSString *) type
{
  if ((type == nil) || (color == nil))
    return;

  color = [color colorUsingColorSpaceName: NSCalibratedRGBColorSpace];

  /* Update font attributes */
  [[fontAttributes objectForKey: type] setObject: color forKey: @"Color"];

  /* Update font defaults */
  [[fontDefaults objectForKey: type] setObject: [NSArray arrayWithColor: color] 
                                        forKey: @"Color"];
}

- (void) setFont: (NSFont *) font forType: (NSString *) type
{
  NSMutableDictionary *dict;
  NSMutableArray *array;
  NSFontManager *fontManager = [NSFontManager sharedFontManager];
  NSFontTraitMask mask;

  if ((type == nil) || (font == nil))
    return;

  dict = [NSMutableDictionary new];
  array = [NSMutableArray new];

  /* Update font attributes */
  [[fontAttributes objectForKey: type] setObject: font forKey: @"Font"];

  /* Update font defaults */
  [dict setObject: [font fontName] forKey: @"Name"];
  [dict setObject: [NSNumber numberWithFloat: [font pointSize]]
           forKey: @"Size"];

  mask = [fontManager traitsOfFont: font];
  if (mask & NSBoldFontMask)
    [array addObject: @"Bold"];
  if (mask & NSItalicFontMask)
    [array addObject: @"Italic"];
  if ([array count] > 0)
    [dict setObject: array forKey: @"Traits"];

  [[fontDefaults objectForKey: type] setObject: dict
                                        forKey: @"Font"];

  RELEASE(dict);
  RELEASE(array);
}

- (NSColor *) colorForType: (NSString *) type
{
  return [[self attributesForType: type] objectForKey: @"Color"];
}

- (void) setNormalFont: (NSFont *) font
{
//  ASSIGN(normalFont, font);
  [self setFont: font forType: @"Normal"];
}

- (NSFont *) normalFont
{
  NSFont *font;

/*
  if (normalFont)
    return normalFont;
  else
*/
    font = [[self attributesForType: @"Normal"] objectForKey: @"Font"];

  if (font == nil)
    font = [NSFont userFixedPitchFontOfSize: [NSFont systemFontSize]];

  [self setNormalFont: font];

  return font;
}

- (void) setTrait: (NSFontTraitMask) mask forType: (NSString *) type
{
  NSFontManager *fontManager = [NSFontManager sharedFontManager];
  NSFont *font;

  font = [fontManager convertFont: [self normalFont] toHaveTrait: mask];
  [self setFont: font forType: type];
}

- (NSFont *) fontForType: (NSString *) type
{
  NSFont *font;
  id object;

  font = [self normalFont];

  if((object = [[self attributesForType: type] objectForKey: @"Font"]))
    {
      if([object isKindOfClass: [NSFont class]])
        {
          return object;
        }
    }
  return font;
}

- (void) buildFontAttributes
{
  NSEnumerator *e;
  NSString *key; 
  id object, objectColor, objectFont;
  NSFont *font;
  NSFontManager *fontManager = [NSFontManager sharedFontManager];

  if (fontDefaults == nil) return;

  // now, build the actual dictionary
  ASSIGN(fontAttributes, [NSMutableDictionary new]);

  e = [fontDefaults keyEnumerator];
  while((key = [e nextObject]))
    {
      NSMutableDictionary *dict =
           [NSMutableDictionary dictionaryWithCapacity: 2];
  
      font = nil;

      object = [fontDefaults objectForKey: key];

      if((objectColor = [object objectForKey: @"Color"]))
        {
          NSColor *color = [NSColor colorWithRGBAArray: objectColor];
          [dict setObject: color forKey: @"Color"];
        }

      if((objectFont = [object objectForKey: @"Font"]))
        {
          id name, size, traits;
	  
          if((name = [objectFont objectForKey: @"Name"]))
            {
              font = [NSFont fontWithName: name
                                     size: [NSFont systemFontSize]];
            }
          else
            {
              font = [self normalFont];
            }
             
          if (font)
            {
              if((size = [objectFont objectForKey: @"Size"]))
                font = [fontManager convertFont: font
                                         toSize: [size floatValue]];

              if((traits = [objectFont objectForKey: @"Traits"]))
                {
                  id trait;
                  NSFontTraitMask mask = 0;
                  NSEnumerator *enumTrait = [traits objectEnumerator];
                  while((trait = [enumTrait nextObject]))
                    {
                      if([trait compare: @"Bold"] == NSOrderedSame)
                        mask |= NSBoldFontMask;
                      else if([trait compare: @"Italic"] == NSOrderedSame)
                        mask |= NSItalicFontMask;
                      /* ignore others */
                    }
                  font = [fontManager convertFont: font
                                      toHaveTrait: mask];
                }
              [dict setObject: font forKey: @"Font"];
            }
        }
      [fontAttributes setObject: dict forKey: key];
    }
}

- (BOOL) loadFontAttributesFromUserDefaults
{
  NSDictionary *lookPresets, *lookDefaults;
  NSEnumerator *e;
  NSString *key; 

  // BNF for the lookPresets:
  // lookPresets ::= '{' [lookPreset ';']* '}'
  // lookPreset ::= string '=' lpdict
  // lpdict ::= '{' [colordef ';'] [fontdef ';'] '}'
  // colordef ::= '(' red ',' green ',' blue ',' alpha ')'
  // red ::= float   green ::= float   blue ::= float   alpha ::= float
  // fontdef ::= '{' ['Name' '=' fontname ';']
  //                 ['Size' '=' fontsize ';']
  //                 ['Traits' '=' fonttraits ';'] '}'
  // fontname ::= <a valid font name>
  // fontsize ::= <a valid font size>
  // fonttraits ::= '(' ['Bold' ','] ['Italic'] ')'
  //
  // Note: No, at the moment I don't do anything but bold and italic.
  
  /* The default font attributes */
  lookPresets =
    [@"{Normal = {Color = (0.0, 0.0, 0.0, 1.0);         "
     @"           Font = {Name = Courier;               "
     @"                   Size = 12.0;};};              "
     @" Comments = {Color = (0.0, 0.5, 0.0, 1.0);       "
     @"             Font = {Traits = (Italic);};};      "
     @" Strings = {Color = (.5, .5, 0.0, 1.0);          "
     @"            Font = {Traits = (Italic);};};       "
     @" Preprocessor = {Color = (0.5, 0.5, 0.5, 1.0);   "
     @"                 Font = {Traits = (Bold);};};    "
     @" Keywords = {Color = (0.8, 0.0, 0.0, 1.0);};     "
     @" KnownTypes = {Color = (0.0, 0.5, 0.5, 1.0);};   "
     @"}"
      propertyList];

  /* Read from user default */
  lookDefaults = [[NSUserDefaults standardUserDefaults]
		    objectForKey: @"CodeFontificationAttributes"];

  // First put in everything from the user defaults...
  if (lookDefaults)
    ASSIGN(fontDefaults, [NSMutableDictionary dictionaryWithDictionary: lookDefaults]);
  else
    ASSIGN(fontDefaults, AUTORELEASE([NSMutableDictionary new]));

  // ...then, everything that the presets have and the user defaults don't. 
  e = [lookPresets keyEnumerator];
  while((key = [e nextObject]))
    {
      if([fontDefaults objectForKey: key] == nil)
        {
          [fontDefaults setObject: [lookPresets objectForKey: key]
                           forKey: key];
        }
    }

  [self buildFontAttributes];

  return YES;
}

- (void) writeFontAttributesToUserDefaults
{
  [[NSUserDefaults standardUserDefaults]
		    setObject: fontDefaults
            forKey: @"CodeFontificationAttributes"];
  [[NSUserDefaults standardUserDefaults] synchronize];
}

- (id) init
{
  self = [super init];
  allNames = nil;
  fontAttributes = nil;
  fontDefaults = nil;
  return self;
}

- (void) dealloc
{
//  RELEASE(normalFont);
  RELEASE(allNames);
  RELEASE(fontAttributes);
  RELEASE(fontDefaults);
  [super dealloc];
}

@end

