/*
 * File Name: signal_slot_unittest.cpp
 */

/*
 * This file is part of uds-plugin-images.
 *
 * 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 "signal_slot.h"
#include "log.h"

namespace testslot
{
typedef std::vector<int> Integers;

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

public:
    void on_signal_void()
    {
        LOGPRINTF("SlotA void ");
    }

    // Slot handler 
    void on_signal_string(const std::string & name)
    {
        LOGPRINTF("SlotA %p : on_signal_string value %s", name.c_str());
    }

    void on_signal_int(int first, int second)
    {
        LOGPRINTF("SlotA(%p): on_signal_int first %d second %d\n", 
                  this, first, second);
    }
    
    void on_signal_three(const std::string & a, int b, const Integers & c)
    {
        LOGPRINTF("SlotA(%p): on_signal_three a %s ", this, a.c_str());
        LOGPRINTF("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            printf("%d ", *it);
        }
        printf("\n");
    }

    void on_signal_four(const std::string & a, int b, const Integers & c, const std::string &d)
    {
        LOGPRINTF("SlotA(%p): on_signal_four a %s ", this, a.c_str());
        LOGPRINTF("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            LOGPRINTF("%d ", *it);
        }
        LOGPRINTF("%s", d.c_str());
        LOGPRINTF("\n");
    }

    void on_signal_five(const std::string & a, int b, 
                        const Integers & c, 
                        const std::string &d, 
                        const std::string &e)
    {
        LOGPRINTF("SlotA(%p): on_signal_five a %s ", this, a.c_str());
        LOGPRINTF("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            LOGPRINTF("%d ", *it);
        }
        LOGPRINTF("%s ", d.c_str());
        LOGPRINTF("%s ", e.c_str());
        LOGPRINTF("\n");
    }
};

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

public:
    void on_signal_string(const std::string & name)
    {
        printf("SlotB(%p): on_signal_string value \"%s\"\n", this, name.c_str());
    }

    void on_signal_int(int first, int second)
    {
        printf("SlotB(%p): on_signal_int first %d second %d\n", this, first, second);
    }
    
    void on_signal_three(const std::string & a, int b, const Integers & c)
    {
        printf("SlotB(%p): on_signal_three a %s ", this, a.c_str());
        printf("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            printf("%d ", *it);
        }
        printf("\n");
    }

    void on_signal_four(const std::string & a, int b, const Integers & c, const std::string &d)
    {
        printf("SlotB(%p): on_signal_four a %s ", this, a.c_str());
        printf("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            printf("%d ", *it);
        }
        printf("%s", d.c_str());
        printf("\n");
    }

    void on_signal_five(const std::string & a, int b, 
                        const Integers & c, 
                        const std::string &d, 
                        const std::string &e)
    {
        printf("SlotB(%p): on_signal_five a %s ", this, a.c_str());
        printf("%d ", b);
        for(Integers::const_iterator it = c.begin(); it != c.end(); ++it)
        {
            printf("%d ", *it);
        }
        printf("%s ", d.c_str());
        printf("%s ", e.c_str());
        printf("\n");
    }
};


};  // anonymous namespace

namespace tut
{
    struct empty
    { 
    };

    typedef test_group<empty> tf;
    typedef tf::object object;
    tf signal_slot_group("signal slot test");

    // Test 1: Basic test.
    template<>
    template<>
    void object::test<1>()
    {
        using namespace utils;
        using namespace testslot;
        Signal<> xx;
        Signal<const std::string &> signal_a;
        Signal<int, int> signal_b;
        Signal<const std::string &, int, const Integers &> signal_c;
        Signal<const std::string &, int, const Integers &, const std::string &> signal_d;
        Signal<const std::string &, int, const Integers &, const std::string &, const std::string &> signal_e;
        SlotA slot_a1, slot_a2;
        SlotB slot_b;

        // prepare parameters;
        std::string name = "destroy signal!";
        int first = 1, second = 100;
        Integers ints;
        for(int i = 0; i < 3; ++i)
        {
            ints.push_back(i);
        }

        // xx.add_slot(&slot_a1, &SlotA::on_signal_void);
        // xx.broadcast();


        signal_a.add_slot(&slot_a1, &SlotA::on_signal_string);
        signal_a.add_slot(&slot_a2, &SlotA::on_signal_string);
        signal_a.add_slot(&slot_b, &SlotB::on_signal_string);
        signal_a.broadcast(name);
        // signal_a.broadcast();

        signal_b.add_slot(&slot_a1, &SlotA::on_signal_int);
        signal_b.add_slot(&slot_a2, &SlotA::on_signal_int);
        signal_b.add_slot(&slot_b, &SlotB::on_signal_int);
        signal_b.broadcast(first, second);
        signal_b.broadcast(first);

        signal_c.add_slot(&slot_a1, &SlotA::on_signal_three);
        signal_c.add_slot(&slot_a2, &SlotA::on_signal_three);
        signal_c.add_slot(&slot_b, &SlotB::on_signal_three);
        signal_c.broadcast(name, first, ints);

        signal_d.add_slot(&slot_a1, &SlotA::on_signal_four);
        signal_d.add_slot(&slot_a2, &SlotA::on_signal_four);
        signal_d.add_slot(&slot_b, &SlotB::on_signal_four);
        signal_d.broadcast(name, first, ints, "another one");

        signal_e.add_slot(&slot_a1, &SlotA::on_signal_five);
        signal_e.add_slot(&slot_a2, &SlotA::on_signal_five);
        signal_e.add_slot(&slot_b, &SlotB::on_signal_five);
        signal_e.broadcast(name, first, ints, "another one", "last one");


    }

    // Test 2: Test remove.
    template<>
    template<>
    void object::test<2>()
    {
        using namespace utils;
        using namespace testslot;
        Signal<const std::string &> signal_a;
        Signal<int, int> signal_b;
        Signal<const std::string &, int, const Integers &> signal_c;
        Signal<const std::string &, int, const Integers &, const std::string &> signal_d;
        Signal<const std::string &, int, const Integers &, const std::string &, const std::string &> signal_e;
        SlotA slot_a1, slot_a2;
        SlotB slot_b;

        // prepare parameters;
        std::string name = "destroy signal!";
        int first = 1, second = 100;
        Integers ints;
        for(int i = 0; i < 3; ++i)
        {
            ints.push_back(i);
        }


        signal_a.add_slot(&slot_a1, &SlotA::on_signal_string);
        signal_a.add_slot(&slot_a2, &SlotA::on_signal_string);
        signal_a.remove_slot(&slot_a1, &SlotA::on_signal_string);
        signal_a.broadcast(name);

        signal_b.add_slot(&slot_a1, &SlotA::on_signal_int);
        signal_b.add_slot(&slot_a2, &SlotA::on_signal_int);
        signal_b.remove_slot(&slot_a2, &SlotA::on_signal_int);
        signal_b.broadcast(first, second);

        signal_c.add_slot(&slot_a1, &SlotA::on_signal_three);
        signal_c.add_slot(&slot_a2, &SlotA::on_signal_three);
        signal_c.remove_slot(&slot_a1, &SlotA::on_signal_three);
        signal_c.broadcast(name, first, ints);

        signal_d.add_slot(&slot_a1, &SlotA::on_signal_four);
        signal_d.add_slot(&slot_a2, &SlotA::on_signal_four);
        signal_d.remove_slot(&slot_a2, &SlotA::on_signal_four);
        signal_d.broadcast(name, first, ints, "another one");

        signal_e.add_slot(&slot_a1, &SlotA::on_signal_five);
        signal_e.add_slot(&slot_a2, &SlotA::on_signal_five);
        signal_e.remove_slot(&slot_a1, &SlotA::on_signal_five);
        signal_e.broadcast(name, first, ints, "another one", "last one");

    }


    // Test 3: Test repeat adding. TODO.
    template<>
    template<>
    void object::test<3>()
    {
        using namespace utils;
        using namespace testslot;
        Signal<const std::string &> signal_a;
        Signal<int, int> signal_b;
        Signal<const std::string &, int, const Integers &> signal_c;
        Signal<const std::string &, int, const Integers &, const std::string &> signal_d;
        Signal<const std::string &, int, const Integers &, const std::string &, const std::string &> signal_e;
        SlotA slot_a1, slot_a2;
        SlotB slot_b;

        // prepare parameters;
        std::string name = "destroy signal!";
        int first = 1, second = 100;
        Integers ints;
        for(int i = 0; i < 3; ++i)
        {
            ints.push_back(i);
        }


        signal_a.add_slot(&slot_a1, &SlotA::on_signal_string);
        signal_a.add_slot(&slot_a2, &SlotA::on_signal_string);
        signal_a.remove_slot(&slot_a1, &SlotA::on_signal_string);
        signal_a.broadcast(name);
               
        
        signal_b.add_slot(&slot_a1, &SlotA::on_signal_int);
        signal_b.add_slot(&slot_a2, &SlotA::on_signal_int);
        signal_b.remove_slot(&slot_a2, &SlotA::on_signal_int);
        signal_b.broadcast(first, second);
        signal_b.broadcast(first);

        signal_c.add_slot(&slot_a1, &SlotA::on_signal_three);
        signal_c.add_slot(&slot_a2, &SlotA::on_signal_three);
        signal_c.remove_slot(&slot_a1, &SlotA::on_signal_three);
        signal_c.broadcast(name, first, ints);

        signal_d.add_slot(&slot_a1, &SlotA::on_signal_four);
        signal_d.add_slot(&slot_a2, &SlotA::on_signal_four);
        signal_d.remove_slot(&slot_a2, &SlotA::on_signal_four);
        signal_d.broadcast(name, first, ints, "another one");

        signal_e.add_slot(&slot_a1, &SlotA::on_signal_five);
        signal_e.add_slot(&slot_a2, &SlotA::on_signal_five);
        signal_e.remove_slot(&slot_a1, &SlotA::on_signal_five);
        signal_e.broadcast(name, first, ints, "another one", "last one");

    }

};


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;
    }

    return 0;
}



