//----------------------------------------------------------------------------//
void BasicRenderedStringParser::appendRenderedText(RenderedString& rs,
                                                   const String& text) const
{
    size_t cpos = 0;
    // split the given string into lines based upon the newline character
    while (text.length() > cpos)
    {
        // find next newline
        const size_t nlpos = text.find('\n', cpos);
        // calculate length of this substring
        const size_t len =
            ((nlpos != String::npos) ? nlpos : text.length()) - cpos;

        // construct new text component and append it.
        RenderedStringTextComponent rtc(text.substr(cpos, len), d_fontName);
        rtc.setPadding(d_padding);
        rtc.setColours(d_colours);
        rtc.setVerticalFormatting(d_vertAlignment);
        rtc.setAspectLock(d_aspectLock);
        rs.appendComponent(rtc);

        // break line if needed
        if (nlpos != String::npos)
            rs.appendLineBreak();

        // advance current position.  +1 to skip the \n char
        cpos += len + 1;
    }
}
//----------------------------------------------------------------------------//
RenderedString DefaultRenderedStringParser::parse(
    const String& input_string,
    const Font* initial_font,
    const ColourRect* initial_colours)
{
    RenderedString rs;

    size_t epos, spos = 0;

    while ((epos = input_string.find('\n', spos)) != String::npos)
    {
        appendSubstring(rs, input_string.substr(spos, epos - spos),
                        initial_font, initial_colours);
        rs.appendLineBreak();

        // set new start position (skipping the previous \n we found)
        spos = epos + 1;
    }

    if (spos < input_string.length())
        appendSubstring(rs, input_string.substr(spos),
                        initial_font, initial_colours);

    return rs;
}
//----------------------------------------------------------------------------//
RenderedString DefaultRenderedStringParser::parse(
                                        const String& input_string,
                                        Font* initial_font,
                                        const ColourRect* initial_colours)
{
    RenderedString rs;
    RenderedStringTextComponent rstc(input_string, initial_font);

    if (initial_colours)
        rstc.setColours(*initial_colours);

    rs.appendComponent(rstc);
    return rs;
}
//----------------------------------------------------------------------------//
void BasicRenderedStringParser::handleWindow(RenderedString& rs, const String& value)
{
    RenderedStringWidgetComponent rwc(value);
    rwc.setPadding(d_padding);
    rwc.setVerticalFormatting(d_vertAlignment);
    rwc.setAspectLock(d_aspectLock);
    rs.appendComponent(rwc);
}
//----------------------------------------------------------------------------//
void BasicRenderedStringParser::handleImage(RenderedString& rs, const String& value)
{
    RenderedStringImageComponent ric(
        PropertyHelper<Image*>::fromString(value));
    ric.setPadding(d_padding);
    ric.setColours(d_colours);
    ric.setVerticalFormatting(d_vertAlignment);
    ric.setSize(d_imageSize);
    ric.setAspectLock(d_aspectLock);
    rs.appendComponent(ric);
}
//----------------------------------------------------------------------------//
void appendSubstring(RenderedString& rs,
                     const String& string,
                     const Font* initial_font,
                     const ColourRect* initial_colours)
{
    RenderedStringTextComponent rstc(string, initial_font);

    if (initial_colours)
        rstc.setColours(*initial_colours);

    rs.appendComponent(rstc);
}
//----------------------------------------------------------------------------//
void RenderedString::split(const size_t line, float split_point,
                           RenderedString& left)
{
    // FIXME: This function is big and nasty; it breaks all the rules for a
    // 'good' function and desperately needs some refactoring work done to it.
    // On the plus side, it does seem to work though ;)

    if (line >= getLineCount())
        CEGUI_THROW(InvalidRequestException("RenderedString::split: "
            "line number specified is invalid."));

    left.clearComponents();

    if (d_components.empty())
        return;

    // move all components in lines prior to the line being split to the left
    if (line > 0)
    {
        // calculate size of range
        const size_t sz = d_lines[line - 1].first + d_lines[line - 1].second;
        // range start
        ComponentList::iterator cb = d_components.begin();
        // range end (exclusive)
        ComponentList::iterator ce = cb + sz;
        // copy components to left side
        left.d_components.assign(cb, ce);
        // erase components from this side.
        d_components.erase(cb, ce);

        LineList::iterator lb = d_lines.begin();
        LineList::iterator le = lb + line;
        // copy lines to left side
        left.d_lines.assign(lb, le);
        // erase lines from this side
        d_lines.erase(lb, le);
    }

    // find the component where the requested split point lies.
    float partial_extent = 0;

    size_t idx = 0;
    const size_t last_component = d_lines[0].second;
    for (; idx < last_component; ++idx)
    {
        partial_extent += d_components[idx]->getPixelSize().d_width;

        if (split_point <= partial_extent)
            break;
    }

    // case where split point is past the end
    if (idx >= last_component)
    {
        // transfer this line's components to the 'left' string.
        //
        // calculate size of range
        const size_t sz = d_lines[0].second;
        // range start
        ComponentList::iterator cb = d_components.begin();
        // range end (exclusive)
        ComponentList::iterator ce = cb + sz;
        // copy components to left side
        left.d_components.insert(left.d_components.end(), cb, ce);
        // erase components from this side.
        d_components.erase(cb, ce);

        // copy line info to left side
        left.d_lines.push_back(d_lines[0]);
        // erase line from this side
        d_lines.erase(d_lines.begin());

        // fix up lines in this object
        for (size_t comp = 0, i = 0; i < d_lines.size(); ++i)
        {
            d_lines[i].first = comp;
            comp += d_lines[i].second;
        }

        return;
    }

    left.appendLineBreak();
    const size_t left_line = left.getLineCount() - 1;
    // Everything up to 'idx' is xfered to 'left'
    for (size_t i = 0; i < idx; ++i)
    {
        left.d_components.push_back(d_components[0]);
        d_components.erase(d_components.begin());
        ++left.d_lines[left_line].second;
        --d_lines[0].second;
    }

    // now to split item 'idx' putting half in left and leaving half in this.
    RenderedStringComponent* c = d_components[0];
    if (c->canSplit())
    {
        RenderedStringComponent* lc = 
            c->split(split_point - (partial_extent - c->getPixelSize().d_width),
                     idx == 0);

        if (lc)
        {
            left.d_components.push_back(lc);
            ++left.d_lines[left_line].second;
        }
    }
    // can't split, if component width is >= split_point xfer the whole
    // component to it's own line in the left part (FIX #306)
    else if (c->getPixelSize().d_width >= split_point)
    {
        left.appendLineBreak();
        left.d_components.push_back(d_components[0]);
        d_components.erase(d_components.begin());
        ++left.d_lines[left_line + 1].second;
        --d_lines[0].second;
    }

    // fix up lines in this object
    for (size_t comp = 0, i = 0; i < d_lines.size(); ++i)
    {
        d_lines[i].first = comp;
        comp += d_lines[i].second;
    }
}