/*
**  Utilities.m
**
**  Copyright (c) 2004
**
**  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 "GNUstep.h"
#include "Utilities.h"
#include <Foundation/Foundation.h>
#include "Constants.h"
#include "AttributesSource.h"
#include "Criterion.h"
#include "Sources.h"

static NSString *my_library_path = nil;
#ifdef GNUSTEP
static NSString *LibraryLocation = @"~/";
#else
static NSString *LibraryLocation = @"~/Documents/";
#endif
static NSString *LibraryPath = @"MyLibrary";

NSString *MyLibraryPath(NSString *newPath)
{
  if (newPath)
    {
      ASSIGN(my_library_path, [newPath stringByStandardizingPath]);
    }
  else
    {
      if (my_library_path == nil)
        {
          ASSIGN(my_library_path, [[LibraryLocation stringByStandardizingPath] stringByAppendingPathComponent: LibraryPath]);    
        }
    }
  return my_library_path;
}

void addAvailableSubjectsIntoCriterion (Criterion *criterion)
{
   /* Try to get all subject */
  [criterion removeAllAvailableSubjects];
  [criterion addAvailableSubject: sAnyText forType: CriterionAnyStringType];
  [criterion addAvailableSubject: sTitle forType: CriterionStringType];
  [criterion addAvailableSubject: sNote forType: CriterionStringType];
  [criterion addAvailableSubject: sSource forType: CriterionSelectionType];
  [criterion addAvailableSubject: sFilename forType: CriterionStringType];
  [criterion addAvailableSubject: sFiletype forType: CriterionStringType];
  [criterion addAvailableSubject: sFileContent forType: CriterionContentType];

  AttributesSource *attributes = [AttributesSource sharedSource];
  NSArray *allKeys;
  NSString *key, *type;
  unsigned int i, count;

  allKeys = [[attributes keysOfAttributes] allObjects];
  count = [allKeys count];
  for(i = 0; i < count; i++)
    {
      key = nameOfAttributeKey([allKeys objectAtIndex: i]);
      /* Add prefix "- " to indicates it is attribute */
      key = [@"- " stringByAppendingString: key];
      type = typeOfAttributeKey([allKeys objectAtIndex: i]);
      if ([type isEqualToString: AttributeStringType])
        {
          [criterion addAvailableSubject: key forType: CriterionStringType];
        }
      else if ([type isEqualToString: AttributeFloatType])
        {
          [criterion addAvailableSubject: key forType: CriterionNumberType];
        }
      else if ([type isEqualToString: AttributeIntegerType])
        {
          [criterion addAvailableSubject: key forType: CriterionNumberType];
        }
      else if ([type isEqualToString: AttributeCalendarDateType])
        {
          [criterion addAvailableSubject: key forType: CriterionDateType];
        }
      else
        {
          NSLog(@"internal Error (SmartFolderEditor): invalid attribute type");
        }
    }
}

NSDictionary *attributesFromFile (NSString *absolutePath)
{
  NSFileManager *fm = [NSFileManager defaultManager];
  NSDictionary *attr = [fm fileAttributesAtPath: absolutePath
	                           traverseLink: YES];
  if (attr == nil) return nil;
  NSDate *creationDate = [attr objectForKey: NSFileCreationDate];
  NSDate *modificationDate = [attr objectForKey: NSFileModificationDate];
  NSNumber *size = [attr objectForKey: NSFileSize];
  NSString *owner = [NSString stringWithFormat: @"%@/%@",
                          [attr objectForKey: NSFileOwnerAccountName],
			  [attr objectForKey: NSFileGroupOwnerAccountName]];
  return [NSDictionary dictionaryWithObjectsAndKeys:
      creationDate, 
      keyOfAttributes(0, FileInfoCategory, FileInfoCreationDate, AttributeCalendarDateType),
      modificationDate,
      keyOfAttributes(1, FileInfoCategory, FileInfoModificationDate, AttributeCalendarDateType),
      owner,
      keyOfAttributes(2, FileInfoCategory, FileInfoOwner, AttributeStringType),
      size,
      keyOfAttributes(3, FileInfoCategory, FileInfoSize, AttributeIntegerType),
      nil];
}

/* decode /xxx/yyy/type */
int orderOfAttributeKey (NSString *key)
{
  return [[[[[key stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] lastPathComponent] intValue];
}

NSString *categoryOfAttributeKey (NSString *key)
{
  return [[[key stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] lastPathComponent];;
}

NSString *nameOfAttributeKey (NSString *key)
{
  return [[key stringByDeletingLastPathComponent] lastPathComponent];
}

NSString *typeOfAttributeKey (NSString *key)
{
  return [key lastPathComponent];
}

NSString *keyOfAttributes (int order, NSString *category, NSString *name, NSString *type)
{
  return [[[[@"/" stringByAppendingPathComponent: [NSString stringWithFormat: @"%d", order]] stringByAppendingPathComponent: category] stringByAppendingPathComponent: name] stringByAppendingPathComponent: type];
}

#include "RegEx.h"

static NSString *RegExDotFloatNumberPattern = @"[-+]?[0-9]*\\.[0-9]+";
static NSString *RegExCommaFloatNumberPattern = @"[-+]?[0-9]*\\,[0-9]+";
//static NSString *RegularExpressionDisplayNumber3 = @"-9.99e-9";
////static NSString *RegularExpressionNumber3 = @"[-+]?([0-9]*\\.)?[0-9]+([eE][-+]?[0-9]+)?";

static NSString *RegExIntegerPattern = @"[-+]?[0-9]+";

static NSString *RegExMMDDYYYYDatePattern = @"\\((0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])(/[0-9]+)?\\)";
static NSString *RegExDDMMYYYYDatePattern = @"\\((0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])(/[0-9]+)?\\)";
static NSString *RegExYYYYMMDDDatePattern = @"\\(([0-9]+)/(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])\\)";

static NSString *RegExTimePattern = @"\\((0?[0-9]|1[0-9]|2[0-4]):(0?[0-9]|[1-6][0-9])(:(0?[0-9]|[1-6][0-9]))?\\)";

static NSString *RegExMMDDYYYYFullDatePattern = @"\\((0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])(/[0-9]+)?[ ]+(0?[0-9]|1[0-9]|2[0-4]):(0?[0-9]|[1-6][0-9])(:(0?[0-9]|[1-6][0-9]))?\\)";
static NSString *RegExDDMMYYYYFullDatePattern = @"\\((0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])(/[0-9]+)?[ ]+(0?[0-9]|1[0-9]|2[0-4]):(0?[0-9]|[1-6][0-9])(:(0?[0-9]|[1-6][0-9]))?\\)";
static NSString *RegExYYYYMMDDFullDatePattern = @"\\(([0-9]+)/(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])[ ]+(0?[0-9]|1[0-9]|2[0-4]):(0?[0-9]|[1-6][0-9])(:(0?[0-9]|[1-6][0-9]))?\\)";


id objectFromRegExParsedString (NSString *string, RegExFloatNumber number, RegExDate date, RegExType *type)
{
  NSString *floatPattern;
  NSString *integerPattern;
  NSString *datePattern;
  NSString *timePattern;
  NSString *fullDatePattern;

  NSRange range;
  NSString *s, *testString;
  NSCalendarDate *d, *currentDate = [NSCalendarDate calendarDate];
  NSNumber *n;

  switch (number)
    {
      case RegExDotFloatNumber:
	floatPattern = RegExDotFloatNumberPattern;
	break;
      case RegExCommaFloatNumber:
	floatPattern = RegExCommaFloatNumberPattern;
	break;
    }

  integerPattern = RegExIntegerPattern;

  switch (date)
    {
      case RegExMMDDYYYYDate:
	datePattern = RegExMMDDYYYYDatePattern;
	fullDatePattern = RegExMMDDYYYYFullDatePattern;
	break;
      case RegExDDMMYYYYDate:
	datePattern = RegExDDMMYYYYDatePattern;
	fullDatePattern = RegExDDMMYYYYFullDatePattern;
	break;
      case RegExYYYYMMDDDate:
	datePattern = RegExYYYYMMDDDatePattern;
	fullDatePattern = RegExYYYYMMDDFullDatePattern;
	break;
    }

  timePattern = RegExTimePattern;

  if ([string isKindOfClass: [NSString class]] == NO)
    {
      NSLog(@"Internal Error (Regex): %@ is not a string", string);
    }
  testString = [string stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];

  /* Try CalendarDateDisplayFormat */
  d = [NSCalendarDate dateWithString: testString
	              calendarFormat: CalendarDateDisplayFormat];
  if (d)
    {
      if ((type))
        *type = RegExCalendarDateType;
      //NSLog(@"date %@", d);
      return d;
    }

  /* Date */
  range = [RegExParser rangeOfString: datePattern
                            inString: testString];
  if ((range.location == 0) && (range.length) == [testString length])
    {
      s = [testString substringWithRange: NSMakeRange(range.location+1, range.length-2)];
      switch(date)
        {
           case RegExMMDDYYYYDate:
             {
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%m/%d/%Y"];
                if (d == nil)
                  d = [NSCalendarDate dateWithString: s
                                      calendarFormat: @"%m/%d"];
                break; 
             }
	   case RegExDDMMYYYYDate:
             {
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%d/%m/%Y"];
                if (d == nil)
                  d = [NSCalendarDate dateWithString: s
                                      calendarFormat: @"%d/%m"];
                break;
             }
           case RegExYYYYMMDDDate:
             {
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%Y/%m/%d"];
                break;
             }
           default: 
             NSLog(@"Internal Error: Unknown Date Format");
         }
       /* Put int time information */
       if (d) 
         {
            d = [NSCalendarDate dateWithString: [NSString stringWithFormat: @"%@ %@", [d descriptionWithCalendarFormat: @"%m/%d/%Y"], [currentDate descriptionWithCalendarFormat: @"%H:%M:%S"]]
                                   calendarFormat: @"%m/%d/%Y %H:%M:%S"];
	    [d setCalendarFormat: CalendarDateDisplayFormat];
	    if ((type))
	      *type = RegExCalendarDateType;
            //NSLog(@"date %@", d);
            return d;
         }
       else
         {
           NSLog(@"Invalide date: %@", s);
         }
    }

  /* Time */
  range = [RegExParser rangeOfString: timePattern
                            inString: testString];
  if ((range.location == 0) && (range.length) == [testString length])
    {
      s = [testString substringWithRange: NSMakeRange(range.location+1, range.length-2)];
      d = [NSCalendarDate dateWithString: s
                          calendarFormat: @"%H:%M:%S"];

      if (d == nil)
        d = [NSCalendarDate dateWithString: s
                            calendarFormat: @"%H:%M"];

      /* Put int time information */
      if (d)
        {
          d = [NSCalendarDate dateWithString: [NSString stringWithFormat: @"%@ %@", [currentDate descriptionWithCalendarFormat: @"%m/%d/%Y"], [d descriptionWithCalendarFormat: @"%H:%M:%S"]]
                              calendarFormat: @"%m/%d/%Y %H:%M:%S"];
	  [d setCalendarFormat: CalendarDateDisplayFormat];
	  if ((type))
	    *type = RegExCalendarDateType;
          //NSLog(@"Time %@, type %d", d, *type);
          return d;
        }
      else
        {
           NSLog(@"Invalide Time: %@", s);
        }
    }
  /* Full time */
  range = [RegExParser rangeOfString: fullDatePattern
                            inString: testString];
  if ((range.location == 0) && (range.length) == [testString length])
    {
      NSArray *temp;
      s = [testString substringWithRange: NSMakeRange(range.location+1, range.length-2)];
      temp = [s componentsSeparatedByString: @" "];
      s = [NSString stringWithFormat: @"%@ %@", [temp objectAtIndex: 0], [temp lastObject]];
      //NSLog(@"full time string %@", s);
      switch(date)
        {
          case RegExMMDDYYYYDate:
            {
              d = [NSCalendarDate dateWithString: s
                                  calendarFormat: @"%m/%d/%Y %H:%M:%S"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%m/%d %H:%M:%S"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%m/%d/%Y %H:%M"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%m/%d %H:%M"];
	      break;
	    }
          case RegExDDMMYYYYDate:
            {
              d = [NSCalendarDate dateWithString: s
                                  calendarFormat: @"%d/%m/%Y %H:%M:%S"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%d/%m %H:%M:%S"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%d/%m/%Y %H:%M"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%d/%m/%Y %H:%M"];
              break;
            }
	  case RegExYYYYMMDDDate: 
            {
              d = [NSCalendarDate dateWithString: s
                                  calendarFormat: @"%Y/%m/%d %H:%M:%S"];
              if (d == nil)
                d = [NSCalendarDate dateWithString: s
                                    calendarFormat: @"%Y/%m/%d %H:%M"];
              break;
            }
          default:
            NSLog(@"Internal Error: Unknown Full Time Format");
        }
      /* Put int time information */
      if (d)
        {
	  [d setCalendarFormat: CalendarDateDisplayFormat];
	  if ((type))
            *type = RegExCalendarDateType;
	  //NSLog(@"Full Time %@", d );
	  return d;
	}
      else
        {
   	  NSLog(@"Invalide date: %@", s);
        }
    }
  /* Float */
  range = [RegExParser rangeOfString: floatPattern
                            inString: testString];
  if ((range.location == 0) && (range.length) == [testString length])
    {
      if (number == RegExCommaFloatNumber) /* -0,99 */
        {
          range = [testString rangeOfString: @","];
          s = [NSString stringWithFormat: @"%@.%@", [testString substringToIndex: range.location], [testString substringFromIndex: range.location+1]];
        }
      else
        s = testString;
      n = [NSNumber numberWithDouble: [s doubleValue]];
      if (n)
        {
	  if ((type))
	    *type = RegExFloatNumberType;
          //NSLog(@"Float %@", n);
          return n;
	}
      else
        {
	  NSLog(@"Invalide float number: %@", testString);
        }
    }

  /* Integer */
  range = [RegExParser rangeOfString: integerPattern
                            inString: testString];
  if ((range.location == 0) && (range.length) == [testString length])
    {
      n = [NSNumber numberWithInt: [testString intValue]];
      if (n)
        {
	  if ((type))
	    *type = RegExIntegerType;
          //NSLog(@"Integer %@", n);
          return n;
	}
      else
        {
	  NSLog(@"Invalide integer number: %@", testString);
        }
    }

  if ((type))
    *type = RegExStringType;

  //NSLog(@"String %@", string);
  return string;
}

