/* libs/graphics/animator/SkDisplayList.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/

#include "SkDisplayList.h"
#include "SkAnimateActive.h"
#include "SkAnimateBase.h"
#include "SkAnimateMaker.h"
#include "SkDisplayApply.h"
#include "SkDrawable.h"
#include "SkDrawGroup.h"
#include "SkDrawMatrix.h"
#include "SkInterpolator.h"
#include "SkTime.h"

SkDisplayList::SkDisplayList() : fDrawBounds(true), fUnionBounds(false), fInTime(0) {
}

SkDisplayList::~SkDisplayList() {
}

void SkDisplayList::append(SkActive* active) {
    *fActiveList.append() = active;
}

bool SkDisplayList::draw(SkAnimateMaker& maker, SkMSec inTime) {
    validate();
    fInTime = inTime;
    bool result = false;
    fInvalBounds.setEmpty();
    if (fDrawList.count()) {
        for (SkActive** activePtr = fActiveList.begin(); activePtr < fActiveList.end(); activePtr++) {
            SkActive* active = *activePtr;
            active->reset();
        }
        for (int index = 0; index < fDrawList.count(); index++) {
            SkDrawable* draw = fDrawList[index];
            draw->initialize(); // allow matrices to reset themselves
            SkASSERT(draw->isDrawable());
            validate();
            result |= draw->draw(maker);
        }
    }
    validate();
    return result;
}

int SkDisplayList::findGroup(SkDrawable* match, SkTDDrawableArray** list,
        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
    *parent = NULL;
    *list = &fDrawList;
    *grandList = &fDrawList;
    return SearchForMatch(match, list, parent, found, grandList);
}

void SkDisplayList::hardReset() {
    fDrawList.reset();
    fActiveList.reset();
}

bool SkDisplayList::onIRect(const SkIRect& r) {
    fBounds = r;
    return fDrawBounds;
}

int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
    *found = NULL;
    for (int index = 0; index < (*list)->count(); index++) {
        SkDrawable* draw = (**list)[index];
        if (draw == match)
            return index;
        if (draw->isApply()) {
            SkApply* apply = (SkApply*) draw;
            if (apply->scope == match)
                return index;
            if (apply->scope->isGroup() && SearchGroupForMatch(apply->scope, match, list, parent, found, grandList, index))
                return index;
            if (apply->mode == SkApply::kMode_create) {
                for (SkDrawable** ptr = apply->fScopes.begin(); ptr < apply->fScopes.end(); ptr++) {
                    SkDrawable* scope = *ptr;
                    if (scope == match)
                        return index;
                    //perhaps should call SearchGroupForMatch here as well (on scope)
                }
            } 
        }
        if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index)) 
            return index;
        
    }
    return -1;
}

bool SkDisplayList::SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, SkTDDrawableArray** list,
        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, int &index) {
            SkGroup* group = (SkGroup*) draw;
            if (group->getOriginal() == match)
                return true;
            SkTDDrawableArray* saveList = *list;
            int groupIndex = group->findGroup(match, list, parent, found, grandList);
            if (groupIndex >= 0) {
                *found = group;
                index = groupIndex;
                return true;
            }
            *list = saveList;
            return false;
        }

void SkDisplayList::reset() {
    for (int index = 0; index < fDrawList.count(); index++) {
        SkDrawable* draw = fDrawList[index];
        if (draw->isApply() == false)
            continue;
        SkApply* apply = (SkApply*) draw;
        apply->reset();
    }           
}

void SkDisplayList::remove(SkActive* active) {
    int index = fActiveList.find(active);
    SkASSERT(index >= 0);
    fActiveList.remove(index);  // !!! could use shuffle instead
    SkASSERT(fActiveList.find(active) < 0);
}

#ifdef SK_DUMP_ENABLED
int SkDisplayList::fDumpIndex;
int SkDisplayList::fIndent;

void SkDisplayList::dump(SkAnimateMaker* maker) {
    fIndent = 0;
    dumpInner(maker);
}

void SkDisplayList::dumpInner(SkAnimateMaker* maker) {
    for (int index = 0; index < fDrawList.count(); index++) {
        fDumpIndex = index;
        fDrawList[fDumpIndex]->dump(maker);
    }
}

#endif

#ifdef SK_DEBUG
void SkDisplayList::validate() {
    for (int index = 0; index < fDrawList.count(); index++) {
        SkDrawable* draw = fDrawList[index];
        draw->validate();
    }
}
#endif