#include "hangman.h"

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#define MAX_LENGTH 50
#define ALPHABET_LENGTH 27

int get_word(const char *wordlist, char secret[])
{
    FILE *fp = fopen(wordlist, "rb");
    if (fp == NULL) {
        fprintf(stderr, "No such file or directory: %s\n", wordlist);
        return 1;
    }

    struct stat st;
    stat(wordlist, &st);
    long int size = st.st_size;

    /* FIXME: Can fall into infinite loop... */
    do {
        long int random = (rand() % size) + 1;

        fseek(fp, random, SEEK_SET);

        int result = fscanf(fp, "%*s %20s", secret);
        if (result != EOF)
            break;
    } while (1);

    fclose(fp);

    return 0;
}

static void print_word(const char *word)
{
    for (size_t i = 0; word[i]; i++) {
        printf(" %c", word[i]);
    }
    printf("\n");
}

bool word_guessed(const char secret[], const char letters_guessed[])
{
    size_t l = strlen(secret), k=0;

    for (size_t j = 0; letters_guessed[j]; j++) {
        for (size_t i = 0; secret[i]; i++) {
            if (secret[i] == letters_guessed[j]) {
                k++;
            }
        }
    }

    return l <= k;
}

void censor_word(const char secret[], const char letters_guessed[], char guessed_word[])
{
    size_t i;
    for (i = 0; secret[i]; i++, guessed_word++) {
        *guessed_word = secret[i];

        bool guessed = false;
        for (int j = 0; letters_guessed[j]; j++) {
            if (secret[i] == letters_guessed[j]) {
                guessed = true;
            }
        }

        if (!guessed) {
            *guessed_word = '_';
        }
    }

    *guessed_word = '\0';
}

void get_available_letters(const char letters_guessed[],
        char available_letters[])
{
    size_t k = 0;

    for (char letter = 'a'; letter <= 'z'; letter++) {
        bool to_be_put = true;

        for (size_t j = 0; letters_guessed[j]; j++) {
            if (letters_guessed[j] == letter) {
                to_be_put = false;
                break;
            }
        }

        if (to_be_put) {
            available_letters[k++] = letter;
        }
    }
    available_letters[k] = '\0';
}

static bool been_already_guessed(const char *guessed_letters,
        const char letter)
{
    for (size_t i = 0; guessed_letters[i]; i++) {
        if (guessed_letters[i] == letter) {
            return true;
        }
    }
    return false;
}

static bool is_good_guess(const char secret[], char guessed_word[], const char letter)
{
    bool result = false;
    for (size_t i = 0; secret[i]; i++) {
        if (secret[i] == letter) {
            result = true;
            break;
        }
    }

    if (result) {
        printf("Good guess:");
    } else {
        printf("Oops! That letter is not in my word:");
    }

    print_word(guessed_word);
    return result;
}

static bool guessed_whole(const char *secret, const char *guess)
{
    for (size_t i = 0; secret[i]; i++)
        if (tolower(guess[i]) != secret[i]) {
            return false;
        }

    return true;
}

static int get_guess(char *guess)
{
    int result = scanf("%s", guess);
    if (result == 1) {
        *guess = tolower(*guess);
        return result;
    }

    int ch = 0;
    fprintf(stderr, "Input was not successful.\n");
    while (ch != EOF && (ch = getchar()) != '\n')
        ;
    return result;
}

void hangman(const char secret[])
{
    printf("Welcome to the game, Hangman!\n");

    size_t length_of_word = strlen(secret);
    printf("I am thinking of a word that is %lu letters long.\n",
            length_of_word);

    int guesses = 8, no_of_guessed_letters = 0;
    char available_letters[ALPHABET_LENGTH],
            guessed_letters[ALPHABET_LENGTH] = { 0 };
    char guess[MAX_LENGTH];
    char guessed_word[MAX_LENGTH] = { 0 };
    get_available_letters(guessed_letters, available_letters);

    while (guesses > 0) {
        printf("-------------\n");
        if (word_guessed(secret, guessed_letters)) {
            printf("Congratulations, you won!\n");
            return;
        }

        printf("You have %d guesses left.\n", guesses);
        printf("Available letters: %s\n", available_letters);

        printf("Please guess a letter: ");

        int read_status = get_guess(guess);
        if (read_status == EOF) {
            fprintf(stderr, "No more input to read.\n");
            return;
        } else if (read_status == 0) {
            continue;
        }

        if (guess[1]) {
            if (guessed_whole(secret, guess)) {
                printf("Congratulations, you won!\n");
            } else {
                printf("Sorry, bad guess. The word was %s.\n", secret);
            }
        }

        if (*guess < 'a' || *guess > 'z') {
            printf("Oops! '%c' is not a valid letter:", *guess);
            censor_word(secret, guessed_letters, guessed_word);
            print_word(guessed_word);
            continue;
        }

        if (been_already_guessed(guessed_letters, *guess)) {
            printf("Oops! You've already guessed that letter:");
            censor_word(secret, guessed_letters, guessed_word);
            print_word(guessed_word);
            continue;
        }

        guessed_letters[no_of_guessed_letters++] = *guess;
        get_available_letters(guessed_letters, available_letters);
        censor_word(secret, guessed_letters, guessed_word);

        if (!is_good_guess(secret, guessed_word, *guess)) {
            guesses--;
        }
    }

    if (guesses == 0) {
        printf("-------------\n");
        printf("Sorry, you ran out of guesses. The word was %s.\n", secret);
    }
}