/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2020, Agnieszka Cicha-Cisek
 * Copyright (c) 2020, Patryk Cisek
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "gtest/gtest.h"

#include <string>
#include "newtotpslotprovider.h"
#include "noemptyslotexception.h"
#include "duplicateslotexception.h"
#include "nitrokeybase.h"

struct NewTOTPSlotProviderParams
{
    NewTOTPSlotProviderParams(std::string name, QList<TOTPSlot> slotsList, TOTPSlot expectedSlot)
        : name(name), slotsList(slotsList), expectedSlot(expectedSlot)
    {
    }
    std::string name;
    QList<TOTPSlot> slotsList;
    TOTPSlot expectedSlot;
};

class NewTOTPSlotProviderParamsDiffrentSlots :
    public testing::TestWithParam<NewTOTPSlotProviderParams> {

};

const std::string NAME_1 = "test1";
const std::string NAME_2 = "test2";
const std::string NAME_3 = "test3";
const std::string NAME_4 = "test4";
const QList<TOTPSlot> SLOTS_LIST_1 {{ "name0", 0 }, { "name1", 1 }, { "name2", 2 }, { "name3", 3 }};
const QList<TOTPSlot> SLOTS_LIST_2 {{ "name0", 0 }, { "name2", 2 }, { "name3", 3 }, { "name4", 4 }};
const QList<TOTPSlot> SLOTS_LIST_3 {{ "name0", 1 }, { "name1", 2 }, { "name2", 3 }, { "name3", 4 }};
const QList<TOTPSlot> SLOTS_LIST_4 {{ "name5", 5 }, { "name0", 0 }, { "name7", 7 }, { "name3", 3 }};
TOTPSlot EXPECTED_SLOT_1 = TOTPSlot(NAME_1, 4);
TOTPSlot EXPECTED_SLOT_2 = TOTPSlot(NAME_2, 1);
TOTPSlot EXPECTED_SLOT_3 = TOTPSlot(NAME_3, 0);
TOTPSlot EXPECTED_SLOT_4 = TOTPSlot(NAME_4, 1);

INSTANTIATE_TEST_SUITE_P(
        InputValuesForTOTPSlotProviderTests,
        NewTOTPSlotProviderParamsDiffrentSlots,
            testing::Values(
                NewTOTPSlotProviderParams(NAME_1, SLOTS_LIST_1, EXPECTED_SLOT_1),
                NewTOTPSlotProviderParams(NAME_2, SLOTS_LIST_2, EXPECTED_SLOT_2),
                NewTOTPSlotProviderParams(NAME_3, SLOTS_LIST_3, EXPECTED_SLOT_3),
                NewTOTPSlotProviderParams(NAME_4, SLOTS_LIST_4, EXPECTED_SLOT_4)));

TEST_P(NewTOTPSlotProviderParamsDiffrentSlots, RightSlotIsReturned) {
    // Given
    NewTOTPSlotProvider provider;

    // When
    TOTPSlot returnedSlot = provider.findFreeSlot(GetParam().slotsList, GetParam().name);

    //Then
    ASSERT_EQ(GetParam().expectedSlot, returnedSlot);
}

TEST(NewTOPTSlotProviderTest, NoEmptySlots) {
    // Given
    std::string name = "test";
    QList<TOTPSlot> slotsList;

    for ( std::size_t i = 0; i < NitrokeyBase::MAX_SLOT_IDX; ++i ) {
        slotsList.append(TOTPSlot("name" + std::to_string(i), i));
    }
    NewTOTPSlotProvider provider;

    EXPECT_THROW({
        try {
            // When
            provider.findFreeSlot(slotsList, name);
        } catch(const NoEmptySlotException& e) {
            // Then
            ASSERT_STREQ("Empty slot not found", e.what());
            throw;
        }
    }, NoEmptySlotException);
}

TEST(NewTOPTSlotProviderTest, DuplicatedSlot) {
    // Given
    std::string name = "test";
    const QList<TOTPSlot> slotsList {
        { "name0", 0 }, { "name1", 1 }, { "name2", 2 }, { "name1", 1 }
    };
    NewTOTPSlotProvider provider;

    EXPECT_THROW({
        try {
            // When
            provider.findFreeSlot(slotsList, name);
        } catch(const DuplicateSlotException& e) {
            // Then
            ASSERT_STREQ("Duplicated slot", e.what());
            throw;
        }
    }, DuplicateSlotException);
}
