/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkCanvas.h"
#include "include/core/SkDrawLooper.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRect.h"
#include "src/core/SkArenaAlloc.h"

void SkDrawLooper::Context::Info::applyToCTM(SkMatrix* ctm) const {
    if (fApplyPostCTM) {
        ctm->postTranslate(fTranslate.fX, fTranslate.fY);
    } else {
        ctm->preTranslate(fTranslate.fX, fTranslate.fY);
    }
}

void SkDrawLooper::Context::Info::applyToCanvas(SkCanvas* canvas) const {
    if (fApplyPostCTM) {
        SkMatrix ctm = canvas->getTotalMatrix();
        ctm.postTranslate(fTranslate.fX, fTranslate.fY);
        canvas->setMatrix(ctm);
    } else {
        canvas->translate(fTranslate.fX, fTranslate.fY);
    }
}

bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) const {
    SkSTArenaAlloc<48> alloc;

    SkDrawLooper::Context* context = this->makeContext(&alloc);
    for (;;) {
        SkPaint p(paint);
        SkDrawLooper::Context::Info info;
        if (context->next(&info, &p)) {
            if (!p.canComputeFastBounds()) {
                return false;
            }
        } else {
            break;
        }
    }
    return true;
}

void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& s,
                                     SkRect* dst) const {
    // src and dst rects may alias and we need to keep the original src, so copy it.
    const SkRect src = s;

    SkSTArenaAlloc<48> alloc;

    *dst = src;   // catch case where there are no loops
    SkDrawLooper::Context* context = this->makeContext(&alloc);

    for (bool firstTime = true;; firstTime = false) {
        SkPaint p(paint);
        SkDrawLooper::Context::Info info;
        if (context->next(&info, &p)) {
            SkRect r(src);

            p.computeFastBounds(r, &r);
            r.offset(info.fTranslate.fX, info.fTranslate.fY);

            if (firstTime) {
                *dst = r;
            } else {
                dst->join(r);
            }
        } else {
            break;
        }
    }
}

bool SkDrawLooper::asABlurShadow(BlurShadowRec*) const {
    return false;
}

void SkDrawLooper::apply(SkCanvas* canvas, const SkPaint& paint,
                         std::function<void(SkCanvas*, const SkPaint&)> proc) {
    SkSTArenaAlloc<256> alloc;
    Context* ctx = this->makeContext(&alloc);
    if (ctx) {
        Context::Info info;
        for (;;) {
            SkPaint p = paint;
            if (!ctx->next(&info, &p)) {
                break;
            }
            canvas->save();
            if (info.fApplyPostCTM) {
                SkMatrix ctm = canvas->getTotalMatrix();
                ctm.postTranslate(info.fTranslate.fX, info.fTranslate.fY);
                canvas->setMatrix(ctm);
            } else {
                canvas->translate(info.fTranslate.fX, info.fTranslate.fY);
            }
            proc(canvas, p);
            canvas->restore();
        }
    }
}