/*
 * 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 "SkDrawMatrix.h"
#include "SkAnimateMaker.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include "SkParse.h"
#include "SkMatrixParts.h"
#include "SkScript.h"
#include "SkTypedArray.h"

enum SkDrawMatrix_Properties {
    SK_PROPERTY(perspectX),
    SK_PROPERTY(perspectY),
    SK_PROPERTY(rotate),
    SK_PROPERTY(scale),
    SK_PROPERTY(scaleX),
    SK_PROPERTY(scaleY),
    SK_PROPERTY(skewX),
    SK_PROPERTY(skewY),
    SK_PROPERTY(translate),
    SK_PROPERTY(translateX),
    SK_PROPERTY(translateY)
};

#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkDrawMatrix::fInfo[] = {
    SK_MEMBER_ARRAY(matrix, Float),
    SK_MEMBER_PROPERTY(perspectX, Float),
    SK_MEMBER_PROPERTY(perspectY, Float),
    SK_MEMBER_PROPERTY(rotate, Float),
    SK_MEMBER_PROPERTY(scale, Float),
    SK_MEMBER_PROPERTY(scaleX, Float),
    SK_MEMBER_PROPERTY(scaleY, Float),
    SK_MEMBER_PROPERTY(skewX, Float),
    SK_MEMBER_PROPERTY(skewY, Float),
    SK_MEMBER_PROPERTY(translate, Point),
    SK_MEMBER_PROPERTY(translateX, Float),
    SK_MEMBER_PROPERTY(translateY, Float)
};

#endif

DEFINE_GET_MEMBER(SkDrawMatrix);

SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) {
    fConcat.reset();
    fMatrix.reset();
}

SkDrawMatrix::~SkDrawMatrix() {
    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
        delete *part;
}

bool SkDrawMatrix::addChild(SkAnimateMaker& maker, SkDisplayable* child) {
    SkASSERT(child && child->isMatrixPart());
    SkMatrixPart* part = (SkMatrixPart*) child;
    *fParts.append() = part;
    if (part->add())
        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix);
    return true;
}

bool SkDrawMatrix::childrenNeedDisposing() const {
    return false;
}

SkDisplayable* SkDrawMatrix::deepCopy(SkAnimateMaker* maker) {
    SkDrawMatrix* copy = (SkDrawMatrix*)
        SkDisplayType::CreateInstance(maker, SkType_Matrix);
    SkASSERT(fParts.count() == 0);
    copy->fMatrix = fMatrix;
    copy->fConcat = fConcat;
    return copy;
}

void SkDrawMatrix::dirty() {
    fDirty = true;
}

bool SkDrawMatrix::draw(SkAnimateMaker& maker) {
    SkMatrix& concat = getMatrix();
    maker.fCanvas->concat(concat);
    return false;
}

#ifdef SK_DUMP_ENABLED
void SkDrawMatrix::dump(SkAnimateMaker* maker) {
    dumpBase(maker);
    if (fMatrix.isIdentity()) {
        SkDebugf("matrix=\"identity\"/>\n");
        return;
    }
    SkScalar result;
    result = fMatrix[SkMatrix::kMScaleX];
    if (result != SK_Scalar1)
        SkDebugf("sx=\"%g\" ", SkScalarToFloat(result));
    result = fMatrix.getScaleY();
    if (result != SK_Scalar1)
        SkDebugf("sy=\"%g\" ", SkScalarToFloat(result));
    result = fMatrix.getSkewX();
    if (result)
        SkDebugf("skew-x=\"%g\" ", SkScalarToFloat(result));
    result = fMatrix.getSkewY();
    if (result)
        SkDebugf("skew-y=\"%g\" ", SkScalarToFloat(result));
    result = fMatrix.getTranslateX();
    if (result)
        SkDebugf("tx=\"%g\" ", SkScalarToFloat(result));
    result = fMatrix.getTranslateY();
    if (result)
        SkDebugf("ty=\"%g\" ", SkScalarToFloat(result));
    result = SkPerspToScalar(fMatrix.getPerspX());
    if (result)
        SkDebugf("perspect-x=\"%g\" ", SkScalarToFloat(result));
    result = SkPerspToScalar(fMatrix.getPerspY());
    if (result)
        SkDebugf("perspect-y=\"%g\" ", SkScalarToFloat(result));
    SkDebugf("/>\n");
}
#endif

SkMatrix& SkDrawMatrix::getMatrix() {
    if (fDirty == false)
        return fConcat;
    fMatrix.reset();
    for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++) {
        (*part)->add();
        fConcat = fMatrix;
    }
    fDirty = false;
    return fConcat;
}

bool SkDrawMatrix::getProperty(int index, SkScriptValue* value) const {
    value->fType = SkType_Float;
    SkScalar result;
    switch (index) {
        case SK_PROPERTY(perspectX):
            result = fMatrix.getPerspX();
            break;
        case SK_PROPERTY(perspectY):
            result = fMatrix.getPerspY();
            break;
        case SK_PROPERTY(scaleX):
            result = fMatrix.getScaleX();
            break;
        case SK_PROPERTY(scaleY):
            result = fMatrix.getScaleY();
            break;
        case SK_PROPERTY(skewX):
            result = fMatrix.getSkewX();
            break;
        case SK_PROPERTY(skewY):
            result = fMatrix.getSkewY();
            break;
        case SK_PROPERTY(translateX):
            result = fMatrix.getTranslateX();
            break;
        case SK_PROPERTY(translateY):
            result = fMatrix.getTranslateY();
            break;
        default:
//          SkASSERT(0);
            return false;
    }
    value->fOperand.fScalar = result;
    return true;
}

void SkDrawMatrix::initialize() {
    fConcat = fMatrix;
}

void SkDrawMatrix::onEndElement(SkAnimateMaker& ) {
    if (matrix.count() > 0) {
        SkScalar* vals = matrix.begin();
        fMatrix.setScaleX(vals[0]);
        fMatrix.setSkewX(vals[1]);
        fMatrix.setTranslateX(vals[2]);
        fMatrix.setSkewY(vals[3]);
        fMatrix.setScaleY(vals[4]);
        fMatrix.setTranslateY(vals[5]);
        fMatrix.setPerspX(SkScalarToPersp(vals[6]));
        fMatrix.setPerspY(SkScalarToPersp(vals[7]));
//      fMatrix.setPerspW(SkScalarToPersp(vals[8]));
        goto setConcat;
    }
    if (fChildHasID == false) {
        {
            for (SkMatrixPart** part = fParts.begin(); part < fParts.end();  part++)
                delete *part;
        }
        fParts.reset();
setConcat:
        fConcat = fMatrix;
        fDirty = false;
    }
}

void SkDrawMatrix::setChildHasID() {
    fChildHasID = true;
}

bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) {
    SkScalar number = scriptValue.fOperand.fScalar;
    switch (index) {
        case SK_PROPERTY(translate):
    //      SkScalar xy[2];
            SkASSERT(scriptValue.fType == SkType_Array);
            SkASSERT(scriptValue.fOperand.fArray->getType() == SkType_Float);
            SkASSERT(scriptValue.fOperand.fArray->count() == 2);
    //      SkParse::FindScalars(scriptValue.fOperand.fString->c_str(), xy, 2);
            fMatrix.setTranslateX((*scriptValue.fOperand.fArray)[0].fScalar);
            fMatrix.setTranslateY((*scriptValue.fOperand.fArray)[1].fScalar);
            return true;
        case SK_PROPERTY(perspectX):
            fMatrix.setPerspX(SkScalarToPersp((number)));
            break;
        case SK_PROPERTY(perspectY):
            fMatrix.setPerspY(SkScalarToPersp((number)));
            break;
        case SK_PROPERTY(rotate): {
            SkMatrix temp;
            temp.setRotate(number, 0, 0);
            fMatrix.setScaleX(temp.getScaleX());
            fMatrix.setScaleY(temp.getScaleY());
            fMatrix.setSkewX(temp.getSkewX());
            fMatrix.setSkewY(temp.getSkewY());
            } break;
        case SK_PROPERTY(scale):
            fMatrix.setScaleX(number);
            fMatrix.setScaleY(number);
            break;
        case SK_PROPERTY(scaleX):
            fMatrix.setScaleX(number);
            break;
        case SK_PROPERTY(scaleY):
            fMatrix.setScaleY(number);
            break;
        case SK_PROPERTY(skewX):
            fMatrix.setSkewX(number);
            break;
        case SK_PROPERTY(skewY):
            fMatrix.setSkewY(number);
            break;
        case SK_PROPERTY(translateX):
            fMatrix.setTranslateX(number);
            break;
        case SK_PROPERTY(translateY):
            fMatrix.setTranslateY(number);
            break;
        default:
            SkASSERT(0);
            return false;
    }
    fConcat = fMatrix;
    return true;
}