/*
 * 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 "SkAnimateBase.h"
#include "SkAnimateMaker.h"
#include "SkAnimateProperties.h"
#include "SkAnimatorScript.h"
#include "SkDisplayApply.h"
#include "SkADrawable.h"

#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkAnimateBase::fInfo[] = {
    SK_MEMBER(begin, MSec),
    SK_MEMBER_ARRAY(blend, Float),
    SK_MEMBER(dur, MSec),
    SK_MEMBER_PROPERTY(dynamic, Boolean),
    SK_MEMBER(field, String),   // name of member info in target
    SK_MEMBER(formula, DynamicString),
    SK_MEMBER(from, DynamicString),
    SK_MEMBER(lval, DynamicString),
    SK_MEMBER_PROPERTY(mirror, Boolean),
    SK_MEMBER(repeat, Float),
    SK_MEMBER_PROPERTY(reset, Boolean),
    SK_MEMBER_PROPERTY(step, Int),
    SK_MEMBER(target, DynamicString),
    SK_MEMBER(to, DynamicString),
    SK_MEMBER_PROPERTY(values, DynamicString)
};

#endif

DEFINE_GET_MEMBER(SkAnimateBase);

SkAnimateBase::SkAnimateBase() : begin(0), dur(1), repeat(SK_Scalar1),
        fApply(nullptr), fFieldInfo(nullptr), fFieldOffset(0), fStart((SkMSec) -1), fTarget(nullptr),
        fChanged(0), fDelayed(0), fDynamic(0), fHasEndEvent(0), fHasValues(0),
        fMirror(0), fReset(0), fResetPending(0), fTargetIsScope(0) {
    blend.setCount(1);
    blend[0] = SK_Scalar1;
}

SkAnimateBase::~SkAnimateBase() {
    SkDisplayTypes type = fValues.getType();
    if (type == SkType_String || type == SkType_DynamicString) {
        SkASSERT(fValues.count() == 1);
        delete fValues[0].fString;
    }
}

int SkAnimateBase::components() {
    return 1;
}

SkDisplayable* SkAnimateBase::deepCopy(SkAnimateMaker* maker) {
    SkAnimateBase* result = (SkAnimateBase*) INHERITED::deepCopy(maker);
    result->fApply = fApply;
    result->fFieldInfo =fFieldInfo;
    result->fHasValues = false;
    return result;
}

void SkAnimateBase::dirty() {
    fChanged = true;
}

#ifdef SK_DUMP_ENABLED
void SkAnimateBase::dump(SkAnimateMaker* maker) {
    dumpBase(maker);
    if (target.size() > 0)
        SkDebugf("target=\"%s\" ", target.c_str());
    else if (fTarget && strcmp(fTarget->id, ""))
        SkDebugf("target=\"%s\" ", fTarget->id);
    if (lval.size() > 0)
        SkDebugf("lval=\"%s\" ", lval.c_str());
    if (field.size() > 0)
        SkDebugf("field=\"%s\" ", field.c_str());
    else if (fFieldInfo)
        SkDebugf("field=\"%s\" ", fFieldInfo->fName);
    if (formula.size() > 0)
        SkDebugf("formula=\"%s\" ", formula.c_str());
    else {
        if (from.size() > 0)
            SkDebugf("from=\"%s\" ", from.c_str());
        SkDebugf("to=\"%s\" ", to.c_str());
    }
    if (begin != 0) {
        SkDebugf("begin=\"%g\" ", begin * 0.001);
    }
}
#endif

SkDisplayable* SkAnimateBase::getParent() const {
    return (SkDisplayable*) fApply;
}

bool SkAnimateBase::getProperty(int index, SkScriptValue* value) const {
    int boolResult;
    switch (index) {
        case SK_PROPERTY(dynamic):
            boolResult = fDynamic;
            goto returnBool;
        case SK_PROPERTY(mirror):
            boolResult = fMirror;
            goto returnBool;
        case SK_PROPERTY(reset):
            boolResult = fReset;
returnBool:
            value->fOperand.fS32 = SkToBool(boolResult);
            value->fType = SkType_Boolean;
            break;
        case SK_PROPERTY(step):
            if (fApply == nullptr)
                return false;    // !!! notify there's an error?
            fApply->getStep(value);
            break;
        case SK_PROPERTY(values):
            value->fOperand.fString = (SkString*) &to;
            value->fType = SkType_String;
            break;
        default:
            SkASSERT(0);
            return false;
    }
    return true;
}

bool SkAnimateBase::hasExecute() const
{
    return false;
}

void SkAnimateBase::onEndElement(SkAnimateMaker& maker) {
    fChanged = false;
    setTarget(maker);
    if (field.size()) {
        SkASSERT(fTarget);
        fFieldInfo = fTarget->getMember(field.c_str());
        field.reset();
    }
    if (lval.size()) {
        // lval must be of the form x[y]
        const char* lvalStr = lval.c_str();
        const char* arrayEnd = strchr(lvalStr, '[');
        if (arrayEnd == nullptr)
            return; //should this return an error?
        size_t arrayNameLen = arrayEnd - lvalStr;
        SkString arrayStr(lvalStr, arrayNameLen);
        SkASSERT(fTarget);  //this return an error?
        fFieldInfo = fTarget->getMember(arrayStr.c_str());
        SkString scriptStr(arrayEnd + 1, lval.size() - arrayNameLen - 2);
        SkAnimatorScript::EvaluateInt(maker, this, scriptStr.c_str(), &fFieldOffset);
    }
}

void SkAnimateBase::packARGB(SkScalar array[], int count, SkTDOperandArray* converted)
{
    SkASSERT(count == 4);
    converted->setCount(1);
    SkColor color = SkColorSetARGB(SkScalarRoundToInt(array[0]),
                                   SkScalarRoundToInt(array[1]),
                                   SkScalarRoundToInt(array[2]),
                                   SkScalarRoundToInt(array[3]));
    (*converted)[0].fS32 = color;
}



void SkAnimateBase::refresh(SkAnimateMaker& ) {
}

bool SkAnimateBase::setParent(SkDisplayable* apply) {
    SkASSERT(apply->isApply());
    fApply = (SkApply*) apply;
    return false;
}

bool SkAnimateBase::setProperty(int index, SkScriptValue& value) {
    bool boolValue = SkToBool(value.fOperand.fS32);
    switch (index) {
        case SK_PROPERTY(dynamic):
            fDynamic = boolValue;
            goto checkForBool;
        case SK_PROPERTY(values):
            fHasValues = true;
            SkASSERT(value.fType == SkType_String);
            to = *value.fOperand.fString;
            break;
        case SK_PROPERTY(mirror):
            fMirror = boolValue;
            goto checkForBool;
        case SK_PROPERTY(reset):
            fReset = boolValue;
checkForBool:
            SkASSERT(value.fType == SkType_Boolean);
            break;
        default:
            return false;
    }
    return true;
}

void SkAnimateBase::setTarget(SkAnimateMaker& maker) {
    if (target.size()) {
        SkAnimatorScript engine(maker, this, SkType_Displayable);
        const char* script = target.c_str();
        SkScriptValue scriptValue;
        bool success = engine.evaluateScript(&script, &scriptValue);
        if (success && scriptValue.fType == SkType_Displayable)
            fTarget = scriptValue.fOperand.fDrawable;
        else if (maker.find(target.c_str(), (SkDisplayable**) &fTarget) == false) {
            if (fApply->getMode() == SkApply::kMode_create)
                return; // may not be an error
            if (engine.getError() != SkScriptEngine::kNoError)
                maker.setScriptError(engine);
            else {
                maker.setErrorNoun(target);
                maker.setErrorCode(SkDisplayXMLParserError::kTargetIDNotFound);
            }
            return;
        }
        if (fApply && fApply->getMode() != SkApply::kMode_create)
            target.reset();
    }
}

bool SkAnimateBase::targetNeedsInitialization() const {
    return false;
}