/*
 * 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.StatValues;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.strategyquant.lib.results.SQOrder;
import com.strategyquant.lib.results.SQOrderList;
import com.strategyquant.lib.results.SQStats;
import com.strategyquant.lib.results.StatValue;
import com.strategyquant.lib.results.StatsConst;
import com.strategyquant.lib.results.StatsTypeCombination;
import com.strategyquant.lib.settings.SQSettings;
import com.strategyquant.lib.time.SQTime;

public class DaysOfTradingValues extends StatValue {
	public static final Logger Log = LoggerFactory.getLogger("DaysOfTradingValues");
	
	private boolean[] daysInTradeArray;
	private boolean[] monthsInTradeArray;
	private boolean[] yearsInTradeArray;
	private SQTime startingDay = new SQTime();

	private boolean badDatesRecognized = false;
	
	@Override
	public void initCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		// create boolean array for every day, month and year when there could be trade
		if(!ordersList.isEmpty()) {
			long minOrderOpen = -1;
			long maxOrderClose = -1;
			
			for(SQOrder order : ordersList) {
				if(maxOrderClose == -1 || order.CloseTime > maxOrderClose) {
					maxOrderClose = order.CloseTime;
				}
				if(minOrderOpen == -1 || order.OpenTime < minOrderOpen) {
					minOrderOpen = order.OpenTime;
				}
			}

			int totalDays = (int) startingDay.getDaysBetween(minOrderOpen, maxOrderClose)+1;
	    	int totalMonths = startingDay.getMonthsBetween(minOrderOpen, maxOrderClose)+1;
	    	int totalYears = startingDay.getYearsBetween(minOrderOpen, maxOrderClose)+1;
			
	    	if(totalDays > 40000) {
	    		// there cannot be over 40k days, if there is then most probably order dates were in a bad format
	    		badDatesRecognized = true;
	    		return;
	    	}

			daysInTradeArray = new boolean[totalDays];
			monthsInTradeArray = new boolean[totalMonths];
			yearsInTradeArray = new boolean[totalYears];
			
			for(int i=0; i<daysInTradeArray.length; i++) daysInTradeArray[i] = false;
			for(int i=0; i<monthsInTradeArray.length; i++) monthsInTradeArray[i] = false;
			for(int i=0; i<yearsInTradeArray.length; i++) yearsInTradeArray[i] = false;
			
			startingDay.set(minOrderOpen);
		}
	}

	private SQTime openDay = new SQTime();
	private SQTime closeDay = new SQTime();
	
	@Override
	public void computeForOrder(SQStats stats, StatsTypeCombination combination, SQOrder order, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		if(badDatesRecognized) return;
		
		// the algorithm: 
		// go from open date to close date and set all days, months, years 
		// during which this order was open as used
		
		openDay.set(order.OpenTime); 
		closeDay.set(order.CloseTime);
		
		int dayIndexStart = openDay.getDaysBetween(startingDay, openDay);
		int monthIndexStart = openDay.getMonthsBetween(startingDay, openDay);
		int yearIndexStart = openDay.getYearsBetween(startingDay, openDay);

		int dayIndexEnd = openDay.getDaysBetween(startingDay, closeDay);
		int monthIndexEnd = openDay.getMonthsBetween(startingDay, closeDay);
		int yearIndexEnd = openDay.getYearsBetween(startingDay, closeDay);
		
		for(int i = dayIndexStart; i <= dayIndexEnd; i++) daysInTradeArray[i] = true;
		for(int i = monthIndexStart; i <= monthIndexEnd; i++) monthsInTradeArray[i] = true;
		for(int i = yearIndexStart; i <= yearIndexEnd; i++) yearsInTradeArray[i] = true;
	}

	@Override
	public void endCompute(SQStats stats, StatsTypeCombination combination, SQOrderList ordersList, SQSettings settings, SQStats statsLong, SQStats statsShort) throws Exception {
		if(ordersList.isEmpty() || badDatesRecognized) {
			stats.set(StatsConst.TOTAL_TRADING_DAYS, 1);
			stats.set(StatsConst.TOTAL_TRADING_MONTHS, 1);
			stats.set(StatsConst.TOTAL_TRADING_YEARS, 1);
			
		} else {
			// count total trading days, months, years
			stats.set(StatsConst.TOTAL_TRADING_DAYS, getCount(daysInTradeArray));
			stats.set(StatsConst.TOTAL_TRADING_MONTHS, getCount(monthsInTradeArray));
			stats.set(StatsConst.TOTAL_TRADING_YEARS, getCount(yearsInTradeArray));
		}
	}

	/**
	 * return count of array element that are true
	 * @param array
	 * @return
	 */
	private int getCount(boolean[] array) {
		int count = 0;
		
		for(int i=0; i<array.length; i++) {
			if(array[i]) count++;
		}
		
		return count;
	}
}