/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package testmeetup;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

/**
 *
 * @author Oriol
 */
public class MeetupDataManager {

    private static String city = "New York";
    private static String folder = ".\\DataFiles\\" + city;
    
    /**
     * OPERATIONS.
     *  1) Given n timestamps, will create a EventQuery with those timestamps and get all the information as raw data.
     *  2) Counts the number of events in a range of time.
     *  3) Gets all the relative RSVPS of a Event File.
     *  4) Transforms RSVP File into RSVP DataFile and Member DataFile without positions.
     *  5) Filters RSVP File by distance.
     *  6) Explodes a RSVP Datafile by frequency.
     *  7) Gets positions of a Member Datafile without positions
     * @param args 
     */
    public static void main(String[] args) {
        MeetupDataManager main = new MeetupDataManager();
        
        city = "San Diego";
        folder = ".\\DataFiles\\" + city;
        main.executeOperation(1);
           
    }     
    
    /**
     * Main that executes different processes depending on parameter operation:
     *  1) Downloads all events given a city and a timeframe, splitting that into 4 frames
     *  2) Downloads all RSVP form an event file previously donwloaded
     *  3) Filters RSVP per distance of the event
     *  4) Creates DataSet containing User ID, Event ID, RSVP
     *  5) Splits previous DataSet into a predefined amount of sparsity levels
     *  6) Given a user ID file that can be created from process [4], download all the user location data
     *  7) Given a item ID file that can be created from process [4], Creates a file containing ID and position of all events
     *  8) Creates a specific datafile made for plotting user position data
     *  9) Creates a specific datafile made for plotting event time data
     * @param operation 
     */
    public void executeOperation(int operation){   
        if (operation == 1){ 
            getEvents(1514764800000L,1517443200000L); //Hard coded; change 
        }
        if (operation == 2){
            getRSVPS(1); //Hard coded; change or make foor loop
        }
        if (operation == 3){
            double distance = 100.00;  //Hard coded; change if needed
            ArrayList<String> eventsFiles = FileUtils.getAllEvents(folder);
            ArrayList<String> RSVPFiles = FileUtils.getAllRSVPS(folder);
            ArrayList<String> eventsTofilter = FileConverter.filterEventsByDistance(eventsFiles, distance);
            FileConverter.filterRSVPByEventIds(RSVPFiles,eventsTofilter);
        }
        if (operation == 4){ 
            FileConverter.RSVPtoTXT(FileUtils.getAllFilteredRSVP(folder),FileUtils.getAllEvents(folder),true,true);
        }

        if (operation == 5){
            String fileName = folder + "\\rsvpDF20.txt";
            FileConverter.splitByNRSVP(fileName); //Name of the file is expected to be that one from operation 4; hard coded
        }
        if (operation == 6){
            String fileName = folder + "\\Members1.txt"; //Filename hard coded; change if neccessary
            FileConverter.getLatLon(fileName,folder); 
        }
        if (operation == 7){
            String fileName = folder + "\\itemsDF1-20.txt"; //Filename hard coded; change if neccessary
            FileConverter.addPosEvents(FileUtils.getAllFilteredRSVP(folder),fileName);
        }
        if (operation == 8){
            FileConverter.EventsToMap(FileUtils.getAllEvents(folder));
        }
        if (operation == 9){
            String fileName = folder + "\\rsvpDF20.txt";  //Filename hard coded; change if neccessary
            FileConverter.TimestampsToDate(fileName);
        }
        
    }


    ///////////////////////OPERATION 1 AND 2 (DOWNLOADING DATA)
    
    /**
     * Retrieves one month of events. Splits start time and end time in 5 separate timestamps, 4 ranges
     * @param start timestamp of start, preferably of the start of a month.
     * @param end timestamp of end, preferably of the end of a month.
     */
    public void getEvents(Long start, Long end){
        QueryEventBuilder qeb = new QueryEventBuilder();
        Long[] times = {start,start+(1/4)*end,start+(2/4)*end,start+(3/4)*end,end};
        
        for (int i = 0; i < times.length -1; i++) {
            System.out.println("=================> Iteration:" + i);

            QueryEvent qe = qeb
                .country("US")
                .state(getState(city))
                .city(city)
                .nResults("10000")
                .convertedTime(times[i].toString() + "," + times[i+1].toString())
                .status("past")
                .radius("100")
                .buildQuery();
            getAllInformation(qe);        
        }
    }
    
    
    /**
     * This method sends and captures the response of the given query, then if the parameter "next" appears on the response, keep sending until all the information is retrieved
     * @param qe Original Query. URL of the query changes while crawling the data.
     */
    public void getAllInformation(QueryEvent qe){
        JSONParser parser = new JSONParser();
        long count = 0;
        long total_count = 100;
        int i = 0;
        JSONObject response,meta = null;
        String fullResponse = "";
        Boolean nextLink = true;
        
        try {
            
            while(count < total_count && nextLink){
                if(i==0){  //First time get URL from QueryObject, then use the given "next" URL until end
                    qe.generateQuery();
                    response = (JSONObject)parser.parse(QuerySender.sendQuery(qe.getURL()));
                }else{
                    response = (JSONObject)parser.parse(QuerySender.sendQuery(meta.get("next").toString()));
                }
                meta = (JSONObject)response.get("meta");
                count += (Long) meta.get("count");
                total_count = (Long) meta.get("total_count");
                nextLink =  ((String) meta.get("next")).length() != 0;  //Stopping condition
                i++;
                
                System.out.println("---------------------------------------------------------------------------------");
                System.out.println("CALL PROCESSED (" + i + "): " + count + " out of " + total_count + " results. ");
                System.out.println("Next Link: " + meta.get("next"));
                System.out.println("---------------------------------------------------------------------------------");
                
                
                //SAVE RESPONSE
                fullResponse += response.get("results").toString();
            }
            FileUtils.saveData(fullResponse,null);
        } catch (Exception ex) {
            Logger.getLogger(MeetupDataManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    /**
     * Get a single RSVP, given a RSVP query.
     * @param qrsvp RSVP Query
     * @return response of the query sent
     * @throws Exception Special cases that will be captured in getRSVPS
     */
    public JSONArray getRSVP(QueryRSVP qrsvp) throws Exception{
        JSONParser parser = new JSONParser();
        JSONArray response = null;
        qrsvp.generateQuery();
        response = (JSONArray) parser.parse(QuerySender.sendQuery(qrsvp.getURL()));
        return response;
    }
    
    /**
     * This function reads a single Events file and makes RSVP Queries for each event present in the file. If the event is private, the RSVP query will fail, and the event will be deleted.
     * @param filenumber number of the EventFile. In my case I saved the Event files as Event1, Event2... 
     */
    public void getRSVPS(Integer filenumber){
        String vnumber = filenumber.toString();
        String eventsFilename = ".\\DataFiles\\"+ city +"\\Events"+ vnumber +".txt"; //Hard coded filename; change or put it in a for loop
        JSONArray events = FileUtils.loadData(eventsFilename);  //Load JSON file into JSONArray
        QueryRSVP qrsvp = new QueryRSVP();
        JSONArray response = null;
        String fullResponse = "";
        System.out.println("NUMBER OF RSVPS TO GET: " + events.size());
        for (int i = 0; i < events.size(); i++) {   //For every JSONObject in JSONArray, parse information to form the RSVP Query that will be sent, processed and added to RSVP file.
            JSONObject event = (JSONObject) events.get(i);
            JSONObject group = (JSONObject) event.get("group");
            qrsvp.setEventID(event.get("id").toString());
            qrsvp.setGroupURL(group.get("urlname").toString());
            try {
                response = getRSVP(qrsvp);
                fullResponse += response.toString();
            } catch (java.net.ProtocolException ex) { //403 forbidden                
                System.out.println("!---------------------------------------------------------------------------------!");
                System.out.println("DELETED EVENT BECAUSE IT WAS PRIVATE!");
                System.out.println("FULL LOG:");
                System.out.println("EVENT REMOVED: " + events.get(i).toString());
                System.out.println("RESPONSE GOT FROM SEND: " + response.toString());
                System.out.println("!---------------------------------------------------------------------------------!");
                events.remove(i); //Delete the event because it's a private event
                
            } catch (Exception ex) {
                Logger.getLogger(MeetupDataManager.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        FileUtils.saveData(events.toString(),".\\DataFiles\\"+ city +"\\Events"+ vnumber + ".txt");
        FileUtils.saveData(fullResponse,".\\DataFiles\\"+ city +"\\RSVP"+ vnumber+ ".txt");  //We need to save RSVP and Events in case some event was deleted in the process
    }

    
    /**
     * Utils hardcoded function to get States from country.
     * @param city one of five cities
     * @return State of the country (US)
     */
    public String getState(String city){
        String state;
        switch(city){
            case "New York":
                state = "NY";
                break;
            case "Los Angeles":
                state = "CA";
                break;
            case "Washington":
                state = "DC";
                break;
            case "San Diego":
                state = "CA";
                break;
            case "Chicago":
                state = "IL";
                break;
            default:
                state = "NA";
        }
        return state;
    } 
      
    
    
    //This method estimates the number of results in the timerange specified (Events).
    // NOT IN USE
    public void getNResults(QueryEvent qe, Long increment) {
        JSONParser parser = new JSONParser();
        JSONObject response,meta = null;
        String range = qe.getTime();  //suposing time is range
        String[] dates = range.split(",");
        Long startts = Long.parseLong(dates[0]);
        Long endts = Long.parseLong(dates[1]);
        Long incremented;
        Long count,total_count = 0L;
        Boolean lastIteration = false;
        
        try {
            while(!lastIteration){
                if(startts + increment >= endts){
                    lastIteration = true;
                }
                //calculate time range of 1 increment
                incremented = Math.min(startts + increment,endts);
                range = startts.toString() + "," + incremented.toString();
                qe.setTime(range);
                qe.generateQuery();
                response = (JSONObject)parser.parse(QuerySender.sendQuery(qe.getURL()));
                //get number of unique elements
                meta = (JSONObject)response.get("meta");
                count = (Long) meta.get("total_count");
                total_count += count;
                //Shift start by one increment so we can continue
                startts += increment;
                System.out.println("Count in this increment: " + count);
            }
            
            System.out.println("---------------------------------------------------------------------------------");
            System.out.println("CALL PROCESSED: counted " + total_count + " unique results. ");
            System.out.println("---------------------------------------------------------------------------------");
            
        } catch (Exception ex) {
            Logger.getLogger(MeetupDataManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
