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

/*
 * 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 to see if name fits into name array.
  if (strlen(name) + 1 > MAX_NAME) {
    return 2;
  }

  User *new_user = malloc(sizeof(struct user));

  if (new_user == NULL) {
    return 3; // malloc failed.
  }

  strncpy(new_user->name, name, MAX_NAME);

  // initailie all values of friends array to null. Usefull for make_friends
  // function.
  for (int i = 0; i < MAX_FRIENDS; i++) {
    new_user->friends[i] = NULL;
  }

  new_user->next = NULL;
	new_user->first_post = NULL;
	
  // if linked list is empty
  if (*user_ptr_add == NULL) {
    *user_ptr_add = new_user;
    return 0;
  }

  User *curr = *user_ptr_add;

  // traverse through the linked list, checking if the name is already taken.
  while (curr->next != NULL) {
    if (strcmp(name, curr->name) == 0) {
      free(new_user);
      return 1;
    }
    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(name, curr->name) == 0) {
      return curr;
    }
    curr = curr->next;
  }
  return NULL;
}

/*
 * 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) {
  // simple traversel of the linked list.
  printf("User List\n");
  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 if file exists.
  FILE *f_ptr = fopen(filename, "r");

  if (f_ptr == NULL) {
    return 1;
  }

  fclose(f_ptr);

  // check if filename can fit in the profile_pic array.
  if (strlen(filename) + 1 > MAX_NAME) {
    return 2;
  }

  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);

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

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

  for (int i = 0; i < MAX_FRIENDS; i++) {
    User *user1_friend = user1->friends[i];

    if (user1_friend != NULL && user1_friend == user2) {
      return 1;
    }
  }

  int user1_reached_max = 1;
  int user2_reached_max = 1;

  for (int i = 0; i < MAX_FRIENDS; i++) {
    User *user1_friend = user1->friends[i];
    User *user2_friend = user2->friends[i];

    if (user1_friend == NULL) {
      user1_reached_max = 0;
    }
    if (user2_friend == NULL) {
      user2_reached_max = 0;
    }
  }

  if (user1_reached_max + user2_reached_max >= 1) {
    return 2;
  }

  for (int i = 0; i < MAX_FRIENDS; i++) {
    User *user1_friend = user1->friends[i];

    if (user1_friend == NULL) {
      user1->friends[i] = user2;
      break;
    }
  }

  for (int i = 0; i < MAX_FRIENDS; i++) {
    User *user2_friend = user2->friends[i];

    if (user2_friend == NULL) {
      user2->friends[i] = user1;
      break;
    }
  }

  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 *f_ptr = fopen(user->profile_pic, "r");
  char buffer[100];
  if (f_ptr != NULL) {
    while (fgets(buffer, 100, f_ptr) != NULL) {
      printf("%s", buffer);
    }
  }

  printf("\nName: %s\n", user->name);
  printf("------------------------------------------\n");
  printf("Friends:\n");
  for (int i = 0; i < MAX_FRIENDS; i++) {
    if (user->friends[i] == NULL) {
      break;
    }
    printf("%s\n", user->friends[i]->name);
  }
  printf("------------------------------------------\n");
  printf("Posts:\n");

  Post *head = user->first_post;

  if (head == NULL) {
    printf("------------------------------------------\n");
    return 0;
  }

  while (head->next != NULL) {
    printf("From: %s\n", head->author);
    printf("date: %s\n", ctime(head->date));
    printf("\n%s", head->contents);
		printf("\n\n");
    printf("===");
		printf("\n\n");
    head = head->next;
  }
  printf("From: %s\n", head->author);
  printf("date: %s\n", ctime(head->date));
  printf("\n%s\n", head->contents);
  printf("------------------------------------------\n");
  return 0;
}

/*
 * Check if user1 and user2 are friends.
 * Return:
 *   - 0 on success
 *   - 1 if users are not friends
 */
int check_friends(const User *user1, const User *user2) {
  for (int i = 0; i < MAX_FRIENDS; i++) {
    User *user1_friend = user1->friends[i];

    if (user1_friend != NULL && user1_friend == user2) {
      return 0;
    }
  }

  return 1;
}

/*
 * 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 (check_friends(author, target) == 1) {
    return 1;
  }
  Post *new_post = malloc(sizeof(Post));
	
	if(new_post == NULL) {
		return 3;
	}
	
  strncpy(new_post->author, author->name, MAX_NAME);
  new_post->contents = contents;
  time_t *t = malloc(sizeof(time_t));

	if (t == NULL) {
		return 3;
	}

  time(t);
  new_post->date = t;

  Post *head = target->first_post;

  if (head == NULL) {
    target->first_post = new_post;
    new_post->next = NULL;
    return 0;
  }

  Post *temp = head;
  target->first_post = new_post;
  new_post->next = temp;

  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 *del_user = find_user(name, *user_ptr_del);
	
	if (del_user == NULL) {
		return 1;
	}

	// remove dangling pointers in the friends of the deleted user.
	for (int i = 0; i < MAX_FRIENDS; i++) {
		User *del_user_friend = del_user->friends[i];

		for (int j = 0; j < MAX_FRIENDS; j++) {
			if (del_user_friend != NULL && del_user_friend->friends[j] == del_user) {
				del_user_friend->friends[j] = NULL;
				break;
			}
		}
	}

	// free posts received by user.
	while (del_user->first_post != NULL) {
		free(del_user->first_post->contents);
		free(del_user->first_post->date);
		Post *next_tmp = del_user->first_post->next;
		free(del_user->first_post);
		del_user->first_post = next_tmp;
	}


	User* head = *user_ptr_del;

  if (head == del_user) {
			*user_ptr_del = del_user->next;
			free(del_user);
      return 0;
  }

	while (head->next != NULL) {
		if (head->next == del_user) {
			head->next = del_user->next;
			free(del_user);
			break;
		}
		head = head->next;
	}

	return 0;
}

valgrind outputs this error:

```
==29619==  Address 0x4a75700 is 32 bytes inside a block of size 160 alloc'd
==29619==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==29619==    by 0x109AF8: create_user (friends.c:29)
==29619==    by 0x109450: process_args (friendme.c:31)
==29619==    by 0x109A04: main (friendme.c:166)
```