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



int friends_check(const User *user1, const User *user2);
void view_post(const Post* view);
void break_up(User *name, const User *target);


/*
 * Check whether two users are friends
 *
 * Return:
 *   - 0 if they are friends
 *   - 1 if either of the user is NULL
 *   - 2 if they are not friends
 */
int friends_check(const User *user1, const User *user2){
    if(user1 == NULL || user2 == NULL){
        return 1;
    }

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


/*
 * Print out a post, including its author, date and contents.
 */
void view_post(const Post* view){
    printf("From: %s\n", view->author);
    printf("Date: %s", ctime(view->date));
    printf("\n%s\n", view->contents);

}


/*
 * Remove the user'target' from the 'friends' arrray of the user 'name'
 */
void break_up(User *name, const User *target){
    if (name == NULL || target == NULL){
        return;
    }
    for(int i = 0; i < MAX_FRIENDS; i++){
        if ((name->friends)[i] == target){
            name->friends[i] = NULL;
        }
    }

}


/*
 * 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) {
    if (strlen(name) > 31){
        return 2;   // return 2 if the given name cannot fit in the 'name' array
    }

    User* curr = *user_ptr_add;

    // if no user has been created
    if (curr == NULL){
        User *new_user = malloc(sizeof(User));
        if (new_user == NULL) {
            perror("malloc");  // prints an error message to stderr
            return 3;
        }
        strcpy(new_user->name, name);

        // Initialize 'next', 'first_post' and every element in 'friends' to NULL
        new_user->next = NULL;
        new_user->first_post = NULL;
        new_user->profile_pic[0] = '\0';
        for (int i = 0; i < MAX_FRIENDS; i++){
            (new_user->friends)[i] = NULL;
        }
        *user_ptr_add = new_user;
        return 0;
    }

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

    if (strcmp(curr->name, name) == 0){
        return 1;   // return 1 if a user by this name already exists in this list
    }else{
        User *new_user = malloc(sizeof(User));
        if (new_user == NULL) {
            perror("malloc");  // prints an error message to stderr
            return 3;
        }
        strcpy(new_user->name, name);
        new_user->next = NULL;
        new_user->first_post = NULL;
        new_user->profile_pic[0] = '\0';
        for (int i = 0; i < MAX_FRIENDS; i++){
            (new_user->friends)[i] = NULL;
        }
        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 && strcmp(curr->name, name) != 0){
        curr = curr->next;
    }
    if (curr == NULL){
        return NULL;
    }else{
        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) {
    printf("User List\n");
    while(curr != NULL){
        printf("\t%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) {
    if (strlen(filename) > 31){
        return 2;   
    }
    FILE *temp = fopen(filename, "r");
    if (temp == NULL){
        fclose(temp);
        return 1;   // return 1 if the file does not exist or cannot be opened.
    }
    strcpy(user->profile_pic, filename);
    int error = fclose(temp);
    if (error != 0) {
        fprintf(stderr, "fclose failed\n");
        return 1;
    }
    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);
    int user1_friend = MAX_FRIENDS;     // The first empty spot index in the 'friends' array of user1
    int user2_friend = MAX_FRIENDS;     // The first empty spot index in the 'friends' array of user2


    if (user1 == NULL || user2 == NULL){
        return 4;
    }else if (user1 == user2){
        return 3;
    }
    for (int i = 0; i < MAX_FRIENDS; i ++){
        if ((user1->friends)[i] == user2 || (user2->friends)[i] == user1){
            return 1;
        }
        if ((user1->friends)[i] == NULL && user1_friend == MAX_FRIENDS){
            user1_friend = i;
        }
        if ((user2->friends)[i] == NULL && user2_friend == MAX_FRIENDS){
            user2_friend = i;
        }

    }

    if (user1_friend == MAX_FRIENDS || user2_friend == MAX_FRIENDS){
        return 2;
    }else{
        (user1->friends)[user1_friend] = user2;
        (user2->friends)[user2_friend] = user1;
        return 0;
    }

}


/*
 * 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) {
    if (user == NULL){
        return 1;
    }
    FILE *pic = fopen(user->profile_pic, "r");
    if (pic != NULL){
        char line[43];
        while(fgets(line, 43, pic) != NULL){
            printf("%s", line);
        }
        printf("\n");
    }
    fclose(pic);
    
    printf("Name: %s\n", user->name);
    printf("------------------------------------------\n");
    printf("Friends:\n");
    for(int i = 0; i < MAX_FRIENDS ; i ++){
        if ((user->friends)[i] != NULL){
            printf("%s\n", ((user->friends)[i])->name);
        }
    }
    printf("------------------------------------------\n");
    printf("Posts:\n");
    if (user->first_post != NULL){
        view_post(user->first_post);
        Post* other_post = (user->first_post)->next;
        while(other_post != NULL){
            printf("\n===\n\n");
            view_post(other_post);
            other_post = other_post->next;
        }
    }
    
    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) {
    if (author == NULL || target == NULL){
        return 2;
    }

    if (friends_check(author, target) != 0){
        return 1;
    }

    Post* new_post = malloc(sizeof(Post));
    strcpy(new_post->author, author->name);
    new_post->contents = contents;
    time_t *now = malloc(sizeof(time_t));
    if (now == NULL) {
        perror("malloc");  // prints an error message to stderr
        return 3;
    }
    *now = time(NULL);
    new_post->date = now;
    new_post->next = target->first_post;
    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* target = find_user(name, *user_ptr_del);
    if (target == NULL){
        return 1;
    }
    for(int i = 0; i < MAX_FRIENDS; i++){
        if((target->friends)[i] != NULL){
            // Delete the user from his/her friend's friend-list.
            break_up((target->friends)[i], target);
            target->friends[i] = NULL;
        }
    }
    Post* target_post = target->first_post;
    while (target_post != NULL){
        Post* temp = target_post->next;
        free((target_post)->date);
        free((target_post)->contents);
        free(target_post);
        target_post = temp;
    }
    User* curr = *user_ptr_del;
    while(curr != NULL){
        // The first user is the user needed to be deleted.
        if (curr == target){
            *user_ptr_del = target->next;
            free(target);
            return 0;
        }

        // The user needed to be deleted is not the first user.
        if ((curr->next) == target){
            curr->next = target->next;
            free(target);
            return 0;
        }
        curr= curr->next;
    }
    return 1;
}

==53683== 160 bytes in 1 blocks are still reachable in loss record 1 of 2
==53683==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==53683==    by 0x109C4A: create_user (friends.c:81)
==53683==    by 0x109470: process_args (friendme.c:31)
==53683==    by 0x109A24: main (friendme.c:166)