// Copyright 1997-2000 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OHHTMLDisplayProcessor.h"

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>
#import <OWF/OWF.h>

#import "OWSGMLDTD-OHHTMLDTD.h"
#import "OHTextBuilder.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniHTML/Building.subproj/OHHTMLDisplayProcessor-ParagraphStyles.m,v 1.7 2000/01/20 00:27:45 kc Exp $")

@implementation OHHTMLDisplayProcessor (ParagraphStyles)

static OWSGMLTagType *addressTagType;
static OWSGMLTagType *blockquoteTagType;
static OWSGMLTagType *centerTagType;
static OWSGMLTagType *divTagType;
static OWSGMLTagType *xmpTagType;
static OWSGMLTagType *h1TagType;
static OWSGMLTagType *h2TagType;
static OWSGMLTagType *h3TagType;
static OWSGMLTagType *h4TagType;
static OWSGMLTagType *h5TagType;
static OWSGMLTagType *h6TagType;
static OWSGMLTagType *listingTagType;
static OWSGMLTagType *paragraphTagType;
static OWSGMLTagType *plaintextTagType;
static OWSGMLTagType *preTagType;
static OWSGMLTagType *styleTagType;

static unsigned int headerAlignAttributeIndex;
static unsigned int divAlignAttributeIndex;
static unsigned int xmpWidthAttributeIndex;
static unsigned int preWidthAttributeIndex;
static unsigned int paragraphAlignAttributeIndex;
static unsigned int plainTextWidthAttributeIndex;

+ (void)didLoad;
{
    OWSGMLDTD *dtd;
    OWSGMLMethods *methods;

    methods = [self sgmlMethods];

    dtd = [self dtd];

    addressTagType = [dtd tagTypeNamed:@"address"];
    blockquoteTagType = [dtd tagTypeNamed:@"blockquote"];
    centerTagType = [dtd tagTypeNamed:@"center"];
    divTagType = [dtd tagTypeNamed:@"div"];
    xmpTagType = [dtd tagTypeNamed:@"xmp"];
    [xmpTagType setContentIsNotValidSGML:YES];
    h1TagType = [dtd tagTypeNamed:@"h1"];
    h2TagType = [dtd tagTypeNamed:@"h2"];
    h3TagType = [dtd tagTypeNamed:@"h3"];
    h4TagType = [dtd tagTypeNamed:@"h4"];
    h5TagType = [dtd tagTypeNamed:@"h5"];
    h6TagType = [dtd tagTypeNamed:@"h6"];
    listingTagType = [dtd tagTypeNamed:@"listing"];
    [listingTagType setContentIsNotValidSGML:YES];
    paragraphTagType = [dtd tagTypeNamed:@"p"];
    plaintextTagType = [dtd tagTypeNamed:@"plaintext"];
    preTagType = [dtd tagTypeNamed:@"pre"];
    styleTagType = [dtd tagTypeNamed:@"style"];

    headerAlignAttributeIndex = [h1TagType addAttributeNamed:@"align"];
    [h2TagType shareAttributesWithTagType:h1TagType];
    [h3TagType shareAttributesWithTagType:h1TagType];
    [h4TagType shareAttributesWithTagType:h1TagType];
    [h5TagType shareAttributesWithTagType:h1TagType];
    [h6TagType shareAttributesWithTagType:h1TagType];
    // In Netscape, the <H7> tag is completely ignored, doesn't even start a paragraph.

    divAlignAttributeIndex = [divTagType addAttributeNamed:@"align"];
    xmpWidthAttributeIndex = [xmpTagType addAttributeNamed:@"width"];
    preWidthAttributeIndex = [preTagType addAttributeNamed:@"width"];
    paragraphAlignAttributeIndex = [paragraphTagType addAttributeNamed:@"align"];
    plainTextWidthAttributeIndex = [plaintextTagType addAttributeNamed:@"width"];


    [methods registerMethod:@"Address" forTagName:@"address"];
    [methods registerMethod:@"BlockQuote" forTagName:@"blockquote"];
    [methods registerMethod:@"Center" forTagName:@"center"];
    [methods registerMethod:@"Div" forTagName:@"div"];
    [methods registerMethod:@"EndCenter" forEndTagName:@"center"];
    [methods registerMethod:@"Example" forTagName:@"xmp"];
    [methods registerMethod:@"Header" forTagName:@"h1"];
    [methods registerMethod:@"Header" forTagName:@"h2"];
    [methods registerMethod:@"Header" forTagName:@"h3"];
    [methods registerMethod:@"Header" forTagName:@"h4"];
    [methods registerMethod:@"Header" forTagName:@"h5"];
    [methods registerMethod:@"Header" forTagName:@"h6"];
    [methods registerMethod:@"Preformatted" forTagName:@"listing"];
    [methods registerMethod:@"Paragraph" forTagName:@"p"];
    [methods registerMethod:@"EndParagraph" forEndTagName:@"p"];
    [methods registerMethod:@"Plaintext" forTagName:@"plaintext"];
    [methods registerMethod:@"Preformatted" forTagName:@"pre"];
    [methods registerMethod:@"IgnoredContents" forTagName:@"style"];
}

- (void)processAddressTag:(OWSGMLTag *)tag;
{
    BOOL oldFontItalic;

    [textBuilder beginParagraph];
    oldFontItalic = [textBuilder setFontItalic:YES];
    [self processContentForTag:tag];
    [textBuilder setFontItalic:oldFontItalic];
    [textBuilder endParagraph];
}

- (void)processBlockQuoteTag:(OWSGMLTag *)tag;
{
    float oldLeftIndent, oldRightIndent;
    float oldHangingIndent;

    [textBuilder beginParagraph];
    
    oldLeftIndent = [textBuilder leftIndent];
    oldHangingIndent = [textBuilder hangingIndent];
    [textBuilder setLeftIndent:oldLeftIndent + 40.0];
    oldRightIndent = [textBuilder rightIndent];
    [textBuilder setRightIndent:oldRightIndent - 40.0];

    [self processContentForTag:tag];

    [textBuilder endParagraph];
    
    [textBuilder setHangingIndent:oldHangingIndent];
    [textBuilder setLeftIndent:oldLeftIndent];
    [textBuilder setRightIndent:oldRightIndent];
}

- (void)processCenterTag:(OWSGMLTag *)tag;
{
    [textBuilder ensureAtStartOfLine];
    [textBuilder startCentering];
}

- (void)processEndCenterTag:(OWSGMLTag *)tag;
{
    [textBuilder ensureAtStartOfLine];
    [textBuilder stopCentering];
}

- (void)processDivTag:(OWSGMLTag *)tag;
{
    OWTextAlignment oldDivisionAlignment;
    NSString *alignment;
    
    [textBuilder ensureAtStartOfLine];
    alignment = sgmlTagValueForAttributeAtIndex(tag, divAlignAttributeIndex);
    if (alignment)
	oldDivisionAlignment = [textBuilder setDivisionAlignmentString:alignment];
    else
	oldDivisionAlignment = [textBuilder setDivisionAlignmentString:OW_UNSPECIFIED_ALIGNMENT];
	

    [self processContentForTag:tag];

    [textBuilder ensureAtStartOfLine];
    [textBuilder setDivisionAlignment:oldDivisionAlignment];
}

- (void)processExampleTag:(OWSGMLTag *)tag;
{
    BOOL oldPreserveWhitespace, oldObeyLines;
    OWFontStyle oldFontStyle;
    int oldLigatureStyle;
    id <OWSGMLToken> sgmlToken;
    OWSGMLTagType *tagType;
    
    tagType = [tag tagType];

    [textBuilder beginBlock];

    oldPreserveWhitespace = [textBuilder setPreserveWhitespace:YES];
    oldObeyLines = [textBuilder setObeyLines:YES];
    oldFontStyle = [textBuilder setFontStyle:FONTSTYLE_FIXEDPITCH];
    oldLigatureStyle = [textBuilder setLigatureType:0];
    [textBuilder setRequiredCharacterWidth:[sgmlTagValueForAttributeAtIndex(tag, xmpWidthAttributeIndex) intValue]];
    
    while ((sgmlToken = [objectCursor readObject])) {
        switch ([sgmlToken tokenType]) {
            case OWSGMLTokenTypeEndTag:
                if ([(OWSGMLTag *)sgmlToken tagType] == tagType)
                    goto endOfXMP;
            case OWSGMLTokenTypeCData:
            case OWSGMLTokenTypeStartTag:
                [textBuilder writeUnprocessedString:[sgmlToken string]];
                break;
            default:
                break;
        }
    }

endOfXMP:
    [textBuilder setLigatureType:oldLigatureStyle];
    [textBuilder setFontStyle:oldFontStyle];
    [textBuilder setObeyLines:oldObeyLines];
    [textBuilder setPreserveWhitespace:oldPreserveWhitespace];

    [textBuilder endBlock];
}


- (void)processHeaderTag:(OWSGMLTag *)tag;
{
    OWSGMLTagType *tagType;
    NSString *alignment;
    unsigned int fontSizeIndex;
    unsigned int oldFontSizeIndex;
    OWFontStyle oldFontStyle;

    tagType = [tag tagType];
    // Note that in Netscape, only font sizes 1-6 are used as headers, not 7 (no REALLY BIG headers).
    if (tagType == h1TagType)
	fontSizeIndex = 6;
    else if (tagType == h2TagType)
	fontSizeIndex = 5;
    else if (tagType == h3TagType)
	fontSizeIndex = 4;
    else if (tagType == h4TagType)
	fontSizeIndex = 3;
    else if (tagType == h5TagType)
	fontSizeIndex = 2;
    else if (tagType == h6TagType)
	fontSizeIndex = 1;
    else {
        OBASSERT(tagType == h1TagType); // Will fail
        // This should never happen
        fontSizeIndex = 3;
    }

    [textBuilder beginBlock];
    
    alignment = sgmlTagValueForAttributeAtIndex(tag, headerAlignAttributeIndex);
    if (alignment)
	[textBuilder setParagraphAlignmentString:alignment];
    
    oldFontStyle = [textBuilder setFontStyle:FONTSTYLE_TITLE];
    oldFontSizeIndex = [textBuilder setFontSizeIndex:fontSizeIndex];

    [self processContentForTag:tag];
    
    [textBuilder endBlock];
    
    [textBuilder setFontStyle:oldFontStyle];
    [textBuilder setFontSizeIndex:oldFontSizeIndex];
}

- (void)processPreformattedTag:(OWSGMLTag *)tag;
{
    BOOL oldLineBreakMode;
    BOOL oldPreserveWhitespace, oldObeyLines;
    OWFontStyle oldFontStyle;
    int oldLigatureStyle;

    [textBuilder beginBlock];

    oldLineBreakMode = [textBuilder setLineBreakMode:NSLineBreakByClipping];
    oldPreserveWhitespace = [textBuilder setPreserveWhitespace:YES];
    oldObeyLines = [textBuilder setObeyLines:YES];
    oldFontStyle = [textBuilder setFontStyle:FONTSTYLE_FIXEDPITCH];
    oldLigatureStyle = [textBuilder setLigatureType:0];
    [textBuilder setRequiredCharacterWidth:[sgmlTagValueForAttributeAtIndex(tag, preWidthAttributeIndex) intValue]];

    [self processContentForTag:tag];

    [textBuilder setLigatureType:oldLigatureStyle];
    [textBuilder setFontStyle:oldFontStyle];
    [textBuilder setObeyLines:oldObeyLines];
    [textBuilder setPreserveWhitespace:oldPreserveWhitespace];
    [textBuilder setLineBreakMode:oldLineBreakMode];

    [textBuilder endBlock];
}

- (void)processParagraphTag:(OWSGMLTag *)tag;
{
    NSString *alignment;
    
    // Note in HTML+ paragraphs will have attributes: id, align, lang, index.

    // Consecutive paragraphs are illegal in HTML, unless we're in obeylines mode.
    [textBuilder beginParagraph];
    alignment = sgmlTagValueForAttributeAtIndex(tag, paragraphAlignAttributeIndex);
    if (alignment)
	[textBuilder setParagraphAlignmentString:alignment];
}

- (void)processEndParagraphTag:(OWSGMLTag *)tag;
{
    [textBuilder endParagraph];
}

// This is close, but not perfect
- (void)processPlaintextTag:(OWSGMLTag *)tag;
{
    unsigned int width;
    id <OWSGMLToken> sgmlToken;
    
    width = [sgmlTagValueForAttributeAtIndex(tag, plainTextWidthAttributeIndex) intValue];

    [textBuilder beginBlock];

    [textBuilder setPreserveWhitespace:YES];
    [textBuilder setObeyLines:YES];
    [textBuilder setFontStyle:FONTSTYLE_FIXEDPITCH];
    [textBuilder setLigatureType:0];
    [textBuilder setRequiredCharacterWidth:width];
    
    while ((sgmlToken = [objectCursor readObject])) {
        switch ([sgmlToken tokenType]) {
            case OWSGMLTokenTypeCData:
            case OWSGMLTokenTypeStartTag:
            case OWSGMLTokenTypeEndTag:
                [textBuilder writeUnprocessedString:[sgmlToken string]];
                break;
            default:
                break;
        }
    }
}

@end
