//
//  WebHTMLDocumentRepresentation.m
//  mySTEP
//
//  Created by Dr. H. Nikolaus Schaller on Mon Jan 05 2006.
//  Revised May 2006
//  Copyright (c) 2004 DSITRI. All rights reserved.
//
//  parse HTML document into DOM node structure (using NSXMLParser as the scanner)

#import "Private.h"
#import "WebHTMLDocumentRepresentation.h"
#import <WebKit/WebDocument.h>
#import <Foundation/NSXMLParser.h>

@implementation _WebHTMLDocumentRepresentation

// methods from WebDocumentRepresentation protocol

- (void) finishedLoadingWithDataSource:(WebDataSource *) source;
{
	NSLog(@"WebHTMLDocumentRepresentation finishedLoadingWithDataSource");
}

- (void) receivedData:(NSData *) data withDataSource:(WebDataSource *) source;
{
#if 1
	NSLog(@"WebHTMLDocumentRepresentation receivedData");
	NSLog(@"document source: %@", [self documentSource]);
#endif
	/*
	[_webFrame loadData:data
			   MIMEType:[_response MIMEType]
	   textEncodingName:[self textEncodingName]
				baseURL:[_request URL]];
	 */
	_parser=[[NSXMLParser alloc] initWithData:data];
	[_parser _setAcceptHTML:YES];	// be lazy about syntax and tag nesting rules
	// install full HTML entity translation table
	[_parser setDelegate:self];
#if 0
	NSLog(@"parser: %@", _parser);
#endif
	// FIXME: we should have incremental parsing!
	if(![_parser parse])	// as far as we come :-)
		NSLog(@"parse failed due to %@", [_parser parserError]);
	[_parser release];
}

- (void) receivedError:(NSError *) error withDataSource:(WebDataSource *) source;
{
	NSLog(@"WebHTMLDocumentRepresentation receivedError: %@", error);
}

- (void) setDataSource:(WebDataSource *) dataSource; { _dataSource=dataSource; }

- (NSString *) title; { return [[_root head] title]; }

- (BOOL) canProvideDocumentSource; { return YES; }

- (NSString *) documentSource;
{
	[_dataSource textEncodingName];
	// translate
	return [[[NSString alloc] initWithData:[_dataSource data] encoding:NSUTF8StringEncoding] autorelease];
}

// XML Parser delegate methods for parsing HTML. We change the current delegate on the fly so that DOMNodes receive the appropriate calls

- (void) parser:(NSXMLParser *) parser parseErrorOccurred:(NSError *) parseError;
{
	NSLog(@"HTML parser parseErrorOccurred: %@", parseError);
}

- (void) parser:(NSXMLParser *) parser foundCharacters:(NSString *) string;
{
	NSLog(@"HTML parser foundCharacters: %@", string);
}

- (void) parser:(NSXMLParser *) parser foundIgnorableWhitespace:(NSString *) whitespaceString;
{
	// might be needed to track to add or not add an implicit space character
}

- (void) parser:(NSXMLParser *) parser didStartElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name attributes:(NSDictionary *) attributes;
{ // handle top level opening tags
	tag=[tag lowercaseString];	// make case insensitive
#if 0
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	if(!_root && [tag isEqualToString:@"html"])
		{
		_root=[DOMHTMLDocument new];
		[parser setDelegate:_root];
		return;
		}
	if([tag isEqualToString:@"head"] || [tag isEqualToString:@"body"])
		{ // process by virtual <html></html> tags
		_root=[DOMHTMLDocument new];
		[parser setDelegate:_root];
		[_root parser:parser didStartElement:tag namespaceURI:URI qualifiedName:name attributes:attributes];
		return;
		}
	else
		{ // virtual <html><head></head><body><body></html>
		DOMHTMLBodyElement *body;
		_root=[DOMHTMLDocument new];
		body=[DOMHTMLBodyElement new];
		[_root setBody:body];
		[body setParent:self];	// come back here on </body>
		[parser setDelegate:body];
		[_root parser:parser didStartElement:tag namespaceURI:URI qualifiedName:name attributes:attributes];
		}
}

- (void) parser:(NSXMLParser *) parser didEndElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name;
{ // handle closing tags
	tag=[tag lowercaseString];	// make case insensitive
#if 1
	NSLog(@"HTML parser %@: </%@>", [parser _tagPath], tag);
#endif
}

@end

@implementation DOMHTMLElement

- (void) setParent:(id) parent; { _parent=parent; }

- (void) parser:(NSXMLParser *) parser parseErrorOccurred:(NSError *) parseError;
{
	NSLog(@"HTML parser parseErrorOccurred: %@", parseError);
}

- (void) parser:(NSXMLParser *) parser foundCharacters:(NSString *) string;
{
	NSLog(@"HTML parser foundCharacters: %@", string);
	if(_html)
		_html=[_html stringByAppendingString:string];	// collect string between tags
	else
		_html=string;	// not retained!
}

- (void) parser:(NSXMLParser *) parser foundIgnorableWhitespace:(NSString *) whitespaceString;
{
	// might be needed to track to add or not add an implicit space character
}

@end

@implementation DOMHTMLDocument

- (DOMHTMLHeadElement *) head; { return _head; }
- (void) setBody:(DOMHTMLBodyElement *) body; { ASSIGN(_body, body); }

- (void) dealloc;
{
	[_head release];
	[_body release];
	[super dealloc];
}

- (void) parser:(NSXMLParser *) parser didStartElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name attributes:(NSDictionary *) attributes;
{ // handle opening tags
	tag=[tag lowercaseString];	// make case insensitive
#if 0
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	if(!_head && [tag isEqualToString:@"head"])
		{
		_head=[DOMHTMLHeadElement new];
		[_head setParent:self];	// come back here on </head>
		[parser setDelegate:_head];
		return;
		}
	if(!_body && [tag isEqualToString:@"body"])
		{ // process by virtual <html></html> tags
		_body=[DOMHTMLBodyElement new];
		[_body setParent:self];	// come back here on </body>
		[parser setDelegate:_body];
		return;
		}
	NSLog(@"unexpected tag: <%@>", tag);
}

- (void) parser:(NSXMLParser *) parser didEndElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name;
{ // handle closing tags
	tag=[tag lowercaseString];	// make case insensitive
#if 0
	NSLog(@"HTML parser %@: </%@>", [parser _tagPath], tag);
#endif
	if([tag isEqualToString:@"http"])
		;	// done
}

@end

@implementation DOMHTMLHeadElement

- (void) dealloc;
{
	[_title release];
	[_meta release];
	[super dealloc];
}

- (NSString *) title; { return _title; }

- (void) parser:(NSXMLParser *) parser didStartElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name attributes:(NSDictionary *) attributes;
{ // handle opening tags
	tag=[tag lowercaseString];	// make case insensitive
#if 0
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	if([tag isEqualToString:@"title"])
		{
		[_html release];
		_html=nil;	// start collecting characters between <title> and </title>
		return;
		}
	if([tag isEqualToString:@"meta"])
		{
		NSString *attrib;
		if((attrib=[attributes objectForKey:@"name"]))
			[attributes objectForKey:@"content"];
		else if((attrib=[attributes objectForKey:@"http-equiv"]))
			[attributes objectForKey:@"content"];
		return;
		}
	if([tag isEqualToString:@"style"])
		{
		// create HTMLStyle node and make the delegate
		// save attributes, e.g. media="all" type="text/css"
		// it will store and process the style on receiving a </style> tag
		return;
		}
	NSLog(@"unexpected tag: <%@>", tag);
}

- (void) parser:(NSXMLParser *) parser didEndElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name;
{ // handle opening tags
	tag=[tag lowercaseString];	// make case insensitive
#if 0
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	if([tag isEqualToString:@"head"])
		{
		[parser setDelegate:_parent];	// go back
		return;
		}
	if([tag isEqualToString:@"title"])
		{
		ASSIGN(_title, _html);
#if 1
		NSLog(@"Document Title: %@", _title);
#endif
		return;
		}
	if([tag isEqualToString:@"meta"])
		{
		return;
		}
	NSLog(@"unexpected tag: </%@>", tag);
}

@end

@implementation DOMHTMLBodyElement

- (void) parser:(NSXMLParser *) parser didStartElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name attributes:(NSDictionary *) attributes;
{ // handle opening tags
	NSString *class;
	NSString *identifier;
	tag=[tag lowercaseString];	// make case insensitive
#if 1
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	class=[attributes objectForKey:@"class"];
	identifier=[attributes objectForKey:@"id"];
	if([tag isEqualToString:@"div"])
		{
		return;
		}
	if([tag isEqualToString:@"span"])
		{
		return;
		}
	if([tag isEqualToString:@"table"])
		{
		return;
		}
	if([tag isEqualToString:@"frame"])
		{
		return;
		}
	if([tag isEqualToString:@"form"])
		{
		return;
		}
	if([tag isEqualToString:@"h1"])
		{
		return;
		}
	if([tag isEqualToString:@"h2"])
		{
		return;
		}
	if([tag isEqualToString:@"h3"])
		{
		return;
		}
	if([tag isEqualToString:@"h4"])
		{
		return;
		}
	if([tag isEqualToString:@"h5"])
		{
		return;
		}
	if([tag isEqualToString:@"h6"])
		{
		return;
		}
	if([tag isEqualToString:@"hr"])
		{
		return;
		}
	NSLog(@"unexpected tag: <%@>", tag);
}

- (void) parser:(NSXMLParser *) parser didEndElement:(NSString *) tag namespaceURI:(NSString *) URI qualifiedName:(NSString *) name;
{ // handle opening tags
	tag=[tag lowercaseString];	// make case insensitive
#if 1
	NSLog(@"HTML parser %@: <%@>", [parser _tagPath], tag);
#endif
	if([tag isEqualToString:@"body"])
		{
		[parser setDelegate:_parent];	// go back
		return;
		}
	NSLog(@"unexpected tag: </%@>", tag);
}

@end

