/*
 * 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 "SkDisplayAdd.h"
#include "SkAnimateMaker.h"
#include "SkDisplayApply.h"
#include "SkDisplayList.h"
#include "SkADrawable.h"
#include "SkDrawGroup.h"

#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkAdd::fInfo[] = {
    SK_MEMBER(mode, AddMode),
    SK_MEMBER(offset, Int),
    SK_MEMBER(use, Drawable),
    SK_MEMBER(where, Drawable)
};

#endif

// start here;
// add onEndElement to turn where string into f_Where
// probably need new SkAnimateMaker::resolve flavor that takes
// where="id", where="event-target" or not-specified
// offset="#" (implements before, after, and index if no 'where')

DEFINE_GET_MEMBER(SkAdd);

SkAdd::SkAdd() : mode(kMode_indirect),
    offset(SK_MaxS32), use(nullptr), where(nullptr) {
}

SkDisplayable* SkAdd::deepCopy(SkAnimateMaker* maker) {
    SkADrawable* saveUse = use;
    SkADrawable* saveWhere = where;
    use = nullptr;
    where = nullptr;
    SkAdd* copy = (SkAdd*) INHERITED::deepCopy(maker);
    copy->use = use = saveUse;
    copy->where = where = saveWhere;
    return copy;
}

bool SkAdd::draw(SkAnimateMaker& maker) {
    SkASSERT(use);
    SkASSERT(use->isDrawable());
    if (mode == kMode_indirect)
        use->draw(maker);
    return false;
}

#ifdef SK_DUMP_ENABLED
void SkAdd::dump(SkAnimateMaker* maker) {
    dumpBase(maker);
    dumpAttrs(maker);
    if (where)
        SkDebugf("where=\"%s\" ", where->id);
    if (mode == kMode_immediate)
        SkDebugf("mode=\"immediate\" ");
    SkDebugf(">\n");
    SkDisplayList::fIndent += 4;
    int save = SkDisplayList::fDumpIndex;
    if (use)    //just in case
        use->dump(maker);
    SkDisplayList::fIndent -= 4;
    SkDisplayList::fDumpIndex = save;
    dumpEnd(maker);
}
#endif

bool SkAdd::enable(SkAnimateMaker& maker ) {
    SkDisplayTypes type = getType();
    SkDisplayList& displayList = maker.fDisplayList;
    SkTDDrawableArray* parentList = displayList.getDrawList();
    if (type == SkType_Add) {
        if (use == nullptr) // not set in apply yet
            return true;
    }
    bool skipAddToParent = true;
    SkASSERT(type != SkType_Replace || where);
    SkTDDrawableArray* grandList SK_INIT_TO_AVOID_WARNING;
    SkGroup* parentGroup = nullptr;
    SkGroup* thisGroup = nullptr;
    int index = where ? displayList.findGroup(where, &parentList, &parentGroup,
        &thisGroup, &grandList) : 0;
    if (index < 0)
        return true;
    int max = parentList->count();
    if (where == nullptr && type == SkType_Move)
        index = max;
    if (offset != SK_MaxS32) {
        index += offset;
        if (index > max) {
            maker.setErrorCode(SkDisplayXMLParserError::kIndexOutOfRange);
            return true;    // caller should not add
        }
    }
    if (offset < 0 && where == nullptr)
        index += max + 1;
    switch (type) {
        case SkType_Add:
            if (offset == SK_MaxS32 && where == nullptr) {
                if (use->isDrawable()) {
                    skipAddToParent = mode == kMode_immediate;
                    if (skipAddToParent) {
                        if (where == nullptr) {
                            SkTDDrawableArray* useParentList;
                            index = displayList.findGroup(this, &useParentList, &parentGroup,
                                &thisGroup, &grandList);
                            if (index >= 0) {
                                parentGroup->markCopySize(index);
                                parentGroup->markCopySet(index);
                                useParentList->begin()[index] = use;
                                break;
                            }
                        }
                        *parentList->append() = use;
                    }
                }
                break;
            } else {
                if (thisGroup)
                    thisGroup->markCopySize(index);
                *parentList->insert(index) = use;
                if (thisGroup)
                    thisGroup->markCopySet(index);
                if (use->isApply())
                    ((SkApply*) use)->setEmbedded();
            }
            break;
        case SkType_Move: {
            int priorLocation = parentList->find(use);
            if (priorLocation < 0)
                break;
            *parentList->insert(index) = use;
            if (index < priorLocation)
                priorLocation++;
            parentList->remove(priorLocation);
            } break;
        case SkType_Remove: {
            SkDisplayable* old = (*parentList)[index];
            if (((SkRemove*)(this))->fDelete) {
                delete old;
                goto noHelperNeeded;
            }
            for (int inner = 0; inner < maker.fChildren.count(); inner++) {
                SkDisplayable* child = maker.fChildren[inner];
                if (child == old || child->contains(old))
                    goto noHelperNeeded;
            }
            if (maker.fHelpers.find(old) < 0)
                maker.helperAdd(old);
noHelperNeeded:
            parentList->remove(index);
            } break;
        case SkType_Replace:
            if (thisGroup) {
                thisGroup->markCopySize(index);
                if (thisGroup->markedForDelete(index)) {
                    SkDisplayable* old = (*parentList)[index];
                    if (maker.fHelpers.find(old) < 0)
                        maker.helperAdd(old);
                }
            }
            (*parentList)[index] = use;
            if (thisGroup)
                thisGroup->markCopySet(index);
            break;
        default:
            SkASSERT(0);
    }
    if (type == SkType_Remove)
        return true;
    if (use->hasEnable())
        use->enable(maker);
    return skipAddToParent; // append if indirect: *parentList->append() = this;
}

bool SkAdd::hasEnable() const {
    return true;
}

void SkAdd::initialize() {
    if (use)
        use->initialize();
}

bool SkAdd::isDrawable() const {
    return getType() == SkType_Add && mode == kMode_indirect && offset == SK_MaxS32 &&
        where == nullptr && use != nullptr && use->isDrawable();
}

//SkDisplayable* SkAdd::resolveTarget(SkAnimateMaker& maker) {
//  return use;
//}


bool SkClear::enable(SkAnimateMaker& maker ) {
    SkDisplayList& displayList = maker.fDisplayList;
    displayList.clear();
    return true;
}


#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkMove::fInfo[] = {
    SK_MEMBER_INHERITED
};

#endif

DEFINE_GET_MEMBER(SkMove);

#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkRemove::fInfo[] = {
    SK_MEMBER_ALIAS(delete, fDelete, Boolean),  // !!! experimental
    SK_MEMBER(offset, Int),
    SK_MEMBER(where, Drawable)
};

#endif

DEFINE_GET_MEMBER(SkRemove);

SkRemove::SkRemove() : fDelete(false) {
}

#if SK_USE_CONDENSED_INFO == 0

const SkMemberInfo SkReplace::fInfo[] = {
    SK_MEMBER_INHERITED
};

#endif

DEFINE_GET_MEMBER(SkReplace);