package com.sp.controllers;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import com.alibaba.fastjson.JSON;
import com.sp.app.AppConstants;
import com.sp.app.SPCommon;
import com.sp.app.SPConfig;
import com.sp.app.ChartFileImporter;
import com.sp.app.HttpClient;
import com.sp.app.TickerFileImporter;
import com.sp.app.AppConstants.ChartInterval;
import com.sp.entities.ChartdataDaily;
import com.sp.entities.ChartdataHour;
import com.sp.entities.ChartdataMinute;
import com.sp.entities.ChartdataSecond;
import com.sp.entities.ChartdataTicker;
import com.sp.app.LogTailer.ReadOrder;
import com.sp.models.ChartdataData;
import com.sp.services.ChartdataService;

/**
 * ChartdataSecond controller.
 */
@Controller
public class ChartdataController {

    private ChartdataService cdss;

    @Autowired
    public void setChartdataService(ChartdataService chartdataService) {
        this.cdss = chartdataService;
    }   
    
    @RequestMapping("cutofftime/{prod_code}")
    @ResponseBody
    public String getCutOffTime(@PathVariable String prod_code) {
    	LocalTime dt = cdss.getCutOffTime(prod_code);    	
    	return dt.toString();
    }     
    
    @RequestMapping("houseclean")
    @ResponseStatus(code = HttpStatus.OK)
    @Async
    public void doHouseClean() throws InterruptedException {      	
    	cdss.doHouseClean("tb.chartdata_second");
    	cdss.doHouseClean("tb.chartdata_ticker");
    	cdss.doHouseClean("tb.chartdata_minute");
    	cdss.doHouseClean("tb.chartdata_hour");
    	cdss.doHouseClean("tb.chartdata_daily");
    } 
    
    @RequestMapping("evictcache")
    @ResponseStatus(code = HttpStatus.OK)
    @Async
    public void doEvictCache() {
    	cdss.doEvictCache();
    }    
    
    // recover data from remote db:trading [old]
    @RequestMapping("recover/trading/{src_ip_addr}/{from_unixtime}/{to_unixtime}")
    @ResponseStatus(code = HttpStatus.OK)
    @ResponseBody
    public ResponseEntity<String> doRecoveryTrading(	
    		@PathVariable String src_ip_addr, 
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
        
        String url_s = String.format("http://%s/pserver/chartdata_recoverdata.php?prod_code=FULL&data_type=sec&from_time=%s&to_time=%s",
        		src_ip_addr, from_unixtime, to_unixtime);
        
        String url_m = String.format("http://%s/pserver/chartdata_recoverdata.php?prod_code=FULL&data_type=min&from_time=%s&to_time=%s",
        		src_ip_addr, from_unixtime, to_unixtime);
        
        String url_h = String.format("http://%s/pserver/chartdata_recoverdata.php?prod_code=FULL&data_type=hour&from_time=%s&to_time=%s",
        		src_ip_addr, from_unixtime, to_unixtime);
        
        String url_d = String.format("http://%s/pserver/chartdata_recoverdata.php?prod_code=FULL&data_type=day&from_time=%s&to_time=%s",
        		src_ip_addr, from_unixtime, to_unixtime);
    	
    	HttpClient hce = new HttpClient();    	
        
    	// recover for db second,minute,hour,daily
        cdss.putChartDataLegacy(hce.get(url_s), ChartInterval.SECOND);
        cdss.putChartDataLegacy(hce.get(url_m), ChartInterval.MINUTE);
        cdss.putChartDataLegacy(hce.get(url_h), ChartInterval.HOUR); 
        cdss.putChartDataLegacy(hce.get(url_d), ChartInterval.DAILY); 
                 
		return new ResponseEntity<String>("Recovery done!", HttpStatus.OK); 
    }      
        
    // recover data from remote db:trading_chart [new]
    @RequestMapping("recover/trading_chart/{src_ip_addr}/{from_unixtime}/{to_unixtime}")
    @ResponseStatus(code = HttpStatus.OK)
    @ResponseBody
    public ResponseEntity<String> doRecoveryTradingChart(	
    		@PathVariable String src_ip_addr, 
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
    	
        String port = SPConfig.getInstance().GetProperty("http.port");
        
        String url_s = String.format("http://%s:%s/recoverdata/trading_chart/sec/%s/%s",
        		src_ip_addr, port, from_unixtime, to_unixtime);
        
        String url_m = String.format("http://%s:%s/recoverdata/trading_chart/min/%s/%s",
        		src_ip_addr, port, from_unixtime, to_unixtime);
        
        String url_h = String.format("http://%s:%s/recoverdata/trading_chart/hour/%s/%s",
        		src_ip_addr, port, from_unixtime, to_unixtime);
        
        String url_d = String.format("http://%s:%s/recoverdata/trading_chart/day/%s/%s",
        		src_ip_addr, port, from_unixtime, to_unixtime);
    	
    	HttpClient hce = new HttpClient();    	
        
    	// recover for db second,minute,hour,daily
        cdss.setChartDataSecond(hce.get(url_s)); 
        cdss.setChartDataMinute(hce.get(url_m)); 
        cdss.setChartDataHour(hce.get(url_h)); 
        cdss.setChartDataDaily(hce.get(url_d));         
         
		return new ResponseEntity<String>("Recovery done!", HttpStatus.OK); 
    }    
 
    // recover data from remote db:trading_chart [new]
    @RequestMapping("recoverdata/trading_chart/sec/{from_unixtime}/{to_unixtime}")
    @ResponseBody
    public Iterable<ChartdataSecond> doRecoverDataTradingChartSecond(	
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
    	
    	Date from = SPCommon.GetDateTime(from_unixtime);
    	Date to = SPCommon.GetDateTime(to_unixtime);
    	
    	return cdss.getChartDataSecond(from, to); 	   	       
    } 
    
    @RequestMapping("recoverdata/trading_chart/min/{from_unixtime}/{to_unixtime}")
    @ResponseBody
    public Iterable<ChartdataMinute> doRecoverDataTradingChartMinute(	
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
    	
    	Date from = SPCommon.GetDateTime(from_unixtime);
    	Date to = SPCommon.GetDateTime(to_unixtime);
    	
    	return cdss.getChartDataMinute(from, to); 	   	       
    } 
    
    @RequestMapping("recoverdata/trading_chart/hour/{from_unixtime}/{to_unixtime}")
    @ResponseBody
    public Iterable<ChartdataHour> doRecoverDataTradingChartHour(	
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
    	
    	Date from = SPCommon.GetDateTime(from_unixtime);
    	Date to = SPCommon.GetDateTime(to_unixtime);
    	
    	return cdss.getChartDataHour(from, to); 	   	       
    } 
    
    @RequestMapping("recoverdata/trading_chart/day/{from_unixtime}/{to_unixtime}")
    @ResponseBody
    public Iterable<ChartdataDaily> doRecoverDataTradingChartDaily(	
    		@PathVariable String from_unixtime, 
    		@PathVariable String to_unixtime) throws Exception {
    	
    	Date from = SPCommon.GetDateTime(from_unixtime);
    	Date to = SPCommon.GetDateTime(to_unixtime);
    	
    	return cdss.getChartDataDaily(from, to); 	   	       
    }     
        
    // recover data locally from pserver csv files to DB
    @RequestMapping("recover/file/{from_yyyymmdd}/{to_yyyymmdd}")
    @ResponseStatus(code = HttpStatus.OK)
    @Async
    public void doRecoveryFile(
    		@PathVariable String from_yyyymmdd, 
    		@PathVariable String to_yyyymmdd) throws InterruptedException {    	
    	doConsolidation(from_yyyymmdd, to_yyyymmdd, true);    	
    }    
    
    @RequestMapping("consolidate/{from_yyyymmdd}/{to_yyyymmdd}/{add}")
    @ResponseStatus(code = HttpStatus.OK)
    @Async
    public void doConsolidation(
    		@PathVariable String from_yyyymmdd, @PathVariable String to_yyyymmdd,     		
    		@PathVariable Boolean add) throws InterruptedException {  
    	
		LocalDate dt_from = SPCommon.GetDateFromStringFormat(from_yyyymmdd, "yyyyMMdd");   	    	
		LocalDate dt_to = SPCommon.GetDateFromStringFormat(to_yyyymmdd, "yyyyMMdd");
		LocalDate dt = dt_from;
		
		while (!dt.isAfter(dt_to)) {
	
			if (add) {
				// parse files
				ChartFileImporter.getInstance().StartOnDate(dt, ReadOrder.UNTIL_EOF);
				TickerFileImporter.getInstance().StartOnDate(dt, ReadOrder.UNTIL_EOF);	
						
				// wait until parsing finishes
				while (!ChartFileImporter.getInstance().IsDone()) Thread.sleep(5000);
				while (!TickerFileImporter.getInstance().IsDone()) Thread.sleep(5000);				
			}
			
			// mass populate data in chartdata tables
			cdss.consolidateChartDataTables("ChartdataMinute", dt, 60L);
			cdss.consolidateChartDataTables("ChartdataHour", dt, 3600L);	
			cdss.consolidateChartDataTables("ChartdataDaily", dt, 86400L);		
			
			dt = dt.plusDays(1);
		}
		
		// TODO: May need to refactor so that it can be run in parallel
		if (add) {
			// because we used our instance to reparse files, we need to restart the parse service for today
			// Note that recovery of data SHOULD be done after business/trading hours
			TickerFileImporter.getInstance().StartService();
			ChartFileImporter.getInstance().StartService();
		}
    }  
    
    @RequestMapping("ticker/query/{prod_code}/{bars}")     
    @ResponseBody
    public ResponseEntity<String> queryTicker( 
    		HttpServletRequest req, 
    		HttpServletResponse resp,    		
    		@PathVariable String prod_code, @PathVariable Integer bars               
            ) throws Exception
    {    	
    	// query our data from DB/cache
		Iterable<ChartdataTicker> cdt = cdss.getTickerData(prod_code, bars);     	

		// return ticker format
		resp.setContentType("text/html"); 
		return new ResponseEntity<String>(ChartdataTicker.getTickerTextCSV(cdt), HttpStatus.OK);
    }    
    
    //@RequestMapping("ticker/query")
    //URL mapping made compatible with old [php] format
    @RequestMapping("pserver/ticker_query.php")     
    @ResponseBody
    public ResponseEntity<String> getTicker( 
    		HttpServletRequest req, 
    		HttpServletResponse resp,
            @RequestParam String prod_code,
            @RequestParam Integer second,
            @RequestParam(value = "from_time", required = false) String from_time                   
            ) throws Exception
    {
    	// set some limits
    	if (second < 60) second = 60;
    	if (second > 900) second = 900;   
    	
    	if (from_time == null) {
    		long from = SPCommon.GetDayStartDate(0);
    		from_time = Long.toString(from);   		
    	}
    	
    	// to_time not set, set some arbitrary future date
		long future = SPCommon.GetEpochTime(21001231, 0);
		String to_time = Long.toString(future);  
    	
    	Date from = SPCommon.GetDateTime(from_time);
    	Date to = SPCommon.GetDateTime(to_time);
    	
    	// query our data from DB/cache
		Iterable<ChartdataData> cdd = cdss.getChartDataCached(prod_code, second, from, to);     	

		// return ticker format
		resp.setContentType("text/html"); 
		return new ResponseEntity<String>(ChartdataData.getTickerTextCSV(cdd), HttpStatus.OK);
    }       
    
    //@RequestMapping("chartdata/query")
    //URL mapping made compatible with old [php] format
    @RequestMapping("pserver/chartdata_query.php")    
    @ResponseBody	
    public ResponseEntity<StreamingResponseBody> getChartData( 
    		HttpServletRequest req, 
    		HttpServletResponse resp,
            @RequestParam String prod_code,
            @RequestParam Integer second,
            @RequestParam(value = "from_time", required = false) String from_time,
            @RequestParam(value = "to_time", required = false) String to_time,
            @RequestParam(value = "days", required = false) Integer days,
            @RequestParam(value = "data_mode", required = false) Integer data_mode                      
            ) throws Exception
    {
    	// set some limits
    	if (second < 5) second = 5;
    	if (second > 86400) second = 86400;    	
    	
    	if (data_mode == null)
    		data_mode = AppConstants.DATAMODE_TEXT_CSV;   	
    	
    	if (from_time == null && (days == null || days <= 0)) {
    		// seconds: 5, 10, 30, ... get only from chartdata_second
    		// seconds: 60, 3600, ... get 1 day from chartdata_second + 8 day history from respective table    		
    		if (second < 60) 			days = 1;
    		else if (second < 3600)		days = 8;		// minute
    		else if (second < 86400)	days = 91;		// hourly
    		else						days = 15001;	// daily
    	}   
    	
    	if (from_time == null) {
    		long from = SPCommon.GetDayStartDate(days-1);
    		from_time = Long.toString(from);   		
    	}    		
    	
    	// to_time not set, set some arbitrary future date
    	if (to_time == null) {
    		long future = SPCommon.GetEpochTime(21001231, 0);
    		to_time = Long.toString(future);
    	}  
    	
    	Date from = SPCommon.GetDateTime(from_time);
    	Date to = SPCommon.GetDateTime(to_time);
    	
    	// query our data from DB/cache
    	Iterable<ChartdataData> cdd = null;
    	
    	if (second >= 60)    	
    		cdd = cdss.getChartDataCached(prod_code, second, from, to);		// cache-able history version     	
    	else
    		cdd = cdss.getChartDataCurrent(prod_code, second, from, to);	// current/today table only

		// determine which mode to return in
    	if (data_mode == AppConstants.DATAMODE_DATA_ARRAY) {
    		resp.setContentType("text/html"); 
    		String ss = ChartdataData.getChartdataArray(cdd);
    		final StreamingResponseBody body = out -> out.write(ss.toString().getBytes(), 0, ss.length());    		
    		return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.OK);
    	}
    	else if (data_mode == AppConstants.DATAMODE_TEXT_CSV) { // default			
    		resp.setContentType("text/html"); 
    		String ss = ChartdataData.getChartdataTextCSV(cdd);
    		final StreamingResponseBody body = out -> out.write(ss.toString().getBytes(), 0, ss.length());    		
    		return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.OK);
    	}    	
    	else if (data_mode == AppConstants.DATAMODE_JAVASCRIPT) {    			
    		resp.setContentType("text/javascript"); 	// no idea why this is JS in php
    		String ss = ChartdataData.getChartdataJavaScript(cdd);
    		final StreamingResponseBody body = out -> out.write(ss.toString().getBytes(), 0, ss.length());    		
    		return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.OK);	
    	}
    	else if (data_mode == AppConstants.DATAMODE_JSON_ARRAY) {        	
    		resp.setContentType("application/json");   
    		String ss = JSON.toJSONString(cdd);	
    		final StreamingResponseBody body = out -> out.write(ss.getBytes(), 0, ss.length());    		
    		return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.OK);	    		
    	}    	
    	else {	// should not come here    			
    		resp.setContentType("text/html"); 
    		String ss = ChartdataData.getChartdataHeader();
    		final StreamingResponseBody body = out -> out.write(ss.toString().getBytes(), 0, ss.length());    		
    		return new ResponseEntity<StreamingResponseBody>(body, HttpStatus.BAD_REQUEST);    		
    	}
    }
}