// Copyright (C) 2014 Guibing Guo
//
// This file is part of LibRec.
//
// LibRec is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// LibRec is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with LibRec. If not, see <http://www.gnu.org/licenses/>.
//

package net.librec.recommender.baseline;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.librec.common.LibrecException;
import net.librec.data.DataModel;
import net.librec.recommender.AbstractRecommender;
import net.librec.similarity.TestSimilarity;


public class TimeAwareRecommender extends AbstractRecommender {

    
    private LinkedHashMap<Long, Long> hmItemTime;
    //private ArrayList<Long[]> hmUserItem;
    private Multimap<Long, Long> hmUserItem;
    private DataModel dataModel;
    private Date dateobject = new Date();
    private Calendar calendarobject = Calendar.getInstance();
    
    @Override
    protected void setup() throws LibrecException {
        super.setup();
        openFileItem();
        dataModel = context.getDataModel();
    }


    @Override
    protected void trainModel() throws LibrecException {

    }


    @Override
    protected double predict(int userIdx, int itemIdx) throws LibrecException {
        double d;
      
        //Get our ID of item and user
        BiMap<Integer,String> itemMapping = dataModel.getItemMappingData().inverse();
        Long itemIndexMeetup = Long.parseLong(itemMapping.get(itemIdx));
        Long userIndexMeetup = Long.parseLong(dataModel.getUserMappingData().inverse().get(userIdx));
        //Get time of the item
        Long timeItem = hmItemTime.get(itemIndexMeetup);
        // Get id of other items, get time of other items, add to timeUser
        List<Integer> itemSet = trainMatrix.getColumns(userIdx);
        ArrayList<Long> timeUser = new ArrayList<Long>();
        for(Integer otherItemId : itemSet){
            timeUser.add(hmItemTime.get(Long.parseLong(itemMapping.get(otherItemId))));
        }
        
        //Algorithm: cosine similarity of time Event and all other Events liked by the user. Compute similarity in [-1,1]
        if(timeUser == null || timeUser.size() == 0) return 0;
        d = distanceCos(timeUser,timeItem);
        d *= 2;
        d -= 1;
        return d;
        
    }
    

    public double distanceCos(ArrayList<Long> userTimes, Long itemTime) {
        int indexDay = getDayOfTheWeek(itemTime);
        int indexHour = getHour(itemTime);
        double[][] matrixUser = new double[24][7];
        double norm, count = 0.0;
        int userDay,userHour;
        for(int i = 0; i < userTimes.size(); i++){  //Create u matrix
            userDay = getDayOfTheWeek(userTimes.get(i));
            userHour = getHour(userTimes.get(i));
            matrixUser[userHour][userDay-1] += 1.0/userTimes.size(); 
        }
        
        for (int i = 0; i < matrixUser.length; i++) {
            for (int j = 0; j < matrixUser[0].length; j++) {
                count += matrixUser[i][j] * matrixUser[i][j];
            }
        }
        norm = Math.sqrt(count);
        return matrixUser[indexHour][indexDay-1] / norm;
    }
    
    
    public int getDayOfTheWeek(long time){
        dateobject.setTime(time);
        calendarobject.setTime(dateobject);
        return calendarobject.get(Calendar.DAY_OF_WEEK);
    }
        
    public int getHour(long time){
        dateobject.setTime(time);
        calendarobject.setTime(dateobject);
        return calendarobject.get(Calendar.HOUR_OF_DAY);
    }
    
    public int getHourRange(long time){
        int hour = getHour(time);
        if(hour >= 8 && hour <= 12){
            return 1; //Morning
        }else if(hour >12 && hour<=20){
            return 2; //Afternoon
        }else{ //>21 || <6 
            return 3; //Night
        }
        
    }
    
    
    public void openFileItem(){
        
        hmItemTime = new LinkedHashMap<Long, Long>();
        hmUserItem = HashMultimap.create();
        String[] splitLine;
        Long item,time,user;
        String line;
        try {
            File folder = new File(conf.get("dfs.data.dir") + "\\" + conf.get("data.input.path"));
            File[] fList = folder.listFiles(); //Get folder, then get all entries in folder
            for (File file : fList){
                file.createNewFile();
                Reader targetReader = new FileReader(file);
                BufferedReader br = new BufferedReader(targetReader);
                line = br.readLine();
                while (line != null && !line.isEmpty()) {
                    splitLine = line.split(",");
                    user = Long.parseLong(splitLine[0]);
                    item = Long.parseLong(splitLine[1]);
                    time = Long.parseLong(splitLine[3]);
                    if(!hmItemTime.containsKey(item)){
                        hmItemTime.put(item, time);
                    }
                    hmUserItem.put(user,time);
                    line = br.readLine();
                }
                br.close();
                targetReader.close();
            }
        } catch (Exception ex) {
            Logger.getLogger(TestSimilarity.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
       
}
