/*
 * File Name: utils_unittest.cpp
 */

/*
 * This file is part of uds-plugin-comics.
 *
 * uds-plugin-images is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * uds-plugin-images is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Copyright (C) 2008 iRex Technologies B.V.
 * All rights reserved.
 */

#include <cassert>
#include <tut.h>
#include <tut_reporter.h>
#include "utils.h"

namespace
{

/// Images interface 
class IPluginA
{
public:
    IPluginA(){}
    ~IPluginA(){}

public:
    virtual void do_a() = 0;
};

class IPluginB
{
public:
    IPluginB(){}
    ~IPluginB(){}

    virtual void do_b() = 0;
};

class IPluginC
{
public:
    IPluginC(){}
    ~IPluginC(){}

    virtual void do_c() = 0;
};


/// Implement all images interfaces.
class Impl : public IPluginA
           , public IPluginB
           , public IPluginC
{
public:
    Impl(){}
    ~Impl(){}

public:
    virtual void do_a() { printf("this %p impl a\n", this); }
    virtual void do_b()  { printf("this %p impl b\n", this); }
    virtual void do_c()   { printf("this %p impl c\n", this); }
};

/// template test
template <class T>
class TemplateImpl : public IPluginA
                   , public IPluginB
                   , public IPluginC
{
public:
    TemplateImpl(){data.push_back(10);}
    ~TemplateImpl(){}

public:
    virtual void do_a() { printf("this %p impl a\n", this); }
    virtual void do_b()  { printf("this %p impl b\n", this); }
    virtual void do_c()   { printf("this %p impl c\n", this); }

private:
    std::vector<T> data;
};

};  // anonymous namespace

namespace tut
{
    struct empty
    { 
    };

    typedef test_group<empty> tf;
    typedef tf::object object;
    tf interface_ptr_group("interface utils test");

    // Test 1: Test the query_interface.
    template<>
    template<>
    void object::test<1>()
    {
        ::Impl instance;
        utils::ObjectTable<Impl> objects;

        // The object supports three interfaces.
        objects.add_interface<IPluginA>(&instance);
        objects.add_interface<IPluginB>(&instance);
        objects.add_interface<IPluginC>(&instance);

        IPluginA * pa = 0;
        IPluginB * pb = 0;
        IPluginC * pc = 0;
        objects.query_interface(&instance, "IPluginA", (void **)&pa);
        objects.query_interface(&instance, "IPluginB", (void **)&pb);
        objects.query_interface(&instance, "IPluginC", (void **)&pc);

        ensure(pa == static_cast<IPluginA *>(&instance));
        ensure(pb == static_cast<IPluginB *>(&instance));
        ensure(pc == static_cast<IPluginC *>(&instance));
    }

    // Test 2: Test the query_interface with template class.
    template<>
    template<>
    void object::test<2>()
    {
        ::TemplateImpl<int> instance;
        utils::ObjectTable<TemplateImpl<int>> objects;

        // The object supports three interfaces.
        objects.add_interface<IPluginA>(&instance);
        objects.add_interface<IPluginB>(&instance);
        objects.add_interface<IPluginC>(&instance);

        IPluginA * pa = 0;
        IPluginB * pb = 0;
        IPluginC * pc = 0;
        objects.query_interface(&instance, "IPluginA", (void **)&pa);
        objects.query_interface(&instance, "IPluginB", (void **)&pb);
        objects.query_interface(&instance, "IPluginC", (void **)&pc);

        ensure(pa == static_cast<IPluginA *>(&instance));
        ensure(pb == static_cast<IPluginB *>(&instance));
        ensure(pc == static_cast<IPluginC *>(&instance));

    }

    // Test 3: Test get_object which can retrieve object from interface.
    template<>
    template<>
    void object::test<3>()
    {
        ::Impl instance;
        utils::ObjectTable<Impl> objects;

        objects.add_interface<IPluginA>(&instance);
        objects.add_interface<IPluginB>(&instance);
        objects.add_interface<IPluginC>(&instance);

        IPluginA * pa = 0;
        IPluginB * pb = 0;
        IPluginC * pc = 0;
        objects.query_interface(&instance, "IPluginA", (void **)&pa);
        objects.query_interface(&instance, "IPluginB", (void **)&pb);
        objects.query_interface(&instance, "IPluginC", (void **)&pc);

        ensure(&instance == objects.get_object(pa));
        ensure(&instance == objects.get_object(pb));
        ensure(&instance == objects.get_object(pc));
    }

    // Test 4: Test remove which removes object from object table.
    template<>
    template<>
    void object::test<4>()
    {
        ::Impl instance_a, instance_b;
        utils::ObjectTable<Impl> objects;

        objects.add_interface<IPluginA>(&instance_a);
        objects.add_interface<IPluginB>(&instance_a);
        objects.add_interface<IPluginC>(&instance_a);

        objects.add_interface<IPluginA>(&instance_b);
        objects.add_interface<IPluginB>(&instance_b);
        objects.add_interface<IPluginC>(&instance_b);

        objects.remove(&instance_a);

        IPluginA * pa = 0;
        IPluginB * pb = 0;
        IPluginC * pc = 0;
        objects.query_interface(&instance_a, "IPluginA", (void **)&pa);
        ensure_equals(pa,  0);

        objects.query_interface(&instance_b, "IPluginA", (void **)&pa);
        ensure(pa == static_cast<IPluginA *>(&instance_b));

        objects.remove(&instance_b);
        objects.query_interface(&instance_b, "IPluginA", (void **)&pa);
        ensure_equals(pa,  0);

    }

};


using std::exception;
using std::cerr;
using std::endl;

namespace tut
{
    test_runner_singleton runner;
}

int main()
{
    tut::reporter reporter;
    tut::runner.get().set_callback(&reporter);

    try
    {
        tut::runner.get().run_tests();
    }
    catch (const std::exception& ex)
    {
        cerr << "tut raised ex: " << ex.what() << endl;
        return 1;
    }
    while(1);

    return 0;
}

