// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * SVG <text> and <tspan> implementation
 *
 * Author:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *   Jon A. Cruz <jon@joncruz.org>
 *   Abhishek Sharma
 *
 * Copyright (C) 1999-2002 Lauris Kaplinski
 * Copyright (C) 2000-2001 Ximian, Inc.
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

/*
 * fixme:
 *
 * These subcomponents should not be items, or alternately
 * we have to invent set of flags to mark, whether standard
 * attributes are applicable to given item (I even like this
 * idea somewhat - Lauris)
 *
 */

#include "sp-string.h"
#include "style.h"

/*#####################################################
#  SPSTRING
#####################################################*/

SPString::SPString() : SPObject() {

    // This is dangerous but strings shouldn't have style.
    // delete (style);
    // style = nullptr;
}

SPString::~SPString() = default;

void SPString::build(SPDocument *doc, Inkscape::XML::Node *repr) {

    read_content();

    SPObject::build(doc, repr);
}

void SPString::release() {
    SPObject::release();
}


void SPString::read_content() {

    string.clear();

    //XML Tree being used directly here while it shouldn't be.
    gchar const *xml_string = getRepr()->content();

    // std::cout << ">" << (xml_string?xml_string:"Null") << "<" << std::endl;

    // SVG2/CSS Text Level 3 'white-space' has five values.
    // See: http://dev.w3.org/csswg/css-text/#white-space
    //            |  New Lines |  Spaces/Tabs | Text Wrapping
    //   ---------|------------|--------------|--------------
    //   normal   |  Collapse  |   Collapse   |     Wrap
    //   pre      |  Preserve  |   Preserve   |   No Wrap
    //   nowrap   |  Collapse  |   Collapse   |   No Wrap
    //   pre-wrap |  Preserve  |   Preserve   |     Wrap
    //   pre-line |  Preserve  |   Collapse   |     Wrap

    // 'xml:space' has two values:
    //   'default' which corresponds to 'normal' (without wrapping).
    //   'preserve' which corresponds to 'pre' except new lines are converted to spaces.
    //  See algorithms described in svg 1.1 section 10.15

    bool collapse_space = true;
    bool collapse_line  = true;
    bool is_css         = false;

    // Strings don't have style, check parent for style
    if( parent && parent->style ) {
        if( parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE     ||
            parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP ||
            parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRELINE  ) {
            collapse_line = false;
        }
        if( parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE     ||
            parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP ) {
            collapse_space = false;
        }
        if( parent->style->white_space.computed != SP_CSS_WHITE_SPACE_NORMAL ) {
            is_css = true; // If white-space not normal, we assume white-space is set.
        }
    }
    if( !is_css ) {
        // SVG 2: Use 'xml:space' only if 'white-space' not 'normal'.
        if (xml_space.value == SP_XML_SPACE_PRESERVE) {
            collapse_space = false;
        }
    }

    bool white_space = false;
    for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) {

        gunichar c = g_utf8_get_char(xml_string);
        switch (c) {
            case 0xd: // Carriage return
                // XML Parsers convert 0xa, 0xd, 0xD 0xA to 0xA. CSS also follows this rule so we
                // should never see 0xd.
                std::cerr << "SPString: Carriage Return found! Argh!" << std::endl;
                continue;
                break;
            case 0xa: // Line feed
                if( collapse_line ) {
                    if( !is_css && collapse_space ) continue; // xml:space == 'default' strips LFs.
                    white_space = true;        // Convert to space and collapse
                } else {
                    string += c;       // Preserve line feed
                    continue;
                }
                break;
            case '\t': // Tab
                if( collapse_space ) {
                    white_space = true;        // Convert to space and collapse
                } else {
                    string += c;       // Preserve tab
                    continue;
                }
                break;
            case ' ': // Space
                if( collapse_space ) {
                    white_space = true;        // Collapse white space
                } else {
                    string += c;       // Preserve space
                    continue;
                }
                break;
            default:
                if( white_space && (!string.empty() || (getPrev() != nullptr))) {
                    string += ' ';
                }
                string += c;
                white_space = false;

        } // End switch
    } // End loop

    // Insert white space at end if more text follows
    if (white_space && getRepr()->next() != nullptr) { // can't use SPObject::getNext() when the SPObject tree is still being built
        string += ' ';
    }

    // std::cout << ">" << string << "<" << std::endl;
    requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
}

void SPString::update(SPCtx * /*ctx*/, unsigned /*flags*/) {

    // if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_MODIFIED_FLAG)) {
    //     /* Parent style or we ourselves changed, so recalculate */
    //     flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // won't be "just a transformation" anymore, we're going to recompute "x" and "y" attributes
    // }
}


/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
