/*
 * Copyright (c) 2015, StrategyQuant - All rights reserved.
 *
 * Code in this file was made in a good faith that it is correct and does what it should. 
 * If you found a bug in this code OR you have an improvement suggestion OR you want to include 
 * your own code snippet into our standard library please contact us at:
 * http://tasks.strategyquant.com/projects/snippets/
 *
 * This code can be used only within StrategyQuant products. 
 * Every owner of valid (free, trial or commercial) license of any StrategyQuant product 
 * is allowed to freely use, copy, modify or make derivative work of this code without limitations,
 * to be used in all StrategyQuant products and share his/her modifications or derivative work 
 * with the StrategyQuant community.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES 
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *  
 */
package com.strategyquant.extend.OverviewTab;

import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;

import com.strategyquant.lib.computers.MonthlyPerformanceComputer;
import com.strategyquant.lib.language.L;
import com.strategyquant.lib.openBalances.MonthlyEndingOpenBalance;
import com.strategyquant.lib.openBalances.OpenBalance;
import com.strategyquant.lib.results.SQOrderList;
import com.strategyquant.lib.results.SQResult;
import com.strategyquant.lib.results.SQResultsGroup;
import com.strategyquant.lib.results.SQStats;
import com.strategyquant.lib.results.StatsConst;
import com.strategyquant.lib.results.StatsTypeCombination;
import com.strategyquant.lib.settings.SQConst;
import com.strategyquant.lib.snippets.OverviewTemplate;

public class SQDefault extends OverviewTemplate {
	
	public SQDefault() {
		setName(L.t("SQ Default"));
		setScreenshotName("sqdefault_screenshot.jpg");
		setHtmlTemplateName("sqdefault_template.htm");
	}
	
	@Override
	public String drawValues(SQResultsGroup strategyResult, String symbol, StatsTypeCombination combination) throws Exception {
		template = loadTemplate(); 
		replaceValues(strategyResult, symbol, combination);
		
		return template;
	}

	public void replaceValues(SQResultsGroup strategyResult, String symbol, StatsTypeCombination combination) {
		SQStats stats = null;
		SQResult result = null;
		
		if(strategyResult!=null) {
	       	result = strategyResult.getResult(symbol);
	   	   	
	       	if(result!=null) {
	   	   		stats = result.getStats(combination);
	   	   	}
		}

		//BIG
		String css_class_small = combination.getPLType().equals(SQConst.PL_IN_TICKS) ? " small" : "";
		
       	replace("Big", 
       			"TOTAL PROFIT", 
       			stats==null ? NA   : d2WithPlType(stats.getDouble(StatsConst.NET_PROFIT), combination.getPLType()), 
       			stats==null ? null : stats.getDouble(StatsConst.NET_PROFIT)>0 ? "positiveNum"+css_class_small : "negativeNum"+css_class_small);	
       	
       	//SMALL1
       	if(combination.getPLType().equals(SQConst.PL_IN_MONEY)) {
       		SQStats statsPips = null;
       		
       		if(result!=null) {
       			statsPips = result.getStats(combination.getDirection(), SQConst.PL_IN_TICKS, combination.getSampleType());
       		}
       		
       		replace("Small1", 
       				"PROFIT IN PIPS", 
       				statsPips==null ? NA   : d2WithPlType(statsPips.getDouble(StatsConst.NET_PROFIT), SQConst.PL_IN_TICKS), 
       				statsPips==null ? null : statsPips.getDouble(StatsConst.NET_PROFIT)>0 ? "positiveNum" : "negativeNum");	
       	} else { 
       		SQStats statsMoney = null;
       		
       		if(result!=null) {
       			statsMoney = result.getStats(combination.getDirection(), SQConst.PL_IN_MONEY, combination.getSampleType());
       		}
       		
       		replace("Small1", 
       				"PROFIT IN MONEY", 
       				statsMoney==null ? NA   : d2WithPlType(statsMoney.getDouble(StatsConst.NET_PROFIT), SQConst.PL_IN_MONEY), 
       				statsMoney==null ? null : statsMoney.getDouble(StatsConst.NET_PROFIT)>0 ? "positiveNum" : "negativeNum");	
       	}
       	
       	//SMALL2
   		replace("Small2", 
   				"YEARLY AVG PROFIT", 
   				stats==null ? NA   : d2WithPlType(stats.getDouble(StatsConst.AVG_PROFIT_BY_YEAR), combination.getPLType()), 
   				stats==null ? null : stats.getDouble(StatsConst.AVG_PROFIT_BY_YEAR)>0 ? "positiveNum" : "negativeNum");	
       	
   		//SMALL3
   		SQStats statsPct = null;
   		
   		if(result!=null) { 
   			statsPct = result.getStats(combination.getDirection(), SQConst.PL_IN_PCT, combination.getSampleType());
   		}
   		
   		replace("Small3", 
   				"YEARLY AVG % RETURN", 
   				statsPct==null ? NA   : d2WithPlType(statsPct.getDouble(StatsConst.AVG_PROFIT_BY_YEAR), SQConst.PL_IN_PCT), 
   				statsPct==null ? null : statsPct.getDouble(StatsConst.AVG_PROFIT_BY_YEAR)>0 ? "positiveNum" : "negativeNum");	
       	
   		replace("Small4", 
   				"CAGR", 
   				stats==null ? NA   : d2WithPlType(stats.getDouble(StatsConst.CAGR), SQConst.PL_IN_PCT), 
   				stats==null ? null : statsPct.getDouble(StatsConst.CAGR)>0 ? "positiveNum" : "negativeNum");	

   		replace("1_1", "# OF TRADES", stats==null ? NA : ""+stats.getInt(StatsConst.NUMBER_OF_TRADES));
       	replace("1_2", "SHARPE RATIO", stats==null ? NA : d2(stats.getDouble(StatsConst.SHARPE_RATIO)));
       	replace("1_3", "PROFIT FACTOR", stats==null ? NA : d2(stats.getDouble(StatsConst.PROFIT_FACTOR)));
       	replace("1_4", "RETURN / DD RATIO", stats==null ? NA : d2(stats.getDouble(StatsConst.RETURN_DD_RATIO)));
       	replace("1_5", "WINNING PERCENTAGE", stats==null ? NA : d2(stats.getDouble(StatsConst.WINNING_PCT))+" %");
       	
       	String dd_key = combination.getPLType().equals(SQConst.PL_IN_PCT) ? StatsConst.PCT_DRAWDOWN : (combination.getPLType().equals(SQConst.PL_IN_TICKS) ? StatsConst.PIPS_DRAWDOWN : StatsConst.DRAWDOWN);
  	
       	replace("2_1", "DRAWDOWN", stats==null ? NA : d2WithPlType(stats.getDouble(dd_key), combination.getPLType()));
       	replace("2_2", "% DRAWDOWN", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.PCT_DRAWDOWN), SQConst.PL_IN_PCT));
       	replace("2_3", "DAILY AVG PROFIT", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.AVG_PROFIT_BY_DAY), combination.getPLType()));
       	replace("2_4", "MONTHLY AVG PROFIT", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.AVG_PROFIT_BY_MONTH), combination.getPLType()));
       	replace("2_5", "AVERAGE TRADE", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.AVG_TRADE), combination.getPLType()));
       	
       	replace("3_1", "ANNUAL % / Max DD %", stats==null ? NA : d2(stats.getDouble(StatsConst.AAR_DD_RATIO)));
    	replace("3_2", "R EXPECTANCY", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.R_EXPECTANCY), "R"));
    	replace("3_3", "R EXPECTANCY SCORE", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.R_EXPECTANCY_SCORE), "R"));
    	replace("3_4", "STR QUALITY NUMBER", stats==null ? NA : d2(stats.getDouble(StatsConst.SQN)));
    	replace("3_5", "SQN SCORE", stats==null ? NA : d2(stats.getDouble(StatsConst.SQN_SCORE)));
    	
    	//Strategy
    	replace("S1_1", "Wins / Losses Ratio", stats==null ? NA : d2(stats.getDouble(StatsConst.WIN_LOSS_RATIO)));
    	replace("S1_2", "Payout Ratio (Avg Win/Loss)", stats==null ? NA : d2(stats.getDouble(StatsConst.PAYOUT_RATIO)));
    	replace("S1_3", "Average # of Bars in Trade", stats==null ? NA : d2(stats.getDouble(StatsConst.AVG_BARS_TRADE)));
    	
    	replace("S2_1", "AHPR", stats==null ? NA : d2(stats.getDouble(StatsConst.AHPR)));
    	replace("S2_2", "Z-Score", stats==null ? NA : d2(stats.getDouble(StatsConst.Z_SCORE)));
    	replace("S2_3", "Z-Probability", stats==null ? NA : d2(stats.getDouble(StatsConst.Z_PROBABILITY))+" %");
    	
    	replace("S3_1", "Expectancy", stats==null ? NA : d2(stats.getDouble(StatsConst.EXPECTANCY)));
    	replace("S3_2", "Deviation", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.STANDARD_DEV), combination.getPLType()));
    	replace("S3_3", "Max Pos. Exposure", stats==null ? NA : ""+stats.getInt(StatsConst.MAX_POS_EXPOSURE));
    	
    	replace("S4_1", "Stagnation in Days", stats==null ? NA : ""+stats.getInt(StatsConst.STAGNATION_PERIOD));
    	replace("S4_2", "Stagnation in %", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.STAGNATION_PERIOD_PCT), SQConst.PL_IN_PCT));
    	replace("S4_3", "Max Lots Exposure", stats==null ? NA : d2(stats.getDouble(StatsConst.MAX_LOTS_EXPOSURE, 0)));

    	//Trades
    	replace("T1_1", "", "");
    	replace("T1_2", "# of Wins", stats==null ? NA : ""+stats.getInt(StatsConst.NUMBER_OF_PROFITS));
    	replace("T1_3", "# of Losses", stats==null ? NA : ""+stats.getInt(StatsConst.NUMBER_OF_LOSSES));
    	replace("T1_4", "# of Cancelled/Expired", stats==null ? NA : ""+stats.getInt(StatsConst.NUMBER_OF_CANCELED));
    	
    	replace("T2_1", "Gross Profit", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.GROSS_PROFIT), combination.getPLType()));
    	replace("T2_2", "Gross Loss", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.GROSS_LOSS), combination.getPLType()));
    	replace("T2_3", "Average Win", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.AVG_WIN), combination.getPLType()));
    	replace("T2_4", "Average Loss", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.AVG_LOSS), combination.getPLType()));
    	
    	replace("T3_1", "Largest Win", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.MAX_PROFIT), combination.getPLType()));
    	replace("T3_2", "Largest Loss", stats==null ? NA : d2WithPlType(stats.getDouble(StatsConst.MAX_LOSS), combination.getPLType()));
    	replace("T3_3", "Max Consec Wins", stats==null ? NA : ""+stats.getInt(StatsConst.MAX_CONSEC_WINS));
    	replace("T3_4", "Max Consec Losses", stats==null ? NA : ""+stats.getInt(StatsConst.MAX_CONSEC_LOSS));
    	
    	replace("T4_1", "Avg Consec Wins", stats==null ? NA : d2(stats.getDouble(StatsConst.AVG_CONSEC_WIN)));
    	replace("T4_2", "Avg Consec Loss", stats==null ? NA : d2(stats.getDouble(StatsConst.AVG_CONSEC_LOSS)));
    	replace("T4_3", "Avg # of Bars in Wins", stats==null ? NA : d2(stats.getDouble(StatsConst.AVG_BARS_WIN)));
    	replace("T4_4", "Avg # of Bars in Losses", stats==null ? NA : d2(stats.getDouble(StatsConst.AVG_BARS_LOSS)));
    	
    	//monthly performance
    	addMonthlyPerformanceTable(strategyResult, symbol, combination);
    	
    	//open balances
    	ArrayList<OpenBalance> openBalanceList = (ArrayList<OpenBalance>) strategyResult.get(SQConst.OPEN_BALANCES);
    	if(openBalanceList!=null) {
    		addOpenBalancesTable(openBalanceList);
    	}
	}

	private void addOpenBalancesTable(ArrayList<OpenBalance> openBalanceList) {
		String tableBody = printOpenBalanceListTableBody(openBalanceList);

		String content = "<div class=\"performance\">"+
		    "<h1>Monthly Ending Equity ($)</h1>"+
		    "<h4 style=\"text-align: left\">Displays equity (including open trades, excluding initial capital) at the end of each month</h4>"+
		    "<table class=\"calendar\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">"+
			"<tr class=\"months\">"+
			"<td>Year</td><td>Jan</td><td>Feb</td><td>Mar</td><td>Apr</td><td>May</td><td>Jun</td><td>Jul</td><td>Aug</td><td>Sep</td><td>Oct</td><td>Nov</td><td>Dec</td><td>YTD</td>"+
			"</tr>"+
			tableBody+
		    "</table>"+
		    "</div>";

		template = template.replace("<!-- end of data -->", content+"\n<!-- end of data -->");
	}

	private String printOpenBalanceListTableBody(ArrayList<OpenBalance> openBalanceList) {
		String tableBody = "";

		TreeMap<Integer, Double[]> mbMap = MonthlyEndingOpenBalance.compute(openBalanceList);
	
		String trClass;
		int row=0;
		
        for(Map.Entry<Integer, Double[]> entry : mbMap.descendingMap().entrySet()) {	        	
        	String year = entry.getKey().toString();
        	Double[] months = entry.getValue();
        	   
        	if(row%2==0) {
        		trClass = "oddrow";
        	} else {
        		trClass = "evenrow";
        	}
        	
        	tableBody+="<tr class=\""+trClass+"\">";
        	tableBody+="<td class=\"bold\">"+year+"</td>";
        	
        	for(int m=0; m<months.length; m++) {
        		if(months[m]<0) {
        			tableBody+="<td class=\"negativeNum\">"+d2(months[m])+"</td>";
        		} else {
        			tableBody+="<td>"+d2(months[m])+"</td>";
        		}        		
        	}
        	
        	tableBody+="</tr>";
        	row++;
        }
        
        return tableBody;
	}

	private void addMonthlyPerformanceTable(SQResultsGroup strategyResult, String symbol, StatsTypeCombination combination) {
		String tableBody = printMonthlyPerformanceTableBody(strategyResult, symbol, combination);

		String content = "<div class=\"performance\">"+
		    "<h1>Monthly Performance ("+combination.getPLType()+")</h1>"+
		    "<table class=\"calendar\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">"+
			"<tr class=\"months\">"+
			"<td>Year</td><td>Jan</td><td>Feb</td><td>Mar</td><td>Apr</td><td>May</td><td>Jun</td><td>Jul</td><td>Aug</td><td>Sep</td><td>Oct</td><td>Nov</td><td>Dec</td><td>YTD</td>"+
			"</tr>"+
			tableBody+
		    "</table>"+
		    "</div>";

		template = template.replace("<!-- end of data -->", content+"\n<!-- end of data -->");
	}

	private String printMonthlyPerformanceTableBody(SQResultsGroup strategyResult, String symbol, StatsTypeCombination combination) {
		String tableBody = "";
		
		if(strategyResult==null) {
			return "<tr class=\"oddrow\">"
					+ "<td class=\"bold\">NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "<td>NA</td>"
					+ "</tr>";
		}
		
		SQOrderList orderList = strategyResult.getOrdersForResultKey(symbol);
		
		TreeMap<Integer, Double[]> mpMap = MonthlyPerformanceComputer.compute(orderList, combination.getPLType());
	
		String trClass;
		int row=0;
		
        for(Map.Entry<Integer, Double[]> entry : mpMap.descendingMap().entrySet()) {	        	
        	String year = entry.getKey().toString();
        	Double[] months = entry.getValue();
        	   
        	if(row%2==0) {
        		trClass = "oddrow";
        	} else {
        		trClass = "evenrow";
        	}
        	
        	tableBody+="<tr class=\""+trClass+"\">";
        	tableBody+="<td class=\"bold\">"+year+"</td>";
        	
        	for(int m=0; m<months.length; m++) {
        		if(months[m]<0) {
        			tableBody+="<td class=\"negativeNum\">"+d2(months[m])+"</td>";
        		} else {
        			tableBody+="<td>"+d2(months[m])+"</td>";
        		}        		
        	}
        	
        	tableBody+="</tr>";
        	row++;
        }
        
        return tableBody;
	}
}
