package com.josedeveloper.twitter;
 
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
 
import oauth.signpost.OAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
 
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
 
public class TwitterRelationshipApp {
 
	static final String TWITTER_DB_PATH = "DATABASE_PATH";
	static final String CONSUMER_KEY = "YOUR_CONSUMER_KEY";
	static final String CONSUMER_SECRET = "YOUR_CONSUMER_SECRET";
	static final String ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
	static final String ACCESS_TOKEN_SECRET = "YOUR_ACCESS_TOKEN_SECRET";
 
	private final GraphDatabaseService graphDB;
	private final Set totalUsers;
	private final String account;
	private final int count;
 
	enum NodeType implements Label {
		TWITTER_USER, HASHTAG;
	}
 
	enum Relationships implements RelationshipType {
		USE, MENTION;
	}
 
	public TwitterRelationshipApp(final String account, final int count) {
		this.account = account;
		this.count = count;
 
		totalUsers = new HashSet<>();
		graphDB = new GraphDatabaseFactory().newEmbeddedDatabase(new File(TWITTER_DB_PATH));
	}
 
    public static void main(String[] args) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException, ClientProtocolException, IOException
    {
    	TwitterRelationshipApp app = new TwitterRelationshipApp("josedeveloper", 100);
    	app.registerShutdownHook();
    	app.insertUsers();
    	app.insertUserMentionsRelationshipsByUser();
    }
 
	private void insertUsers() throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException, ClientProtocolException, IOException {
    	OAuthConsumer oAuthConsumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
		oAuthConsumer.setTokenWithSecret(ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
 
		HttpGet httpGet = new HttpGet("https://api.twitter.com/1.1/friends/list.json?screen_name=" + account + "&count=" + count); //those who I follow
 
		oAuthConsumer.sign(httpGet);
 
		HttpClient httpClient = HttpClientBuilder.create().build();
		HttpResponse httpResponse = httpClient.execute(httpGet);
 
		//int statusCode = httpResponse.getStatusLine().getStatusCode();
 
		JsonReader reader = Json.createReader(httpResponse.getEntity().getContent());
		JsonObject root = reader.readObject();
		JsonArray users = root.getJsonArray("users");
 
		Iterator iter = users.iterator();
		while (iter.hasNext()) {
			JsonObject user = (JsonObject) iter.next();
 
			try (Transaction tx = graphDB.beginTx()) {
				Node userNode = graphDB.createNode(NodeType.TWITTER_USER);
 
				userNode.setProperty("id", user.getString("id_str"));
				userNode.setProperty("name", user.getString("name"));
				userNode.setProperty("screen_name", user.getString("screen_name"));
 
				insertRelationshipsWithHashtagsByUser(userNode, graphDB);
 
				tx.success();
			} catch (Exception e) {
				System.out.println(e);
			}
 
			totalUsers.add(user.getString("screen_name"));
		}
	}
 
	private void registerShutdownHook() {
 
 
		    // Registers a shutdown hook for the Neo4j instance so that it
		    // shuts down nicely when the VM exits (even if you "Ctrl-C" the
		    // running application).
		Runtime.getRuntime().addShutdownHook(new Thread() {
			@Override
		    public void run() {
				graphDB.shutdown();
		    }
 
		});
	}
 
 
	private static void insertRelationshipsWithHashtagsByUser(Node user, final GraphDatabaseService db) throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException, ClientProtocolException, IOException {
		OAuthConsumer oAuthConsumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
		oAuthConsumer.setTokenWithSecret(ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
 
		HttpGet httpGet = new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + user.getProperty("screen_name"));
		oAuthConsumer.sign(httpGet);
 
		HttpClient httpClient = HttpClientBuilder.create().build();
		HttpResponse httpResponse = httpClient.execute(httpGet);
 
		//int statusCode = httpResponse.getStatusLine().getStatusCode();
 
		JsonReader timelineReader = Json.createReader(httpResponse.getEntity().getContent());
		JsonArray tweets = timelineReader.readArray();
		Iterator tweetsIter = tweets.iterator();
		Map<String, Integer> usedHashtags = new HashMap<>();
		while(tweetsIter.hasNext()) {
			JsonObject tweet = (JsonObject) tweetsIter.next();
 
			JsonObject entities = tweet.getJsonObject("entities");
			JsonArray hashtags = entities.getJsonArray("hashtags");
			Iterator hashtagsIter = hashtags.iterator();
 
			while (hashtagsIter.hasNext()) {
				String hashtag = ((JsonObject) hashtagsIter.next()).getString("text");
 
				if (usedHashtags.containsKey(hashtag)) {
					Integer counter = usedHashtags.get(hashtag);
					usedHashtags.put(hashtag, ++counter);
				} else{
					usedHashtags.put(hashtag, Integer.valueOf(1));
				}
			}
 
		}
 
		for (String hashtag : usedHashtags.keySet()) {
 
			try (Transaction tx = db.beginTx()) {
				Node hashtagNode = db.findNode(NodeType.HASHTAG, "text", hashtag);
				if (hashtagNode == null)
					hashtagNode = db.createNode(NodeType.HASHTAG);
 
				hashtagNode.setProperty("text", hashtag);
 
				Integer timesUsed = usedHashtags.get(hashtag);
				Relationship use = user.createRelationshipTo(hashtagNode, Relationships.USE);
				use.setProperty("times", timesUsed);
 
				tx.success();
			} catch (Exception e) {
				System.out.println(e);
			}
		}
 
 
	}
 
	private void insertUserMentionsRelationshipsByUser() throws OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException, ClientProtocolException, IOException {
		OAuthConsumer oAuthConsumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
		oAuthConsumer.setTokenWithSecret(ACCESS_TOKEN, ACCESS_TOKEN_SECRET);
 
		for (String twitterUser : totalUsers) {
 
			HttpGet httpGet = new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + twitterUser);
			oAuthConsumer.sign(httpGet);
 
			HttpClient httpClient = HttpClientBuilder.create().build();
			HttpResponse httpResponse = httpClient.execute(httpGet);
 
			//int statusCode = httpResponse.getStatusLine().getStatusCode();
 
			JsonReader timelineReader = Json.createReader(httpResponse.getEntity().getContent());
			JsonArray tweets = timelineReader.readArray();
			Iterator tweetsIter = tweets.iterator();
			Map<String, Integer> userMentionsDone = new HashMap<>();
			while(tweetsIter.hasNext()) {
				JsonObject tweet = (JsonObject) tweetsIter.next();
 
				JsonObject entities = tweet.getJsonObject("entities");
				JsonArray userMentions = entities.getJsonArray("user_mentions");
				Iterator hashtagsIter = userMentions.iterator();
 
				while (hashtagsIter.hasNext()) {
					String userMentioned = ((JsonObject) hashtagsIter.next()).getString("screen_name");
 
					if (totalUsers.contains(userMentioned)) {
						if (userMentionsDone.containsKey(userMentioned)) {
							Integer counter = userMentionsDone.get(userMentioned);
							userMentionsDone.put(userMentioned, ++counter);
						} else{
							userMentionsDone.put(userMentioned, Integer.valueOf(1));
						}
					}
				}
 
			}
 
			for (String userMentionDone : userMentionsDone.keySet()) {
 
				try (Transaction tx = graphDB.beginTx()) {
					Node twitterUserMentionedNode = graphDB.findNode(NodeType.TWITTER_USER, "screen_name", userMentionDone);
					Node twitterUserNode = graphDB.findNode(NodeType.TWITTER_USER, "screen_name", twitterUser);
 
					Integer timesMentioned = userMentionsDone.get(userMentionDone);
					Relationship use = twitterUserNode.createRelationshipTo(twitterUserMentionedNode, Relationships.MENTION);
					use.setProperty("times", timesMentioned);
 
					tx.success();
				} catch (Exception e) {
					System.out.println(e);
				}
			}
		}
 
	}
 
}