#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h> // for EXIT_FAILURE
/*#include <int_list.h>*/

//#include <int_stack.h>
#include <stack.h>

/*
 * test program for the stack implementation in stack.h.
 *
 * Author: Marc Meunier (mame0264@student.umu.se).
 *
 * Version information:
 *   2025-04-22: v1.0.  First version.
 */

#define VERSION "v1.0"
#define VERSION_DATE "2025-04-22"

/*
 * Function to compare the values stored in the list.
 */
bool value_equal(int v1, int v2)
{
    return v1 == v2;
}

/**
 * int_create() - make a dynamic copy of an integer
 * @i: Integer to copy
 *
 * Returns: A pointer to a dynamic copy of i
 */
int *int_create(int i)
{
    // Allocate memory for an integer and set the value
    int *v=malloc(sizeof(*v));
    *v = i;
    return v;
}

// Return the memory used by the integer.
void int_kill(void *v)
{
    int *p=v;
    free(p);
}


// test that the stack_empty() function creates an empty stack.

void test_stack_empty(void)
{   
    // prints start message
    fprintf(stderr, "Starting test_stack_empty()...\n");
    // initialize stack
    stack *s = stack_empty(int_kill);
    // check that stack initialized correctly
    if (s == NULL) {
        fprintf(stderr, "FAIL: stack_empty() returned NULL.\n");
        exit(1);
    }
    // returns memory and exit message. 
    stack_kill(s);
    fprintf(stderr, "done.\n");
}

// test that the stack_is_empty() function returns true for an empty stack
void isempty_return_true(void)
{
    // print start message
    fprintf(stderr, "Starting isempty_return_true()...\n");
    // initialize stack
    stack *s = stack_empty(int_kill);
    // prints if function returns false
    if (!stack_is_empty(s))
    {
        fprintf(stderr, "\nStack is empty but \"stack_is_empty\" reports non empty\n");
        exit(1);
    }

    // adds values to stack    
    stack_push(s, int_create(10));
    // test if stack prints false with values inside
    if (stack_is_empty(s))
    {
        fprintf(stderr, "\nStack is non empty but \"stack_is_empty\" reports empty.\n");
        exit(1);
    }
    // returns memory and exit message.
    stack_kill(s);
    fprintf(stderr, "done.\n");
}

// tests if stack returns top value in stack.
void push_stack_on_top(void)
{
    // startmessage
    fprintf(stderr, "Starting push_stack_on_top()...\n");
    // initialize stack
    stack *s = stack_empty(int_kill);
    // how many numbers that should be added to stack
    int numberofelements = 10;
    // adds values to stack in  
    for (int i = 1; i <= numberofelements; i++)
    {   
        // if the value i is higher then 5 then add in steps of 5
        // to check that the the stack_push does not just add value x in position x
        if (i > 5)
        {
            int a = 5*i;
            s = stack_push(s, int_create((a)));
            
        }
        else
        // if values are not higher just add next intedger
        {
            s = stack_push(s, int_create(i));
        }
    }
    // initialize a variable to store the value in the top of the stack
    int a = 0;
    // tests if values are returned in correct order when more values are in stack. 
    for (int i = 0; i <= numberofelements-1; i++)
    {
        a = numberofelements - i;
        // check if value is same as the top value in stack. 
        // converts pointer to an intedger value. 
        int *stored_ptr = stack_top(s);
        int stored_value = *stored_ptr;    
        
        // checks if value higher then 5 so it has same values as inserted into stack. 
        if (a > 5)
        {   // if they are not equal print the error message
            if (!value_equal(stored_value, a*5))
            {
                fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", (a*5), stored_value);
                exit(1);
            }
        }
        else
        {   // if they are not equal print the error message
            if (!value_equal(stored_value, a))
            {
                fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", a, stored_value);
                exit(1);
            }
        }
        // removes one value from the stack so that the next value can be checked.
        s = stack_pop(s);
    }
    // returns memory and exit message.
    stack_kill(s);
    fprintf(stderr, "done.\n");
    
}

// tests if the top reads wrong value. 
void top_read_wrong(void)
{
    // start message
    fprintf(stderr, "Starting top_read_wrong()...\n");
    //initialize stack
    stack *s = stack_empty(int_kill);
    //loops over some values to add to the stack.
    for (int i = 0; i <= 3; i++)
    {
        // adds the value to the stack
        s = stack_push(s,int_create(i));
        // converts the pointer to an intedger value.
        int *stored_ptr = stack_top(s);
        int stored_value = *stored_ptr;
        // checks values in stack and compare to inputed value. 
        if (!value_equal(stored_value, i))
        {
            fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value);
            exit(1);
        }
    }
    // returns memory and exit message.
    stack_kill(s);
    fprintf(stderr,"done.\n");
}

// tests if the pop function removes the wrong value or not. 
void pop_remove_wrong(void)
{   
    // startmessage
    fprintf(stderr, "Starting pop_remove_wrong()...\n");
    // how many numbers that should be added to stack
    int iter = 3;
    // initialize stack
    stack *s = stack_empty(int_kill);
    // adds values to stack 
    for (int i = 0; i <= iter; i++)
    {
        s = stack_push(s,int_create(i));
    }
    // check if values are correct and if removes them from the stack 
    for (int i = 0; i <= iter; i++)
    {   // converts the pointer to an intedger value.
        int *stored_ptr = stack_top(s);
        int stored_value = *stored_ptr;
        // checks if the value is correct and if not print error message
        if (!value_equal(stored_value, 3-i))
        {
            fprintf(stderr, "FAIL: Expected <%d>, got <%d>.\n", i, stored_value);
            exit(1);
        }
        // removes the value from the stack to continue loop over next value
        s = stack_pop(s);
    }
    // returns memory and exit message.
    stack_kill(s);
    fprintf(stderr,"done.\n");
} 



// main function that runs all the tests.
int main(void)
{
    // prints the version and date of the program
    printf("%s, %s %s: Test program for the typed int_stack datatype.\n",
           __FILE__, VERSION, VERSION_DATE);
    // tests each function.
    // stack_empty(); 
    test_stack_empty();
    // stack_is_empty();
    isempty_return_true();
    // stack_push();
    push_stack_on_top();
    // stack_top();
    top_read_wrong();
    // stack_pop();
    pop_remove_wrong();
    
    // if everything sucessfull then print sucessful message
    fprintf(stderr, "\nSUCCESS: Implementation passed all tests. Normal exit.\n");
    // returns 0 (program not crashed)
    return 0;
}
