package deltafeedapp.subscribers;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import deltafeedapp.pushmodehandlers.PushModeHandler;
import jayeson.lib.delivery.api.SslConfig;
import jayeson.lib.delivery.module.subscriber.ScopedSubscriberConfig;
import jayeson.lib.delivery.module.subscriber.SubscriberConfig;
import jayeson.lib.feed.api.IBetEvent;
import jayeson.lib.feed.api.IBetMatch;
import jayeson.lib.feed.api.IBetRecord;
import jayeson.lib.feed.api.ISnapshot;
import jayeson.lib.feed.api.OddType;
import jayeson.lib.feed.api.twoside.IB2Match;
import jayeson.lib.feed.api.twoside.IB2Record;
import jayeson.lib.feed.api.twoside.PivotType;
import jayeson.lib.feed.basketball.BasketballMatch;
import jayeson.lib.feed.basketball.BasketballTimeType;
import jayeson.lib.feed.soccer.SoccerMatch;
import jayeson.lib.feed.tennis.TennisMatch;
import jayeson.lib.feed.tennis.TennisTimeType;
import jayeson.lib.sports.client.FeedView;
import jayeson.lib.sports.client.SportsConfig;
import jayeson.lib.sports.client.SportsFeedClient;
import jayeson.lib.sports.client.SportsFeedFactory;
import jayeson.lib.sports.codec.FilterData;
import jayeson.lib.sports.core.TTLConfig;
import jayeson.lib.sports.receive.SportsFeedInConfig;
import jayeson.lib.streamfinder.StreamfinderConfig;
import jayeson.model.filterrules.CompanyFilterRule;
import jayeson.model.filterrules.MarketFilterRule;
import jayeson.model.filterrules.PivotTypeFilterRule;
import jayeson.model.filterrules.TimeTypeFilterRule;

public class SportsFeedSubscriber {

	public static void main(String[] args) throws InterruptedException {	
		/* Creating factory before instantiating SportsFeedClient */
		SportsFeedFactory factory = new SportsFeedFactory();
		
		/* 1) Create SportsFeedClient using SubscriberConfig and SportsFeedInConfig */
//		SubscriberConfig subConfig = new SubscriberConfig();
//		ScopedSubscriberConfig scopeConfig = new ScopedSubscriberConfig();
//		StreamfinderConfig sfConf = new StreamfinderConfig("unity_username", "unity_password", "http://feed.url.com", "");
//		List<String> streamList = Arrays.asList("STREAM_NAME");
//		Map<String, List<String>> discMap = new HashMap<String, List<String>>();
//		discMap.put("70", streamList);
//		scopeConfig.setDiscoveryStreams(discMap);
//		scopeConfig.setSfConfig(sfConf);
//		subConfig.setAuthDataRenewIntervalMs(30000);
//		Map<String, ScopedSubscriberConfig> scopeMap = new HashMap<>();
//		scopeMap.put("unity_scope", scopeConfig);
//		subConfig.setScopes(scopeMap);
//		SslConfig sslConfig = new SslConfig();
//		sslConfig.setEnableSelfSignedCert(true);
//		subConfig.setSslConfig(sslConfig);
//		SportsFeedInConfig sportsConfig = new SportsFeedInConfig();
//		TTLConfig ttlConfig = new TTLConfig();
//		ttlConfig.setEnableTtl(false);
//		sportsConfig.setTtlConfig(ttlConfig);
//		SportsFeedClient client = factory.createFromSubscriberConf(subConfig, sportsConfig);
		
		/* 2) Create SportsFeedClient with config file */
//		SportsFeedClient client = factory.createFromConfigFile("path/to/conf/conf.json");
		
		/* 3) Create SportsFeedClient with SportsConfig */
//		SportsConfig sConfig = new SportsConfig("unity_username","unity_password","http://feed.url.com");
//		sConfig.getTtlConfig().setEarlyttl(70000);
//		SportsFeedClient client = factory.createFromConfig(sConfig);
		
		/* 4) Create SportsFeedClient using default config file (located in conf folder - libSportsConfig.json) */
		SportsFeedClient client = factory.create();
		
		/* Finally, start the client */
		client.start();
		
		/* A single client supports multiple views. 
		   Views also determine the shape of records that will be retrieved. 
		   Below are different views created with no filters applied. */
//		FeedView<IB2Match> noFilterIB2MatchFeedView = client.view(IB2Match.class);
		
		/* You can also create a view with specific sport type to use sport specific api */
//		FeedView<SoccerMatch> noFilterSoccerMatchFeedView = client.view(SoccerMatch.class);
//		FeedView<TennisMatch> noFilterTennisMatchFeedView = client.view(TennisMatch.class);
//		FeedView<BasketballMatch> noFilterBasketballMatchFeedView = client.view(BasketballMatch.class);
		
		/* Implementing filters */
		/* This example retrieves only handicap odds. */
		PivotTypeFilterRule pivotRule = new PivotTypeFilterRule();
		pivotRule.setTypes(Arrays.asList(PivotType.HDP));

		/* This example retrieves only odds from specific sportbook in this case it is IBC and SBO. */
		CompanyFilterRule companyRule = new CompanyFilterRule();
		companyRule.setIncludeCompanies(Arrays.asList("IBC","SBO"));

		/* This example retrieves only odds from specific Market Type in this case it is LIVE. */
		MarketFilterRule marketRule = new MarketFilterRule();
		marketRule.setMonitorMarketType(OddType.TODAY);

		/* This example retrieves only odds with specific basketball time type in this case it is BasketballTimeType.FT */
		TimeTypeFilterRule<BasketballTimeType> basketballTimeTypeRule = new TimeTypeFilterRule<BasketballTimeType>();
		basketballTimeTypeRule.setTypes(Arrays.asList(BasketballTimeType.FT)); 

		/* This example retrieves only odds with specific tennis time type in this case it is TennisTimeType(1,0,0) M1S0G0 */
		TimeTypeFilterRule<TennisTimeType> tennisTimeTypeRule = new TimeTypeFilterRule<TennisTimeType>();
		tennisTimeTypeRule.setTypes(Arrays.asList(new TennisTimeType(1,0,0)));

		/* Assign the specified filters into the FilterData object and start using them */
		FilterData hdpTodayFilterData = new FilterData("default", Arrays.asList(pivotRule,marketRule));
		FilterData companyFilterData = new FilterData("default", Arrays.asList(companyRule));
		/* Server side filter decides the records to be received 
		   Pass in the FilterData object into this function to set an upstream filter */
		client.setUpStreamFilter(hdpTodayFilterData);
		
		/* Client side filter decides the records to be sent to the local view(s) 
		   Pass in the IDataFilter object from FilterData object into this function to set a local filter */
		FeedView<IBetMatch> filterView = client.view(companyFilterData.getFilter(), IBetMatch.class);
		
		/* You can now retrieve data from the newly created view. You can choose to process this data by polling the view or by attaching an event handler. */
		/* Process events by attaching an event handler */
		PushModeHandler myHandler = new PushModeHandler("FeedviewFilterByCompany");
		filterView.register(myHandler);
		

		
		/* Read data by polling the view from a feed view, basically it is to repeatedly poll for the latest snapshot */
		while (true) {
		    /* You can update 'IB2Match' to 'SoccerMatch' or 'TennisMatch' or etc. according to the type of FeedView you initialized */
			int limit = 3;
			ISnapshot<IBetMatch> snapshot = filterView.snapshot();
		    Collection<IBetMatch> matches = snapshot.matches();
		    System.out.print("There are currently " + matches.size() + " matches. ");
		    System.out.print("Only printing "+limit+" matches/events/records\n");
		    printMatches(matches,limit);
		    /* Perform business logic on the list of matches */
		    Thread.sleep(5000);
		}
		
		/* Once an EventHandler is no longer needed, it should be unregistered from a view */
//		filterView.unregister(myHandler);
//		client.removeView(filterView);
		
	}
	/* Below are printing function for display purposes */
	public static <M extends IBetMatch> void printMatches(Collection<M> matches, int limit) {
	    Iterator<M> it = matches.iterator();
	    for (int i = 0; i < limit && it.hasNext(); i++) {
	        M match = it.next();
	        System.out.println("-------------------------");
	        System.out.println(match.sportType() + ":" + match.league() + ":" + ((IB2Match)match).participantOne() + ":" + ((IB2Match)match).participantTwo());
	        printEvents(match.events(), limit);
	    }
	}
	
	public static <E extends IBetEvent> void printEvents(Collection<E> events, int limit) {
        Iterator<E> it = events.iterator();
        for (int i = 0; i < limit && it.hasNext(); i++) {
        	IBetEvent event = it.next();
            printRecords(event.records(), limit);
        }
	}
	
	public static <R extends IBetRecord> void printRecords(Collection<R> records, int limit) {
	    Iterator<R> it = records.iterator();
        for (int i = 0; i < limit && it.hasNext(); i++) {
        	IBetRecord record = it.next();
            System.out.println(record.source() + ":" + ((IB2Record)record).pivotType() + ":" + ((IB2Record)record).oddType() + ":" + ((IB2Record)record).rateOver() + ":" + ((IB2Record)record).rateUnder() + ":" + ((IB2Record)record).rateEqual());
        }
	}
	
	public static <M extends IBetMatch> void printMatches(Collection<M> matches) {
		printMatches(matches, matches.size());
	}
	
	public static <E extends IBetEvent> void printEvents(Collection<E> events) {
		printEvents(events, events.size());
	}
	
	public static <R extends IBetRecord> void printRecords(Collection<R> records) {
		printRecords(records, records.size());
	}
}