/*
    Author: 
    
    Given movie ratings from other customers, 
    predict a movie rating from a customer.

    input: 

    1. file 

    # of users
    # of movies
    each row has a name, followed by ratings of movies [all separated by space(s)]

    2. keyboard:  customer name, movie id

    output: screen
    predicted rating, and error
   
 */

import java.util.*;
import java.io.*;


public class HW4
{
    private static final int NUM_CUSTOMERS = 14;
    private static final int NUM_MOVIES = 10;
    private static final String RATINGS_FILENAME = "movieRatings.txt";


    // return distance is sum of absolute difference
    // if one customer does not have a rating, assume rating is 3
    // exclude ratings for the target movie
    // assume both ratings are of the same length
    // ratings1: ratings of one customer, indexed by movie
    // ratings2: ratings of another customer, indexed by movie
    // targetMovie: index of the target movie
    public static int distance(int[] ratings1, int[] ratings2, int targetMovie)
    {
	int distance = 0;

	// start your instructions here




	return distance;
    }



    // return the index of the nearest neighbor of the target customer
    //    only customers who have rated the target movie can be neighbors
    //    print the customer name and distance if he/she has rated the movie
    //    see the sample dialog in the assignment
    // ratings: customers are in rows, movies are in columns
    // customers: names of the customers, indexed by customer/row
    // targetCustomer: index of the target customer
    // targetMovie: index of the target movie
    // ratedMoive: whether a customer has rated the target movie, indexed by customer/row
    public static int nearestNeighbor(int[][] ratings, String[] customers,
				      int targetCustomer, int targetMovie,
				      boolean[] ratedMovie)
    {
	// distance(ratings[0], ratings[1], targetMovie)
	// yields the distance between customers 0 and 1 

	int  nearestNeighbor = 0;

	// start your instructions here



	
	return nearestNeighbor;
    }







    // main method to read in ratings, get a movie and a customer from a user
    // and predict the rating.


    public static void main(String[] arg)
    {
	int[][] ratings = new int[NUM_CUSTOMERS][NUM_MOVIES];
	String[] customers = new String[NUM_CUSTOMERS];

	// read ratings from file
	System.out.println("Reading in data...");
	readRatings(customers, ratings, RATINGS_FILENAME);

	int cust = askForCustomer(customers);
	int movie = askForMovie();


	System.out.println();
	System.out.println("Thinking...");

	int predictedRating = nearestNeighborRating(ratings, customers, cust, movie);
	System.out.println();
	System.out.println("Predicted rating = " +  predictedRating);

	if (ratings[cust][movie] > 0)
	    {
		System.out.println("Real rating = " + ratings[cust][movie]);
		System.out.println("Error (predicted - real) = "  + (predictedRating - ratings[cust][movie]));
	    }
	else
	    {
		System.out.println("Real rating = unknown");
	    }
    }




    // read ratings from ratingFilename into ratings
    // rating 1-5, 0 means not rated
    // each row has a customer name, followed by movie ratings
    public static void readRatings(String[] customers, int[][] ratings, String ratingsFilename)
    {
	try
	    {
		Scanner ratingsFile = new Scanner(new File(ratingsFilename));

		// read customers and ratings
		for (int cust=0; cust < NUM_CUSTOMERS; cust++)
		    {
			customers[cust] = ratingsFile.next();
			for (int movie = 0; movie < NUM_MOVIES; movie++)
			    ratings[cust][movie] = ratingsFile.nextInt();
		    }


		// printing what have been read
		System.out.printf("%10s", "Movie");
		for (int movie = 0; movie < NUM_MOVIES; movie++)
		    System.out.printf(" %d", movie);
		System.out.println();
		System.out.println();

		for (int cust=0; cust < NUM_CUSTOMERS; cust++)
		    {
			System.out.printf("%10s", customers[cust]);
			for (int movie = 0; movie < NUM_MOVIES; movie++)
			    System.out.printf(" %d", ratings[cust][movie]);
			System.out.println();
		    }
		System.out.println();
		
		
	    }
	catch (FileNotFoundException e)
	    {
		System.err.println(e.getMessage());
	    }
    }



    // ask the user to enter the customer name
    public static int askForCustomer(String[] customers)
    {
	Scanner keyboard = new Scanner(System.in);

	System.out.print("Type the customer name: ");
	String custName = keyboard.next();
	int cust = findCustomer(custName, customers);

	while (cust < 0)
	    {
		System.out.println("** Customer " + custName + " was not found **");
		System.out.print("Re-type the customer name: ");
		custName = keyboard.next();
		cust = findCustomer(custName, customers);
	    }
	
	return cust;
    }

    // ask the user to enter the movie
    public static int askForMovie()
    {
	Scanner keyboard = new Scanner(System.in);

	System.out.print("Type the movie number (0-" + (NUM_MOVIES - 1) + "): ");
	int movie = keyboard.nextInt();

	while ((movie < 0) || (movie >= NUM_MOVIES))
	    {
		System.out.println("** movie " + movie + " is out of range ** ");
		System.out.print("Re-type the movie number (0-" + (NUM_MOVIES - 1) + "): ");
		movie = keyboard.nextInt();
	    }

	return movie;
    }


    // given a list of customer names and a target customer name, 
    // return index of the target
    // -1 if not found
    public static int findCustomer(String target, String[] customers)
    {
	int cust=0;
	while ((cust < customers.length) && !target.equalsIgnoreCase(customers[cust]))
	    {
		cust++;
	    }

	if (cust >= customers.length)
	    cust = -1;
	    
        return cust;
    }


    // given the ratings, the customer names, the target customer and movie
    // return the rating of the target movie using the nearest neighbor strategy
    public static int nearestNeighborRating(int[][] ratings, String[] customers, 
					    int targetCustomer, int targetMovie)
    {
	// find customers who rated the movie (excluding the targetCustomer)
	boolean[] ratedMovie = customersWithMovieRating(ratings, targetMovie, targetCustomer);

		// find the nearest neighbor
	int nearestNeighbor = nearestNeighbor(ratings, customers, 
					      targetCustomer, targetMovie,
					      ratedMovie);
	if (nearestNeighbor >= 0)
	    {
		System.out.println("Nearest Neighbor is " + customers[nearestNeighbor]);

		// return the rating of the movie by the nearest neighbor
		return ratings[nearestNeighbor][targetMovie];
	    }
	else 
	    return 0;
    }




    // return a boolean array indicating a customer has rated a movie
    // excluding the targetCustomer
    public static boolean[] customersWithMovieRating(int[][] ratings,
                                         int targetMovie, int targetCustomer)
    {
	boolean[] ratedMovie = new boolean[ratings.length];

	for (int customer = 0; customer < ratings.length; customer++)
	    if ((ratings[customer][targetMovie] > 0) && // customer rated the movie
		(customer != targetCustomer)) 
		ratedMovie[customer] = true;
            else
		ratedMovie[customer] = false;

	return ratedMovie;
    }

}
