/* Copyright (c) 2007 Eric Scott Albright
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <UnitTest++.h>
#include <enchant.h>
#include "../EnchantBrokerTestFixture.h"
#include <vector>


void EnchantDictionaryDescribeCallback (const char * const lang_tag,
				                          const char * const provider_name,
				                          const char * const provider_desc,
				                          const char * const provider_file,
				                          void * user_data)
{
    std::vector<DictionaryDescription>* dictionaryList = reinterpret_cast<std::vector<DictionaryDescription>*>(user_data);
    dictionaryList->push_back(DictionaryDescription(lang_tag, provider_name, provider_desc, provider_file));
}

static void * global_user_data;
void EnchantDictionaryDescribeAssignUserDataToStaticCallback (const char * const,
				                                              const char * const,
				                                              const char * const,
				                                              const char * const,
				                                              void * user_data)
{
  global_user_data = user_data;
}

static bool freeStringListCalled;
static void FreeStringList (EnchantProvider * me, char **str_list)
{
   freeStringListCalled = true;
    
   return MockProviderFreeStringList(me, str_list);
}

static bool listDictionariesCalled;
static char** ListDictionaries (EnchantProvider * me, size_t * out_n_dicts)
{
   listDictionariesCalled = true;
    
   return MockEnGbProviderListDictionaries(me, out_n_dicts);
}

struct EnchantBrokerListDictionaries_TestFixtureBase : EnchantBrokerTestFixture
{
    //Setup
    EnchantBrokerListDictionaries_TestFixtureBase(ConfigureHook userConfiguration):
            EnchantBrokerTestFixture(userConfiguration)
    {
        listDictionariesCalled=false; 
        freeStringListCalled=false;
        global_user_data = NULL;
    }
    std::vector<DictionaryDescription> _dictionaryList;
};

static void List_Dictionaries_ProviderConfiguration (EnchantProvider * me, const char *)
{
     me->list_dicts=ListDictionaries;
     me->free_string_list = FreeStringList;
}


struct EnchantBrokerListDictionaries_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase
{
    //Setup
    EnchantBrokerListDictionaries_TestFixture():
            EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfiguration)
    { }
};

static void List_Dictionaries_ProviderConfigurationLacksFreeStringList (EnchantProvider * me, const char *)
{
     me->list_dicts=ListDictionaries;
     me->free_string_list = NULL;
}

struct EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase
{
    //Setup
    EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture():
            EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfigurationLacksFreeStringList)
    { }
};

static char ** 
InvalidTagEnGbListDictionaries (EnchantProvider *, 
		 	                  size_t * out_n_dicts)
{
    *out_n_dicts = 2;
    char** out_list = g_new0 (char *, *out_n_dicts + 1);
    out_list[0] = g_strdup ("en_GB");
    out_list[1] = g_strdup ("en-GB");

    return out_list;
}

static void List_Dictionaries_InvalidTag_ProviderConfiguration (EnchantProvider * me, const char *)
{
     me->list_dicts=InvalidTagEnGbListDictionaries;
     me->free_string_list = FreeStringList;
}

struct EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase
{
    //Setup
    EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture():
            EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_InvalidTag_ProviderConfiguration)
    { }
};


static char ** 
DuplicateEnGbListDictionaries (EnchantProvider *, 
		 	                  size_t * out_n_dicts)
{
    *out_n_dicts = 2;
    char** out_list = g_new0 (char *, *out_n_dicts + 1);
    out_list[0] = g_strdup ("en_GB");
    out_list[1] = g_strdup ("en_GB");

    return out_list;
}

static void List_Dictionaries_ProviderConfigurationDuplicateTags (EnchantProvider * me, const char *)
{
     me->list_dicts=DuplicateEnGbListDictionaries;
     me->free_string_list = MockProviderFreeStringList;
}

struct EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture : EnchantBrokerListDictionaries_TestFixtureBase
{
    //Setup
    EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture():
            EnchantBrokerListDictionaries_TestFixtureBase(List_Dictionaries_ProviderConfigurationDuplicateTags)
    { }
};

/**
 * enchant_broker_list_dicts
 * @broker: A non-null #EnchantBroker
 * @fn: A non-null #EnchantDictDescribeFn
 * @user_data: Optional user-data
 *
 * Enumerates the dictionaries available from
 * all Enchant providers.
 */

/////////////////////////////////////////////////////////////////////////////
// Test Normal Operation

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_UserDataNotNull_PassedThrough)
{
    char* userData = "some user data";
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, userData);
    CHECK_EQUAL(userData, global_user_data);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_UserDataNull_PassedThrough)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, NULL);
    CHECK_EQUAL((void*)NULL, global_user_data);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_listDictionariesCalledOnProvider)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK(listDictionariesCalled);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_freeStringListCalledOnProvider)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK(freeStringListCalled);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_dictionaryListHasDictionariesFromProvider)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK_EQUAL((unsigned int)1, _dictionaryList.size());
    if(_dictionaryList.size()>0){
        CHECK(_dictionaryList[0].DataIsComplete());
        CHECK_EQUAL(std::string("en_GB"), _dictionaryList[0].LanguageTag);
        CHECK_EQUAL(std::string("mock"), _dictionaryList[0].Name);
        CHECK_EQUAL(std::string("Mock Provider"), _dictionaryList[0].Description);
    }
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture, 
             EnchantBrokerListDictionaries_HasPreviousError_ErrorCleared)
{
    SetErrorOnMockProvider("something bad happened");

    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);

    CHECK_EQUAL((void*)NULL, (void*)enchant_broker_get_error(_broker));
}

/////////////////////////////////////////////////////////////////////////////
// Test Error Conditions
TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_NullBroker_DoNotCallDescribeFunction)
{
    enchant_broker_list_dicts(NULL, EnchantDictionaryDescribeAssignUserDataToStaticCallback, NULL);
    CHECK(!listDictionariesCalled);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_TestFixture,
             EnchantBrokerListDictionaries_NullCallback_DoNotCallDescribeFunction)
{
    enchant_broker_list_dicts(_broker, NULL, NULL);
    CHECK(!listDictionariesCalled);
}

TEST_FIXTURE(EnchantBrokerTestFixture,
             EnchantBrokerListDictionaries_ProviderLacksListDictionaries_CallbackNeverCalled)
{
    void* userData = "some user data";
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeAssignUserDataToStaticCallback, userData);
    CHECK_EQUAL((void*)NULL, global_user_data);
}

TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderDuplicateTags_TestFixture,
             EnchantBrokerListDictionaries_ProviderDuplicateTags_CallbackCalledOnlyOncePerUniqueTag)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK_EQUAL((unsigned int)1, _dictionaryList.size());
}

TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderLacksFreeStringList_TestFixture,
             EnchantBrokerListDictionaries_freeStringListCalledOnProvider)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK(!freeStringListCalled);
}


TEST_FIXTURE(EnchantBrokerListDictionaries_ProviderReturnsInvalidTag_TestFixture,
             EnchantBrokerListDictionaries_ProviderReturnsInvalidTag)
{
    enchant_broker_list_dicts(_broker, EnchantDictionaryDescribeCallback, &_dictionaryList);
    CHECK_EQUAL((unsigned int)1, _dictionaryList.size());
}