require 'json'


class Game
    attr_accessor :answer, :correct_guesses, :missed_guesses, :max_misses, :game_over

    def initialize(answer=nil, correct_guesses=[], missed_guesses=[], max_misses=6, game_over=false)
        if answer == nil
            answer = select_word()
        end
        @answer = answer.upcase
        @correct_guesses = correct_guesses
        @missed_guesses = missed_guesses
        @max_misses = max_misses
        @game_over = game_over
    end

    def main()
        while self.game_over == false
            play_round()
            if self.win?
                self.display()
                puts "\nYOU WIN!"
                self.game_over = true
            elsif self.lose?
                self.display()
                puts "\nThe word was #{self.answer}"
                puts "\nYOU LOSE!"
                self.game_over = true
            end
        end
    end

    def win?
        self.missed_guesses.length < self.max_misses && self.answer == encode_word(self.answer, self.correct_guesses)
    end

    def lose?
        self.missed_guesses.length >= self.max_misses
    end

    def display()
        puts display_hangman(self.missed_guesses.length)
        puts encode_word(self.answer, self.correct_guesses)
        puts display_misses(self.missed_guesses)
    end

    def play_round()
        puts "\n"
        self.display()
        puts "Guess a letter: (1 to save, 0 to quit)"
        guess = get_guess()
        if guess == "1" 
            self.save_game()
        elsif guess == "0"
            self.game_over = true
        elsif self.answer[guess]
            self.correct_guesses = self.correct_guesses.append(guess).sort
        else
            self.missed_guesses = self.missed_guesses.append(guess).sort
        end
    end

    def save_game
        puts "\nName of saved game: "
        filename = gets.chomp
        Dir.mkdir('save_data') unless Dir.exist?('save_data')
        File.write("save_data/#{filename}.json", self.to_json)
    end

    def load_game
        puts "\Name of saved game: "
        filename = gets.chomp
        valid_save_game = File.exist?("save_data/#{filename}.json")
        if valid_save_game == true
            file = File.open("save_data/#{filename}.json")
            data = JSON.load file
            file.close
            self.from_json(data)
        else
            puts "save_data/#{filename}.json not found"
        end
        return valid_save_game
    end


    def from_json(data)
        self.answer = data['answer']
        self.correct_guesses = data['correct_guesses']
        self.missed_guesses = data['missed_guesses']
        self.max_misses = data['max_misses']
        self.game_over = data['game_over']
    end

    def to_json
        {
            "answer" => self.answer,
            "correct_guesses" => self.correct_guesses,
            "missed_guesses" => self.missed_guesses,
            "max_misses" => self.max_misses,
            "game_over" => self.game_over
        }.to_json
    end

    def get_guess
        repeat = true
        while repeat
            guess = gets.chomp.upcase
            repeat = !guess_ok?(guess, self.correct_guesses, self.missed_guesses)
        end
        return guess
    end
end


def guess_ok?(guess, correct_guesses, missed_guesses)
    if guess.length != 1
        puts "Guess must be a single character"
        valid = false
    elsif !letters_only?(guess) && guess != "1" && guess != "0"
        puts "Guess must be an alphabetic character"
        valid = false
    elsif correct_guesses.include?(guess) || missed_guesses.include?(guess)
        puts "Guess must be a letter you have not already chosen"
        valid = false
    else
        valid = true
    end
    return valid
end


def display_misses(missed_guesses)
    "Misses: " + missed_guesses.join(", ")
end


def guesses_to_regex(guesses)
    Regexp.new("[^" + guesses.join() + "]")
end


def encode_word(word, correct_guesses)
    if correct_guesses.length > 0
        guesses_regex = guesses_to_regex(correct_guesses)
    else
        guesses_regex = /./
    end
    word.gsub( /#{guesses_regex}/, "_")
end


def display_hangman(strikes)
    case strikes
    when 0
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "      |\n" \
        "      |\n" \
        "      |\n" \
        "      |\n" \
        "========="
    when 1
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        "      |\n" \
        "      |\n" \
        "      |\n" \
        "========="
    when 2
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        "  |   |\n" \
        "      |\n" \
        "      |\n" \
        "========="
    when 3
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        " /|   |\n" \
        "      |\n" \
        "      |\n" \
        "========="
    when 4
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        " /|\\  |\n" \
        "      |\n" \
        "      |\n" \
        "========="
    when 5
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        " /|\\  |\n" \
        " /    |\n" \
        "      |\n" \
        "========="
    when 6
        pic =  
        "  +---+\n" \
        "  |   |\n" \
        "  O   |\n" \
        " /|\\  |\n" \
        " / \\  |\n" \
        "      |\n" \
        "========="
    end
    return pic
end


def select_word()
    all_words = File.read("input/google-10000-english-no-swears.txt").split.select{ |word| word.length > 4 && word.length < 13}
    all_words.sample
end


def letters_only?(string)
    string.match(/[A-Za-z]/) ? true : false
end