/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#include "SkMatrixParts.h"
#include "SkAnimateMaker.h"
#include "SkDrawMatrix.h"
#include "SkDrawRectangle.h"
#include "SkDrawPath.h"

SkMatrixPart::SkMatrixPart() : fMatrix(NULL) {
}

void SkMatrixPart::dirty() {
    fMatrix->dirty();
}

SkDisplayable* SkMatrixPart::getParent() const {
    return fMatrix;
}

bool SkMatrixPart::setParent(SkDisplayable* parent) {
    SkASSERT(parent != NULL);
    if (parent->isMatrix() == false)
        return true;
    fMatrix = (SkDrawMatrix*) parent;
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkRotate::fInfo[] = {
    SK_MEMBER(center, Point),
    SK_MEMBER(degrees, Float)
};

#endif

DEFINE_GET_MEMBER(SkRotate);

SkRotate::SkRotate() : degrees(0) {
    center.fX = center.fY = 0;
}

bool SkRotate::add() {
    fMatrix->rotate(degrees, center);
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkScale::fInfo[] = {
    SK_MEMBER(center, Point),
    SK_MEMBER(x, Float),
    SK_MEMBER(y, Float)
};

#endif

DEFINE_GET_MEMBER(SkScale);

SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) {
    center.fX = center.fY = 0;
}

bool SkScale::add() {
    fMatrix->scale(x, y, center);
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkSkew::fInfo[] = {
    SK_MEMBER(center, Point),
    SK_MEMBER(x, Float),
    SK_MEMBER(y, Float)
};

#endif

DEFINE_GET_MEMBER(SkSkew);

SkSkew::SkSkew() : x(0), y(0) {
    center.fX = center.fY = 0;
}

bool SkSkew::add() {
    fMatrix->skew(x, y, center);
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkTranslate::fInfo[] = {
    SK_MEMBER(x, Float),
    SK_MEMBER(y, Float)
};

#endif

DEFINE_GET_MEMBER(SkTranslate);

SkTranslate::SkTranslate() : x(0), y(0) {
}

bool SkTranslate::add() {
    fMatrix->translate(x, y);
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkFromPath::fInfo[] = {
    SK_MEMBER(mode, FromPathMode),
    SK_MEMBER(offset, Float),
    SK_MEMBER(path, Path)
};

#endif

DEFINE_GET_MEMBER(SkFromPath);

SkFromPath::SkFromPath() :
    mode(0), offset(0), path(NULL) {
}

SkFromPath::~SkFromPath() {
}

bool SkFromPath::add() {
    if (path == NULL)
        return true;
    static const uint8_t gFlags[] = {
        SkPathMeasure::kGetPosAndTan_MatrixFlag,    // normal
        SkPathMeasure::kGetTangent_MatrixFlag,      // angle
        SkPathMeasure::kGetPosition_MatrixFlag      // position
    };
    if ((unsigned)mode >= SK_ARRAY_COUNT(gFlags))
        return true;
    SkMatrix result;
    fPathMeasure.setPath(&path->getPath(), false);
    if (fPathMeasure.getMatrix(offset, &result, (SkPathMeasure::MatrixFlags)gFlags[mode]))
        fMatrix->set(result);
    return false;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkRectToRect::fInfo[] = {
    SK_MEMBER(destination, Rect),
    SK_MEMBER(source, Rect)
};

#endif

DEFINE_GET_MEMBER(SkRectToRect);

SkRectToRect::SkRectToRect() :
    source(NULL), destination(NULL) {
}

SkRectToRect::~SkRectToRect() {
}

bool SkRectToRect::add() {
    if (source == NULL || destination == NULL)
        return true;
    SkMatrix temp;
    temp.setRectToRect(source->fRect, destination->fRect,
                       SkMatrix::kFill_ScaleToFit);
    fMatrix->set(temp);
    return false;
}

#ifdef SK_DUMP_ENABLED
void SkRectToRect::dump(SkAnimateMaker* maker) {
    dumpBase(maker);
    SkDebugf("/>\n");
    SkDisplayList::fIndent += 4;
    if (source) {
        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
        SkDisplayList::fIndent += 4;
        source->dump(maker);
        SkDisplayList::fIndent -= 4;
        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
    }
    if (destination) {
        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
        SkDisplayList::fIndent += 4;
        destination->dump(maker);
        SkDisplayList::fIndent -= 4;
        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");
    }
    SkDisplayList::fIndent -= 4;
    dumpEnd(maker);
}
#endif

const SkMemberInfo* SkRectToRect::preferredChild(SkDisplayTypes ) {
    if (source == NULL)
        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
    else {
        SkASSERT(destination == NULL);
        return getMember("destination");
    }
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkPolyToPoly::fInfo[] = {
    SK_MEMBER(destination, Polygon),
    SK_MEMBER(source, Polygon)
};

#endif

DEFINE_GET_MEMBER(SkPolyToPoly);

SkPolyToPoly::SkPolyToPoly() : source(NULL), destination(NULL) {
}

SkPolyToPoly::~SkPolyToPoly() {
}

bool SkPolyToPoly::add() {
    SkASSERT(source);
    SkASSERT(destination);
    SkPoint src[4];
    SkPoint dst[4];
    SkPath& sourcePath = source->getPath();
    int srcPts = sourcePath.getPoints(src, 4);
    SkPath& destPath = destination->getPath();
    int dstPts = destPath.getPoints(dst, 4);
    if (srcPts != dstPts)
        return true;
    SkMatrix temp;
    temp.setPolyToPoly(src, dst, srcPts);
    fMatrix->set(temp);
    return false;
}

#ifdef SK_DUMP_ENABLED
void SkPolyToPoly::dump(SkAnimateMaker* maker) {
    dumpBase(maker);
    SkDebugf("/>\n");
    SkDisplayList::fIndent += 4;
    if (source) {
        SkDebugf("%*s<source>\n", SkDisplayList::fIndent, "");
        SkDisplayList::fIndent += 4;
        source->dump(maker);
        SkDisplayList::fIndent -= 4;
        SkDebugf("%*s</source>\n", SkDisplayList::fIndent, "");
    }
    if (destination) {
        SkDebugf("%*s<destination>\n", SkDisplayList::fIndent, "");
        SkDisplayList::fIndent += 4;
        destination->dump(maker);
        SkDisplayList::fIndent -= 4;
        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");
    }
    SkDisplayList::fIndent -= 4;
    dumpEnd(maker);
}
#endif

void SkPolyToPoly::onEndElement(SkAnimateMaker& ) {
    SkASSERT(source);
    SkASSERT(destination);
    if (source->childHasID() || destination->childHasID())
        fMatrix->setChildHasID();
}

const SkMemberInfo* SkPolyToPoly::preferredChild(SkDisplayTypes ) {
    if (source == NULL)
        return getMember("source"); // !!! cwap! need to refer to member through enum like kScope instead
    else {
        SkASSERT(destination == NULL);
        return getMember("destination");
    }
}