/*
**  Channel.m
**
**  Copyright (c) 2002
**
**  Author: Ludovic Marcotte <ludovic@Sophos.ca>
**
**  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#import "Channel.h"

#import "Constants.h"
#import "NSStringExtensions.h"
#import "Plop.h"
#import "PlopView.h"
#import "PlopWindowController.h"

// XML Headers needed under GNUstep
#ifndef MACOSX
#import <Foundation/GSXML.h>

// XML Headers needed under Cocoa
#else
#import <CoreServices/CoreServices.h>
#endif

@implementation Channel

//
//
//
- (id) init
{
  self = [super init];

  allElements = [[NSMutableArray alloc] init];

  animation_timer = nil;

  return self;
}


//
//
//
- (void) dealloc
{
  NSLog(@"Channel: -dealloc");

  [plop setChannel: nil];

  if ( timer )
    {
      [timer invalidate];
      DESTROY(timer);
    }

  RELEASE(allElements);
  RELEASE(icon);
  RELEASE(plop);
  RELEASE(inProgress);

  [super dealloc];
}


//
// access / mutation methods
//
- (void) addElement: (Element *) theElement
{
  [allElements addObject: theElement];
}


//
//
//
- (void) removeElement: (Element *) theElement
{
  [allElements removeObject: theElement];
}

//
//
//
- (void) removeAllElements
{
  [allElements removeAllObjects];
}


//
//
//
- (int) count
{
  return [allElements count];
}


//
//
//
- (int) itemCount
{
  int i, count;
  
  count = 0;

  for (i = 0; i < [allElements count]; i++)
    {
      Element *aElement;

      aElement = [allElements objectAtIndex: i];

      if ( [aElement type] == ITEM )
	{
	  count++;
	}
    }

  return count;
}


//
//
//
- (Element *) elementAtIndex: (int) theIndex
{
  return [allElements objectAtIndex: theIndex];
}


//
//
//
- (NSImage *) icon
{
  return icon;
}


//
//
//
- (void) setIcon: (NSImage *) theIcon
{
  RETAIN(theIcon);
  RELEASE(icon);
  icon = theIcon;
}


//
//
//
- (Plop *) plop
{
  return plop;
}


//
//
//
- (void) setPlop: (Plop *) thePlop
{
  if ( thePlop )
    {
      NSImage *aImage;

      RETAIN(thePlop);
      RELEASE(plop);
      plop = thePlop;

      [plop setChannel: self];

      // We set our icon
      aImage = [[NSImage alloc] initWithContentsOfFile: [NSString stringWithFormat: @"%@/%@",
								  PlopFolioUserLibraryPath(),
								  [thePlop iconName]] ];
      [self setIcon: aImage];
      RELEASE(aImage);
#if 0
      // We start our timer
      NSLog(@"Starting timer with %d minutes interval.", [thePlop refresh]);

      timer = [NSTimer scheduledTimerWithTimeInterval: ([thePlop refresh]*60)
		       target: self
		       selector: @selector(update:)
		       userInfo: nil
		       repeats: YES];
      RETAIN(timer);
#endif
    }
  else
    {
      if ( timer )
	{
	  [timer invalidate];
	  DESTROY(timer);
	}
      
      DESTROY(plop);
    }
}


//
//
//
- (PlopWindowController *) plopWindowController
{
  return plopWindowController;
}


// No need to RETAIN here
- (void) setPlopWindowController: (PlopWindowController *) theController
{
  plopWindowController = theController;
}


//
// Delegate methods
//
- (void) URLHandleResourceDidBeginLoading:(NSURLHandle *)sender
{
  PlopView *theView;

NSLog(@"Begin loading");
  animation_index = 1;

  // We start our animation
  animation_timer = [NSTimer scheduledTimerWithTimeInterval: 0.05
			     target: self
			     selector: @selector(updateAnimation)
			     userInfo: nil
			     repeats: YES];
  RETAIN(animation_timer);

  // We set the stop icon
  theView = [(id)[[self plopWindowController] window] plopView];
  [theView setReloadIcon: [NSImage imageNamed: @"stop.tiff"]];
}



//
//
//
- (void) URLHandleResourceDidCancelLoading: (NSURLHandle *) sender
{
  PlopView *theView;

  NSLog(@"Cancelling update of channel %@... using %@", [plop title], [plop contentSource]);

  // We stop our animation
  [animation_timer invalidate];
  DESTROY(animation_timer);

  theView = [(id)[[self plopWindowController] window] plopView];

  [theView setAnimationIcon: nil];
  [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]];
}


//
//
//
- (void)              URLHandle: (NSURLHandle *) sender 
 resourceDataDidBecomeAvailable: (NSData *) newBytes
{
  // We do nothing here
}


//
//
//
- (void)                URLHandle: (NSURLHandle *) sender
 resourceDidFailLoadingWithReason: (NSString *) reason
{
  NSLog(@"failed loading URL, reason: %@:", reason);
}


//
//
//
- (void) URLHandleResourceDidFinishLoading: (NSURLHandle *) sender 
{
  //
  // GNUstep XML Parsing Code
  //
#ifndef MACOSX
  GSXMLParser *aParser;
  NSData *aData;

  PlopView *theView;

  NSLog(@"Updating channel %@... using %@", [plop title], [plop contentSource]);

  // We stop our animation
  [animation_timer invalidate];
  DESTROY(animation_timer);

  theView = [(id)[[self plopWindowController] window] plopView];

  [theView setAnimationIcon: nil];
  [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]];

  // We get our data
  aData = [sender resourceData];

  if ( !aData )
    {
      NSLog(@"Unable to get the data from the content source. Ignoring Plop.");
      return;
    }

  [self removeAllElements];
  aParser = [GSXMLParser parserWithData: aData];

  if ( [aParser parse] )
    {
      GSXMLDocument *aDocument;
      GSXMLNode *aRoot, *aNode;
      
      aDocument = [aParser document];
      aRoot = [aDocument root];
            
      aNode = [aRoot firstChildElement];
      
      //
      // We loop inside our channel/image/item nodes
      //
      while (aNode != nil)
	{ 
	  NSLog(@"aNode name = %@", [aNode name]);

	  if ( [[aNode name] caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame )
	    {
	      [self _parseNodeInformationUsingElement: self
		    node: aNode];
	    }
	  else
	    {
	      Element *aElement;
	      
	      aElement = [[Element alloc] init];
	      
	      [self _parseNodeInformationUsingElement: aElement
		    node: aNode];
	      
	      [self addElement: aElement];
	      RELEASE(aElement);
	    }
	  
	  // We get the next node
	  aNode = [aNode nextElement];
	}

      // We finally update our "view"
      if ( [self plopWindowController] )
	{
	  [[self plopWindowController] update];
	}
    }
  else
    {
      NSLog(@"Error while decoding the RDF content.");
    }
    
  //
  // OS X XML Parsing Code
  //
#else
  CFXMLTreeRef aTreeRef, xmlTreeRoot;
  CFXMLNodeRef xmlRoot;
  NSData *aData;
  int i;

  PlopView *theView;
  
  // We stop our animation
  [animation_timer invalidate];
  DESTROY(animation_timer);

  theView = [(id)[[self plopWindowController] window] plopView];

  [theView setAnimationIcon: nil];
  [theView setReloadIcon: [NSImage imageNamed: @"reload.tiff"]];

  // We get our data
  aData = [sender resourceData];

  aTreeRef = CFXMLTreeCreateFromData(kCFAllocatorDefault,
				     (CFDataRef)aData,
				     NULL,
				     kCFXMLParserSkipWhitespace|kCFXMLParserSkipMetaData,
				     kCFXMLNodeCurrentVersion);


  if ( !aTreeRef )
    {
      NSRunAlertPanel(_(@"Error!"),
                      _(@"Corrupted RDF. Ignoring %@ Plop."),
                      _(@"OK"),
                      NULL,
                      NULL,
		      [plop title]);
      return;
    }

  [self removeAllElements];

  for (i = 0; i < CFTreeGetChildCount(aTreeRef); i++)
    {
      xmlTreeRoot = CFTreeGetChildAtIndex(aTreeRef, i);
      xmlRoot = CFXMLTreeGetNode(xmlTreeRoot);
    
      if ( CFXMLNodeGetTypeCode(xmlRoot) == kCFXMLNodeTypeElement )
       {
         CFXMLTreeRef xmlSubTreeRef;
         CFXMLNodeRef aNode;
         int j;
         
         // We get our RDF info
         for (j = 0; j < CFTreeGetChildCount(xmlTreeRoot); j++)
             {
               xmlSubTreeRef = CFTreeGetChildAtIndex(xmlTreeRoot, j);
               aNode = CFXMLTreeGetNode(xmlSubTreeRef);
               
               if ( CFXMLNodeGetTypeCode(aNode) == kCFXMLNodeTypeElement )
                 {
                   NSString *aString;
                   
                   aString = [(NSString*)CFXMLNodeGetString(aNode) stringByTrimmingWhiteSpaces];
                   
                   if ( [aString caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame )
		     {
		       [self _parseNodeInformationUsingElement: self
			  node: (id)xmlSubTreeRef];
		     }
	           else
		    {
		      Element *aElement;
		  
		      aElement = [[Element alloc] init];
		  
		      [self _parseNodeInformationUsingElement: aElement
		   	    node: (id)xmlSubTreeRef];
		  
		      [self addElement: aElement];
		      RELEASE(aElement);
		  }
               }
             } // for

       }
    }


  // We finally update our "view"
  if ( [self plopWindowController] )
   {
     [[self plopWindowController] update];
   }
#endif  
}


//
// Other methods
//
- (void) update: (id) sender
{  
  // If the animation is already running, we just return
  if ( animation_timer && [animation_timer isValid] )
    {
      // We must stop...
      if ( [sender isKindOfClass: [PlopView class]] )
	{

	  NSLog(@"Cancelling load in background...");
	  
	  // FIXME: This doesn't work and it's normal. We should handle the stop
	  // differently.
	  [inProgress cancelLoadInBackground];
	  [inProgress release];
	  inProgress = nil;
	}

      NSLog(@"Update already running.");
      return;
    }
  else
    {
      inProgress = [[[self plop] contentSource] URLHandleUsingCache: NO];
      [inProgress retain];
      [inProgress addClient: self];
      [inProgress loadInBackground];
    }
}


//
//
//
- (void) updateAnimation
{
  PlopView *theView;
  
  theView = [(id)[[self plopWindowController] window] plopView];

  //NSLog(@"Downloading...");
  
  [theView setAnimationIcon: [NSImage imageNamed: [NSString stringWithFormat: @"anim%d.tiff",
							    animation_index]] ];
  animation_index += 1;
  [theView setNeedsDisplay: YES];

  if ( animation_index > 16 )
    {
      animation_index = 1;
    }
}

@end


//
// Private methods
//
@implementation Channel (Private)


- (void) _updateElement: (Element *) theElement
		   name: (NSString *) theName
		content: (NSString *) theContent
{
  if ( [theName caseInsensitiveCompare: @"TITLE"] == NSOrderedSame )
    {
      [theElement setTitle: theContent];
    }
  else if ( [theName caseInsensitiveCompare: @"LINK"] == NSOrderedSame )
    {
      [theElement setLink: [NSURL URLWithString: theContent] ];
    }
  else if ( [theName caseInsensitiveCompare: @"DESCRIPTION"] == NSOrderedSame )
    {
      [theElement setDescription: theContent];
    }
  else if ( [theName caseInsensitiveCompare: @"URL"] == NSOrderedSame )
    {
      [theElement setURL: [NSURL URLWithString: theContent] ];
    }
}


//
//
//
- (void) _parseNodeInformationUsingElement: (Element *) theElement
				      node: (id) theNode
{
  NSString *aNodeName;
#ifndef MACOSX
  static int textNodeType = -1;
  GSXMLNode *aChild;
  
  if (textNodeType < 0)
    {
      textNodeType = [GSXMLNode typeFromDescription: @"XML_TEXT_NODE"];
    }
  aNodeName = [(GSXMLNode *)theNode name];
#else
   CFXMLNodeRef xmlNode, aNode;
   CFXMLTreeRef xmlTreeRef;
   int i;
            
   xmlNode = CFXMLTreeGetNode((CFXMLTreeRef)theNode);
   aNodeName = (NSString *)CFXMLNodeGetString(xmlNode);
#endif


  // We decode the right type
  if ( [aNodeName caseInsensitiveCompare: @"CHANNEL"] == NSOrderedSame )
    {
      [theElement setType: CHANNEL];
    }
  else if ( [aNodeName caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame )
    {
      [theElement setType: IMAGE];
    }
  else if ( [aNodeName caseInsensitiveCompare: @"ITEM"] == NSOrderedSame )
    {
      [theElement setType: ITEM];
    }
  else if ( [aNodeName caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame )
    {
      [theElement setType: TEXTINPUT];
    }
  else
    {
      [theElement setType: UNKNOWN];
    }

#ifndef MACOSX
  // We loop inside our channel/image/item node attributes
  if ( (aChild = [theNode firstChild]) )
    {
      while ( aChild )
	{
	  NSString *aName, *aContent;
	  GSXMLNode *aNode;
	  
	  //
	  // Ok, this is kinda weird. Some klips are defined like that:
	  //
	  // <channel>... </channel>
	  // <item>...</item>
	  // 
	  // and some like:
	  //
	  // <channel>
	  //   <item>...</item>
	  // </channel>
	  //
	  if ( [[aChild name] caseInsensitiveCompare: @"ITEM"] == NSOrderedSame ||
	       [[aChild name] caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame ||
	       [[aChild name] caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame ) 
	    {
	      Element *aElement;
	      
	      aElement = [[Element alloc] init];
	      
	      [self _parseNodeInformationUsingElement: aElement
		    node: aChild];
	      
	      [self addElement: aElement];
	      RELEASE(aElement);
	      
	      aChild = [aChild next];
	      continue;
	    }


	  aNode = [aChild firstChild];

	  while ( aNode )
	    {
	      if ( [aNode type] == textNodeType )
		{
		  if ( [[[aNode content] stringByTrimmingWhiteSpaces] length] > 0 )
		    {
		      break;
		    }
		}
	      
	      aNode = [aNode next];
	    }
	  
	  if ( !aNode )
	    {
	      aChild = [aChild next];
	      continue;
	    }
	  

	  aName = [aChild name];
	  aContent = [[aNode content] stringByTrimmingWhiteSpaces];
	  
	  if ( aName && aContent )
	    {
	      [self _updateElement: theElement
		    name: aName
		    content: aContent];
	    }
	  
	  aChild = [aChild next];
	}
    }
  else
    {
      NSLog(@"No children");
    }
  
  //
  // OS X XML Parsing Code
  //
#else

  for (i = 0; i < CFTreeGetChildCount((CFXMLTreeRef)theNode); i++)
    {
      NSString *aName, *aContent;
      CFXMLTreeRef xmlSubTreeRef;
      CFXMLNodeRef xmlSubNode;
      int j;
      
      xmlTreeRef = CFTreeGetChildAtIndex((CFXMLTreeRef)theNode, i);
      aNode = CFXMLTreeGetNode(xmlTreeRef);
      
      xmlSubTreeRef = NULL;
      xmlSubNode = NULL;

      for ( j = 0; j < CFTreeGetChildCount(xmlTreeRef); j++ )
	{
	  xmlSubTreeRef = CFTreeGetChildAtIndex(xmlTreeRef, j);
	  xmlSubNode = CFXMLTreeGetNode(xmlSubTreeRef);
          
	  if ( CFXMLNodeGetTypeCode(xmlNode) == kCFXMLNodeTypeElement )
	    {
	      break;
	    }
	  else
	    {
	      xmlSubNode = NULL;
	    }
	}
      
      if ( !xmlSubNode )
	{
	  continue;
	}

      aName = (NSString *)CFXMLNodeGetString(aNode);
      aContent = [(NSString *)CFXMLNodeGetString(xmlSubNode) stringByTrimmingWhiteSpaces];
      
      //
      // See description above.
      //
      if ( [aName caseInsensitiveCompare: @"ITEM"] == NSOrderedSame ||
	   [aName caseInsensitiveCompare: @"IMAGE"] == NSOrderedSame ||
	   [aName caseInsensitiveCompare: @"TEXTINPUT"] == NSOrderedSame ) 
	{
	  Element *aElement;
	  
	  aElement = [[Element alloc] init];
	  
	  [self _parseNodeInformationUsingElement: aElement
		node: (id)xmlTreeRef];
	  
	  [(Channel *)theElement addElement: aElement];
	  RELEASE(aElement);
	}
      
      if ( aName && aContent )
	{
	  [self _updateElement: theElement
		name: aName
		content: aContent];
	}
    }    
#endif
}

@end

