
#undef DEBUG
#undef DEBUGTABLE
#undef ASSERTIONS

#import "RSSFeedList.h"

#include <time.h>

@implementation TableTuple : NSObject

-initWithFeed: (RSSFeed*) myFeed
      article: (unsigned int) myArticle
	 time: (unsigned int) myTime
{
  feed =      myFeed;
  article =   myArticle;
  time =      myTime;
  
  RETAIN(feed);
  
  
  dateAsString = nil;
  
  return self;
}

-feed
{
  return AUTORELEASE(RETAIN(feed));
}


-(unsigned int) article
{
#ifdef DEBUGX
  printf("### [tuple article] returns %u\n", article);
#endif
  return article;
}


-(unsigned int) time
{
  return time;
}


-dateString
{
  if (dateAsString == nil)
    {
      RELEASE(date); // you never know...
      date = [NSDate dateWithTimeIntervalSince1970: ((NSTimeInterval) time)];
      dateAsString = [date description];
      RETAIN(dateAsString);
      RETAIN(date);
    }
  
#ifdef DEBUGX
  NSLog(@"r dateAsSrting rc (%u) \"%@\"",
	 [dateAsString retainCount], dateAsString);
#endif
  
  return dateAsString;
}


-(void) dealloc
{
  NSLog(@"TableTuple dealloc");
  
  //NSLog(@"TableTuple dealloc 1 %@ (rc=%d)",
  // [feed description], [feed retainCount]);
  RELEASE(feed);
  //NSLog(@"TableTuple dealloc 2");
  RELEASE(date);
  //NSLog(@"TableTuple dealloc 3");
  RELEASE(dateAsString);
  //NSLog(@"TableTuple dealloc 4");
  
  [super dealloc];
}

@end


@implementation RSSFeedList

-(void) dealloc
{
  RELEASE(feedCol);
  RELEASE(headlineCol);
  RELEASE(dateCol);
  RELEASE(table);
  RELEASE(list);
  RELEASE(articleViewer);
  
  RELEASE(markedFeed);
  RELEASE(markedArticle);
}

-init
{
  self = [super init];
  
  list  = [[NSMutableArray alloc] init];
  table = nil; // ok, because rebuildTable produces it.
  
  articleViewer = nil;
  feedCol = nil;
  headlineCol = nil;
  dateCol = nil;
  
  [self rebuildTable];
  
  return self;
}


-initWithFeedCol: (id) myFeedCol
     headlineCol: (id) myHeadlineCol
	 dateCol: (id) myDateCol
{
  self = [self init];
  
  [self feedCol: myFeedCol];
  [self headlineCol: myHeadlineCol];
  [self dateCol: myDateCol];
  return self;
}


// NSCoding stuff
-(id)initWithCoder: (NSCoder*)coder
{
#ifdef DEBUG
  NSLog(@"started decoding RSSFeedList");
#endif
  
  if (self = [self init])
    {
      //[self feedCol: [coder decodeObject]];
      //[self headlineCol: [coder decodeObject]];
      //[self dateCol: [coder decodeObject]];
      
      // articleViewer will *not* be decoded.
      // it points back to MyDelegate
      
      // Table is generated by rebuildTable.
      // table = [coder decodeObject];
      
      // the list of rss feeds
      list = RETAIN([coder decodeObject]);
      
      // now rebuild the table
      [self rebuildTable];
      
#ifdef DEBUGX
      NSLog(@"%@ retain count: %u",
	    [feedCol description], [feedCol retainCount]);
      NSLog(@"%@ retain count: %u",
	    [headlineCol description], [headlineCol retainCount]);
      NSLog(@"%@ retain count: %u",
	    [dateCol description], [dateCol retainCount]);
#endif
    }
  
#ifdef DEBUG
  NSLog(@"finished decoding RSSFeedList (rc=%i)", [self retainCount]);
#endif
  return self;
}

// NSCoding stuff
-(void)encodeWithCoder: (NSCoder*)coder
{
#ifdef DEBUG
  NSLog(@"started encoding RSSFeedList");
#endif
  
  //[coder encodeObject: feedCol];
  //[coder encodeObject: headlineCol];
  //[coder encodeObject: dateCol];
  
  // article viewer will *not* be encoded
  // it points back to MyDelegate
  
  // table is generated by rebuildTable when decoding
  //[coder encodeObject: table];
  
  [coder encodeObject: list];
  
#ifdef DEBUG
  NSLog(@"finished encoding RSSFeedList");
#endif
}


#define BROWSER "dillo"
-(void) viewSelected
{  
  NSLog(@"viewing %@", [markedArticle url]);
  
#ifdef MACOSX
  [[NSWorkspace sharedWorkspace]
    openURL: [NSURL URLWithString: [markedArticle url]]];
#else
  if (!fork())
    execlp(BROWSER, BROWSER, [[markedArticle url] cString], NULL);
#endif
}

-(void) dumpArray: (NSArray*) a
{
  unsigned int i;
  
#ifdef DEBUGX
  printf ("dumpArray %s (rc=%u)\n", [[a description] cString],
	  [a retainCount]);
#endif
  
  for (i=0;i<[a count];i++)
    {
      id o;
      
      o = [a objectAtIndex: i];
      printf(" * %s (rc=%u)\n",
	     [[o description] cString],
	     [o retainCount]);
    }
}

-rebuildTable
{
  unsigned int  i,article;
  RSSFeed*      feed;
  TableTuple*   tuple;
  
  // first release the old table
  
#ifdef DEBUG
  NSLog(@"rebuildTable: releasing table with rc %u and %d entries\n",
	[table retainCount], [table count]);
#endif
  
  RELEASE(table);
  
#ifdef DEBUG
  NSLog(@"creating a new table");
#endif
  // then create a new table
  table = [[NSMutableArray alloc] init];
  
  // and fill the new table with info
  for (i=0; i<[list count]; i++) // iterate over feeds
    {
      feed = [list objectAtIndex:i];
      
      printf("  i=%u\n", i);
      // iterate over articles in feed
      for (article=0; article<[feed count]; article++)
	{
	  printf("    article=%u\n", article);
	  tuple =
	    [ [TableTuple alloc]
	      initWithFeed: feed
	      article: article
	      time: [feed timeAtIndex: article] ];
	  [table addObject: tuple];
	  //printf("      releasing tuple \"%s\" with rc %u\n",
	  //       [[tuple description] cString], [tuple retainCount]);
	  RELEASE(tuple);
	}
    }
  
  NSLog (@"ending rebuildTable with %d", [table count]);
  //[self dumpArray: table];
  //[self dumpArray: list];
  return table;
}

-feedCol: (id) myFeedCol
{
  RELEASE(feedCol);
  feedCol = myFeedCol;
  RETAIN(feedCol);
}

-headlineCol: (id) myHeadlineCol
{
  RELEASE(headlineCol);
  headlineCol = myHeadlineCol;
  RETAIN(headlineCol);
}

-dateCol: (id) myDateCol
{
  RELEASE(dateCol);
  dateCol = myDateCol;
  RETAIN(dateCol);
}

-(void)setArticleViewer: (id)aArticleViewer
{
  RELEASE(articleViewer);
  articleViewer = RETAIN(aArticleViewer);
}

-(RSSFeed*) markedFeed
{
  return AUTORELEASE(RETAIN(markedFeed));
}

// NS Table stuff

// must be efficient!
-(int) numberOfRowsInTableView: (NSTableView*)aTableView
{
#ifdef DEBUGTABLE
  
  int retval = 0;
  
  if (table != nil)
    {
      retval = [table count];
      NSLog(@"numberOfRowsInTableView... table rows == %d", retval);
    }
  else
    NSLog(@"numberOfRowsInTableView... table is nil!");
  
  return retval;
  
#else // !DEBUGTABLE
  
  // FIXME catch if table is dirty?
  return [table count];
  
#endif // DEBUGTABLE
}


// implementation is optional
/*
-(BOOL) tableView: (NSTableView*) tableView
       acceptDrop: (id <NSDraggingInfo>) info
	      row: (int) row
    dropOperation: (NSTableViewDropOperation) operation;
*/

// is executed when a table row is clicked
-(BOOL) tableView: (NSTableView*) aTableView
  shouldSelectRow: (int) rowIndex
{
  TableTuple* tuple;
  RSSArticle* article;
  
#ifdef DEBUGTABLE
  //NSLog(@">> --- tableView:shouldSelectRow:");
  //NSLog(@"table = %d", (unsigned int)table);
#endif
  
  if (rowIndex >= [table count])
    {
#ifdef DEBUGTABLE
      NSLog(@"<< tableView:shouldSelectRow: -> NO");
#endif
      return NO; // not selectable
    }

#ifdef DEBUGTABLE
  //NSLog(@"XXX-");
#endif
  
  tuple = [table objectAtIndex: rowIndex];
  
#ifdef DEBUGTABLE
  //NSLog(@"XXX");
#endif
  
  if (tuple == nil)
    {
#ifdef DEBUGTABLE
      NSLog(@"<< tableView:shouldSelectRow: -> NO");
#endif
      return NO; // error => not selectable
    }
  
#ifdef DEBUGTABLE
  //NSLog(@"YYY");
#endif
  
#ifdef DEBUGTABLE
  if (articleViewer == nil)
    {
      NSLog(@"In RSSFeedList.m:");
      NSLog(@"  I can't show any articles if articleViewer is nil!");
    }
#endif
  
  article = [[tuple feed] articleAtIndex: [tuple article]];
  [articleViewer setArticle: article];
  
#ifdef DEBUGTABLE
  NSLog(@"<< tableView:shouldSelectRow:");
#endif
  
  // actualize the currently marked feed.
  RELEASE(markedFeed);
  markedFeed = RETAIN([tuple feed]);
  
  // actualize the currently marked article
  RELEASE(markedArticle);
  markedArticle = RETAIN(article);
  
#ifdef DEBUGTABLE
  NSLog(@"marked feed = %@", [markedFeed description]);
#endif
  
  return YES;
}

// must be efficient!
- (id)              tableView: (NSTableView*) aTableView
    objectValueForTableColumn: (NSTableColumn*) aTableColumn
			  row: (int) rowIndex
{
  RSSFeed* feed;
  TableTuple* tuple;
  NSString * result;
  
#ifdef DEBUG
  NSLog(@">> tableView:objectValueForTableColumn:row:");
#endif
  
  if (rowIndex >= [table count])
    result = @"this row is not present.";
  else
    {
      tuple = [table objectAtIndex: rowIndex];
      feed = [tuple feed];
      
      if ( aTableColumn == feedCol )
	{
	  result = [feed feedName];
	}
      else if ( aTableColumn == headlineCol )
	{
	  result = [feed headlineAtIndex: [tuple article]];
	}
      else if ( aTableColumn == dateCol )
	{
	  result = [tuple dateString];
	}
      else
	{
	  result = @"no value";
	}
    }
  
  //RELEASE(feed);
  //RELEASE(tuple);
  
  
#ifdef DEBUG
  NSLog(@"<< tableView:objectValueForTableColumn:row: -> %@",
	[result description]);
#endif
  return result;
}


- (void) tableView: (NSTableView*) tableView
    setObjectValue: (id) anObject
    forTableColumn: (NSTableColumn*) aTableColumn
	       row: (int) rowIndex
{
  return; // dunno what to do here. :-(
}


// NSArray stuff

-(NSMutableArray*) list
{
  return list;
}


// FIXME: Make this a threaded task!
-(void) updateAllFeeds
{
  int i;
  RSSFeed* feed;
  
  for (i=0; i<[list count]; i++)
    {
      feed = [list objectAtIndex: i];
      
      [feed fetch];
    }
  
  [self rebuildTable];
}


-(void) addFeed: (RSSFeed*) feed
{
  NSLog(@"addFeed: %@", [feed description]);
  
#ifdef ASSERTIONS
  if ([feed class] != [RSSFeed self])
    {
      NSLog (@"addFeed: only takes RSSFeeds, not %@: %@.\n",
	      [[feed class] description],
	      [feed description] );
      return;
    }
#endif
  
  [list addObject: feed];
  [self rebuildTable];
}

-(void) removeFeed: (RSSFeed*) feed
{
  NSLog(@"removeFeed: %@ (rc=%d)",
	[feed description], [feed retainCount]);
  
#ifdef ASSERTIONS
  if ([feed class] != [RSSFeed self])
    {
      NSLog (@"addFeed: only takes RSSFeeds, not %@: %@.\n",
	      [[feed class] description],
	      [feed description] );
      return;
    }
#endif
  
  [list removeObject: feed];
  [self rebuildTable];
}


@end

