/*
 * Copyright (C) 2013 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include "LayoutRect.h"
#include "RenderBlockFlow.h"
#include "SimpleLineLayout.h"
#include "SimpleLineLayoutFlowContents.h"
#include <wtf/IteratorRange.h>
#include <wtf/text/WTFString.h>

namespace WebCore {
namespace SimpleLineLayout {

class RunResolver {
    WTF_MAKE_FAST_ALLOCATED;
public:
    class Iterator;

    class Run {
    public:
        explicit Run(const Iterator&);

        // Position relative to the enclosing flow block.
        unsigned start() const;
        unsigned end() const;
        // Position relative to the actual renderer.
        unsigned localStart() const;
        unsigned localEnd() const;

        float logicalLeft() const;
        float logicalRight() const;

        FloatRect rect() const;
        float expansion() const;
        ExpansionBehavior expansionBehavior() const;
        int baselinePosition() const;
        int baselineOffset() const { return m_iterator.resolver().m_baseline; }
        StringView text() const;
        String textWithHyphen() const;
        const RenderObject& renderer() const;
        bool isEndOfLine() const;
        bool hasHyphen() const { return m_iterator.simpleRun().hasHyphen; }
        bool isLineBreak() const { return m_iterator.simpleRun().isLineBreak; }
        const SimpleLineLayout::Run& simpleRun() const { return m_iterator.simpleRun(); }

        unsigned lineIndex() const;

    private:
        float computeBaselinePosition() const;
        void constructStringForHyphenIfNeeded();

        const Iterator& m_iterator;
    };

    class Iterator {
    friend class Run;
    friend class RunResolver;
    friend class LineResolver;
    public:
        Iterator(const RunResolver&, unsigned runIndex, unsigned lineIndex);

        Iterator& operator++();
        Iterator& operator--();

        bool operator==(const Iterator&) const;
        bool operator!=(const Iterator&) const;

        Run operator*() const;

        bool atBegin() const;

    private:
        const SimpleLineLayout::Run& simpleRun() const;
        unsigned lineIndex() const { return m_lineIndex; }
        Iterator& advance();
        Iterator& advanceLines(unsigned);
        const RunResolver& resolver() const { return *m_resolver; }
        const Layout& layout() const { return *m_layout; }

        RefPtr<const Layout> m_layout;
        const RunResolver* m_resolver;
        unsigned m_runIndex;
        unsigned m_lineIndex;
    };

    RunResolver(const RenderBlockFlow&, const Layout&);

    const RenderBlockFlow& flow() const { return m_flowRenderer; }
    const FlowContents& flowContents() const { return m_flowContents; }
    Iterator begin() const;
    Iterator end() const;

    WTF::IteratorRange<Iterator> rangeForRect(const LayoutRect&) const;
    WTF::IteratorRange<Iterator> rangeForRenderer(const RenderObject&) const;
    WTF::IteratorRange<Iterator> rangeForLine(unsigned lineIndex) const;
    Iterator runForPoint(const LayoutPoint&) const;
    WTF::IteratorRange<Iterator> rangeForRendererWithOffsets(const RenderObject&, unsigned start, unsigned end) const;

private:
    enum class IndexType { First, Last };
    unsigned lineIndexForHeight(LayoutUnit, IndexType) const;
    unsigned adjustLineIndexForStruts(LayoutUnit, IndexType, unsigned lineIndexCandidate) const;

    const RenderBlockFlow& m_flowRenderer;
    const Layout& m_layout;
    const FlowContents m_flowContents;
    const LayoutUnit m_lineHeight;
    const LayoutUnit m_baseline;
    const LayoutUnit m_borderAndPaddingBefore;
    const float m_ascent;
    const float m_descent;
    const float m_visualOverflowOffset;
};

class LineResolver {
public:
    class Iterator {
    public:
        explicit Iterator(RunResolver::Iterator);

        Iterator& operator++();
        bool operator==(const Iterator&) const;
        bool operator!=(const Iterator&) const;

        FloatRect operator*() const;
        // FIXME: Use a list to support multiple renderers per line.
        const RenderObject& renderer() const;

    private:
        RunResolver::Iterator m_runIterator;
    };

    LineResolver(const RunResolver&);

    Iterator begin() const;
    Iterator end() const;

    WTF::IteratorRange<Iterator> rangeForRect(const LayoutRect&) const;

private:
    const RunResolver& m_runResolver;
};

RunResolver runResolver(const RenderBlockFlow&, const Layout&);
LineResolver lineResolver(const RunResolver&);

inline unsigned RunResolver::Run::start() const
{
    return m_iterator.simpleRun().start;
}

inline unsigned RunResolver::Run::end() const
{
    return m_iterator.simpleRun().end;
}

inline float RunResolver::Run::logicalLeft() const
{
    return m_iterator.simpleRun().logicalLeft;
}

inline float RunResolver::Run::logicalRight() const
{
    return m_iterator.simpleRun().logicalRight;
}

inline float RunResolver::Run::expansion() const
{
    return m_iterator.simpleRun().expansion;
}

inline ExpansionBehavior RunResolver::Run::expansionBehavior() const
{
    return m_iterator.simpleRun().expansionBehavior;
}

inline int RunResolver::Run::baselinePosition() const
{
    return roundToInt(computeBaselinePosition());
}

inline bool RunResolver::Run::isEndOfLine() const
{
    return m_iterator.simpleRun().isEndOfLine;
}

inline unsigned RunResolver::Run::lineIndex() const
{
    return m_iterator.lineIndex();
}

inline RunResolver::Iterator& RunResolver::Iterator::operator++()
{
    return advance();
}

inline float RunResolver::Run::computeBaselinePosition() const
{
    auto& resolver = m_iterator.resolver();
    auto offset = resolver.m_borderAndPaddingBefore + resolver.m_lineHeight * lineIndex();
    if (!m_iterator.layout().hasLineStruts())
        return offset + resolver.m_baseline;
    for (auto& strutEntry : resolver.m_layout.struts()) {
        if (strutEntry.lineBreak > lineIndex())
            break;
        offset += strutEntry.offset;
    }
    return offset + resolver.m_baseline;
}

inline RunResolver::Iterator& RunResolver::Iterator::operator--()
{
    --m_runIndex;
    if (simpleRun().isEndOfLine)
        --m_lineIndex;
    return *this;
}

inline bool RunResolver::Iterator::operator==(const Iterator& other) const
{
    ASSERT(m_resolver == other.m_resolver);
    return m_runIndex == other.m_runIndex;
}

inline bool RunResolver::Iterator::operator!=(const Iterator& other) const
{
    return !(*this == other);
}

inline RunResolver::Run RunResolver::Iterator::operator*() const
{
    return Run(*this);
}

inline bool RunResolver::Iterator::atBegin() const
{
    return *this == resolver().begin();
}

inline const SimpleLineLayout::Run& RunResolver::Iterator::simpleRun() const
{
    return layout().runAt(m_runIndex);
}

inline RunResolver::Iterator RunResolver::begin() const
{
    return Iterator(*this, 0, 0);
}

inline RunResolver::Iterator RunResolver::end() const
{
    return Iterator(*this, m_layout.runCount(), m_layout.lineCount());
}

inline LineResolver::Iterator& LineResolver::Iterator::operator++()
{
    m_runIterator.advanceLines(1);
    return *this;
}

inline bool LineResolver::Iterator::operator==(const Iterator& other) const
{
    return m_runIterator == other.m_runIterator;
}

inline bool LineResolver::Iterator::operator!=(const Iterator& other) const
{
    return m_runIterator != other.m_runIterator;
}

inline LineResolver::Iterator LineResolver::begin() const
{
    return Iterator(m_runResolver.begin());
}

inline LineResolver::Iterator LineResolver::end() const
{
    return Iterator(m_runResolver.end());
}

inline WTF::IteratorRange<LineResolver::Iterator> LineResolver::rangeForRect(const LayoutRect& rect) const
{
    auto runRange = m_runResolver.rangeForRect(rect);
    return { Iterator(runRange.begin()), Iterator(runRange.end()) };
}

inline LineResolver lineResolver(const RunResolver& runResolver)
{
    return LineResolver(runResolver);
}

}
}
