
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkViewInflate.h"
#include "SkView.h"
#include <stdio.h>

SkViewInflate::SkViewInflate() : fIDs(kMinIDStrAlloc), fStrings(kMinIDStrAlloc)
{
}

SkViewInflate::~SkViewInflate()
{
}

void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent)
{
    const char* str = dom.findAttr(node, "id");
    if (str)
        fIDs.set(str, parent);

    const SkDOM::Node* child = dom.getFirstChild(node);
    while (child)
    {
        SkView* view = this->createView(dom, child);
        if (view)
        {
            this->rInflate(dom, child, view);
            parent->attachChildToFront(view)->unref();
        }
        else
        {
            const char* name = dom.getName(child);
            const char* target;

            if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != nullptr)
                this->addIDStr(&fListenTo, parent, target);

            if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != nullptr)
                this->addIDStr(&fBroadcastTo, parent, target);
        }
        child = dom.getNextSibling(child);
    }

    parent->setVisibleP(true);
    this->inflateView(parent, dom, node);
}

void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node)
{
    // called after all of view's children have been instantiated.
    // this may be overridden by a subclass, to load in layout or other helpers
    // they should call through to us (INHERITED) before or after their patch
    view->inflate(dom, node);
}

SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
{
    fIDs.reset();

    if (root == nullptr)
    {
        root = this->createView(dom, node);
        if (root == nullptr)
        {
            printf("createView returned nullptr on <%s>\n", dom.getName(node));
            return nullptr;
        }
    }
    this->rInflate(dom, node, root);

    // resolve listeners and broadcasters
    {
        SkView*            target;
        const IDStr*    iter = fListenTo.begin();
        const IDStr*    stop = fListenTo.end();
        for (; iter < stop; iter++)
        {
            if (fIDs.find(iter->fStr, &target))
                target->addListenerID(iter->fView->getSinkID());
        }

        iter = fBroadcastTo.begin();
        stop = fBroadcastTo.end();
        for (; iter < stop; iter++)
        {
            if (fIDs.find(iter->fStr, &target))
                iter->fView->addListenerID(target->getSinkID());
        }
    }

    // now that the tree is built, give everyone a shot at the ID dict
    root->postInflate(fIDs);
    return root;
}

SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root)
{
    SkDOM                dom;
    const SkDOM::Node*    node = dom.build(xml, len);

    return node ? this->inflate(dom, node, root) : nullptr;
}

SkView* SkViewInflate::findViewByID(const char id[]) const
{
    SkASSERT(id);
    SkView* view;
    return fIDs.find(id, &view) ? view : nullptr;
}

SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
{
    if (!strcmp(dom.getName(node), "view"))
        return new SkView;
    return nullptr;
}

void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str)
{
    size_t len = strlen(str) + 1;
    IDStr* pair = list->append();
    pair->fView = view;
    pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
    memcpy(pair->fStr, str, len);
}

#ifdef SK_DEBUG
void SkViewInflate::dump() const
{
    const IDStr* iter = fListenTo.begin();
    const IDStr* stop = fListenTo.end();
    for (; iter < stop; iter++)
        SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);

    iter = fBroadcastTo.begin();
    stop = fBroadcastTo.end();
    for (; iter < stop; iter++)
        SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
}
#endif
