#include "friends.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int check_friends_length(User *checked_user);
void add_friends(User *user1, User *user2);
int check_friendship(User *user1, User *user2);
int remove_from_friends(User *unfriendee, User *unfriender);

/*
 * Create a new user with the given name.  Insert it at the tail of the list
 * of users whose head is pointed to by *user_ptr_add.
 *
 * Return:
 *   - 0 if successful
 *   - 1 if a user by this name already exists in this list
 *   - 2 if the given name cannot fit in the 'name' array
 *       (don't forget about the null terminator)
 */
int create_user(const char *name, User **user_ptr_add) {
    // Check length
    if (strlen(name) > MAX_NAME) {
        return 2;
    }

    // Search for given User
    User *user_to_check = find_user(name, *user_ptr_add);
    if (user_to_check != NULL) {
        return 1;
    }

    // Create User
    User *new_user = malloc(sizeof(User));
    strncpy(new_user->name, name, MAX_NAME);

    // If list is empty, initialize it
    if (*user_ptr_add == NULL) {
        *user_ptr_add = new_user;
        return 0;
    }

    // If list is not empty, add a User to the end of it
    User *curr = *user_ptr_add;
    while (curr->next != NULL) {
        curr = curr->next;
    }
    curr->next = new_user;
    return 0;
}


/*
 * Return a pointer to the user with this name in
 * the list starting with head. Return NULL if no such user exists.
 *
 * NOTE: You'll likely need to cast a (const User *) to a (User *)
 * to satisfy the prototype without warnings.
 */
User *find_user(const char *name, const User *head) {
    User *curr = (User *) head;

    while (curr != NULL) {
        if (strcmp(curr->name, name) == 0) {
            return curr;
        }
        curr = curr->next;
    }
    return curr;
}


/*
 * Print the usernames of all users in the list starting at curr.
 * Names should be printed to standard output, one per line.
 */
void list_users(const User *curr) {
    while (curr != NULL) {
        printf("%s\n", curr->name);
        curr = curr->next;
    }
}


/*
 * Change the filename for the profile pic of the given user.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if the file does not exist or cannot be opened.
 *   - 2 if the filename is too long.
 */
int update_pic(User *user, const char *filename) {
    // Check length
    if (strlen(filename) > MAX_NAME) {
        return 2;
    }

    FILE *given_file = fopen(filename, "r");
    if (given_file == NULL) {
        return 1;
    }
    fclose(given_file);

    strncpy(user->profile_pic, filename, MAX_NAME);

    return 0;
}


/*
 * Make two users friends with each other.  This is symmetric - a pointer to
 * each user must be stored in the 'friends' array of the other.
 *
 * New friends must be added in the first empty spot in the 'friends' array.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if the two users are already friends.
 *   - 2 if the users are not already friends, but at least one already has
 *     MAX_FRIENDS friends.
 *   - 3 if the same user is passed in twice.
 *   - 4 if at least one user does not exist.
 *
 * Do not modify either user if the result is a failure.
 * NOTE: If multiple errors apply, return the *largest* error code that applies.
 */
int make_friends(const char *name1, const char *name2, User *head) {
    User *user1 = find_user(name1, head);
    User *user2 = find_user(name2, head);

    // Check existence
    if (user1 == NULL || user2 == NULL) {
        return 4;
    }

    // Check equivalence
    if (strcmp(name1, name2) == 0) {
        return 3;
    }

    // Check friend list lengths
    if (check_friends_length(user1) == MAX_FRIENDS || check_friends_length(user2) == MAX_FRIENDS) {
        return 2;
    }

    // Check existing friendship
    if (check_friendship(user1, user2) == 0) {
        return 1;
    }

    // Add friends
    add_friends(user1, user2);
    return 0;

}

int check_friends_length(User *checked_user) {
    //HELPER
    // return number of friends

    int counter = 0;

    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (checked_user->friends[i] != NULL) {
            counter++;
        }
    }
    return counter;
}

void add_friends(User *user1, User *user2) {
    // HELPER
    // PRECONDITION: Users have room in their friend's list

    int i = 0;
    while (user1->friends[i] != NULL) {
        i++;
    }
    user1->friends[i] = user2;

    i = 0;
    while (user2->friends[i] != NULL) {
        i++;
    }
    user2->friends[i] = user1;
}

int check_friendship(User *user1, User *user2) {
    // HELPER
    // Return 0 if friends, return 1 if not friends

    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (user1->friends[i] == user2) {
            return 0;
        }
    }
    return 1;
}


/*
 * Print a user profile.
 * For an example of the required output format, see the example output
 * linked from the handout.
 * Return:
 *   - 0 on success.
 *   - 1 if the user is NULL.
 */
int print_user(const User *user) {
    // Check if User exists
    if (user == NULL) {
        return 1;
    }


    // Print photo if photo exists
    FILE *profile_photo = fopen(user->profile_pic, "r");
    char c;
    if (profile_photo) {
        while ((c = getc(profile_photo)) != EOF)
            printf("%c", c);
        printf("\n");
    }
    fclose(profile_photo);

    // Print details
    printf("Name: %s\n", user->name);
    printf("------------------------------------------\n");
    printf("Friends:\n");
    for (int i = 0; i < 10; i++) {
        if (user->friends[i] != NULL){
            printf("%s\n", user->friends[i]->name);
        }
    }
    printf("------------------------------------------\n");
    printf("Posts:\n");
    Post *curr = user->first_post;
    while(curr != NULL) {
        printf("From: %s\n", curr->author);
        printf("Date: %s\n", ctime(curr->date));
        printf("%s\n", curr->contents);
        curr = curr->next;
        if (curr != NULL) {
            printf("\n===\n\n");
        }
    }
    printf("------------------------------------------\n");


    return 0;
}




/*
 * Make a new post from 'author' to the 'target' user,
 * containing the given contents, IF the users are friends.
 *
 * Insert the new post at the *front* of the user's list of posts.
 *
 * 'contents' is a pointer to heap-allocated memory - you do not need
 * to allocate more memory to store the contents of the post.
 *
 * Return:
 *   - 0 on success
 *   - 1 if users exist but are not friends
 *   - 2 if either User pointer is NULL
 */
int make_post(const User *author, User *target, char *contents) {
    
    // Check if Users exist
    if (author == NULL || target == NULL) {
        return 2;
    }


    // Check if Users are friends
    if (check_friendship((User *) author, target) == 1) {
        return 1;
    }


    // Create Post
    Post *new_post = malloc(sizeof(Post));
    strncpy(new_post->author, author->name, MAX_NAME);
    new_post->contents = contents;


    // Initialize time created
    time_t *time_ptr = malloc(sizeof(time_t));
    *time_ptr = time(NULL);
    new_post->date = time_ptr;



    // // Check if a first post exists
    if (target->first_post == NULL) {
        target->first_post = new_post;
        return 0;
    }


    // // Add post to the front if others already exist
    Post *old_first = target->first_post;
    new_post->next = old_first;
    target->first_post = new_post;
    return 0;
}


/*
 * From the list pointed to by *user_ptr_del, delete the user
 * with the given name.
 * Remove the deleted user from any lists of friends.
 *
 * Return:
 *   - 0 on success.
 *   - 1 if a user with this name does not exist.
 */
int delete_user(const char *name, User **user_ptr_del) {
    
    User *user_to_be_deleted = find_user(name, *user_ptr_del);

    // Check existence
    if (user_to_be_deleted == NULL) {
        return 1;
    }

    // Delete friends
    User *friend_of_deleted;
    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (user_to_be_deleted->friends[i] != NULL) {
            friend_of_deleted =  find_user(user_to_be_deleted->friends[i]->name, *user_ptr_del);
            remove_from_friends(user_to_be_deleted, friend_of_deleted);
        }
    }


    // Delete from main user list if first
    if (*user_ptr_del == user_to_be_deleted) {
        *user_ptr_del = (*user_ptr_del)->next;
    } else {
        // Delete from main user list if not first
        User *curr = *user_ptr_del;
        while (curr->next != user_to_be_deleted) {
            curr = curr->next;
        }
        curr->next = curr->next->next;  
    }


    // Free memory from posts sent to this user
    if (user_to_be_deleted->first_post != NULL) {
        Post *curr_post = user_to_be_deleted->first_post;
        Post *next_post = curr_post->next;
        while (next_post != NULL){
            free(curr_post->date);
            free(curr_post->contents);
            free(curr_post);
            curr_post = next_post;
            next_post = next_post->next;
        }
        free(curr_post->date);
        free(curr_post->contents);
        free(curr_post);
    }
    free(user_to_be_deleted);

    return 0;
}

void remove_from_friends(User *unfriendee, User *unfriender) {
    // HELPER
    // Returns 0 when successfully unfriended and returns 1 when not found in friends list
    for (int i = 0; i < MAX_FRIENDS; i++) {
        if (unfriender->friends[i] == unfriendee) {
            unfriender->friends[i] =  NULL;
        }
    }
}



Can you fix the segmentation fault?