#define SECONDS_IN_DAY			86400
#define SECONDS_IN_HOUR			3600
#define SECONDS_IN_MINUTE		60

#define SET_PREVCLOSE 		0
#define SET_DAILY_EST 		1
#define SET_WEEKLY 			2
#define SET_MONTHLY			3

#define PERIOD_CLOSE		0
#define PERIOD_HIGH			1
#define PERIOD_LOW			2

#define COLOR_P   SlateGray
#define COLOR_1   Olive
#define COLOR_2   Gray
#define COLOR_3   DarkGreen



#property  copyright "jason normandin"
// very slighty modified by Shadders for speed optimisation.  
// Full credit to Jason Normandin ('Tesla') for original work which is pretty
// much everything except for a couple of lines I changed.
#property  link      ""

// Pivot point lines should display in chart prin
#property indicator_chart_window

// Seven Lines to display
// In order, buffers are:
// R3, R2, R1, PP, S1, S2, S3
#property indicator_buffers 7

// Indicator line colors
#property indicator_color1 White
#property indicator_color2 White
#property indicator_color3 White
#property indicator_color4 White
#property indicator_color5 White
#property indicator_color6 White
#property indicator_color7 White


// Default Set will be daily eastern close
extern int pivotset	= SET_DAILY_EST;

// Buffers
double     resist3[];
double     resist2[];
double     resist1[];
double     pivotpoint[];
double     support1[];
double     support2[];
double     support3[];

// Holds Readable Name of Set for line labelling
string periodName;


int init() {

	color linecolor = White;
	

	// Set periodName and linecolor
	switch (pivotset) {
		case SET_PREVCLOSE:
			periodName = "FourHour";
			break;
		case SET_DAILY_EST:
			periodName = "Daily";
			linecolor = COLOR_P;
			break;
		case SET_WEEKLY:
			periodName = "Weekly";
			linecolor = DodgerBlue;
			break;
		case SET_MONTHLY:
			periodName = "Monthly";
			linecolor = MediumBlue;
			break;
	}
	
	// Set indicator name	
	IndicatorShortName(periodName + " Pivots");
	
	// Assign Buffers and their properties
	SetIndexBuffer(0, resist3);
	SetIndexStyle(0, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_3);
	SetIndexLabel(0, periodName + " R3");
	
	SetIndexBuffer(1, resist2);
	SetIndexStyle(1, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_2);
	SetIndexLabel(1, periodName + " R2");
	
	SetIndexBuffer(2, resist1);
	SetIndexStyle(2, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_1);
	SetIndexLabel(2, periodName + " R1");
	
	SetIndexBuffer(3, pivotpoint);
	SetIndexStyle(3, DRAW_LINE, EMPTY, EMPTY, linecolor);
	SetIndexLabel(3, periodName + " Pivot");
	
	SetIndexBuffer(4, support1);
	SetIndexStyle(4, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_1);
	SetIndexLabel(4, periodName + " S1");
	
	SetIndexBuffer(5, support2);
	SetIndexStyle(5, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_2);
	SetIndexLabel(5, periodName + " S2");
	
	SetIndexBuffer(6, support3);
	SetIndexStyle(6, DRAW_LINE, STYLE_DOT, EMPTY, COLOR_3);
	SetIndexLabel(6, periodName + " S3");
	
  	return(0);
}

int start() {
   int limit;
   int counted_bars=IndicatorCounted();
   
   // Don't display daily pivots on charts with timeframe too long
   if (pivotset == SET_DAILY_EST && Period() >= PERIOD_H4) return(0);
   if (pivotset == SET_WEEKLY    && Period() >= PERIOD_W1) return(0);
   if (pivotset == SET_MONTHLY   && Period() >= PERIOD_W1) return(0);
   
   if(counted_bars>0) counted_bars--;
   limit=Bars-counted_bars;

   datetime timestamp = iTime(NULL, 0, limit-1);
   datetime ReferenceCloseTime = GetTimestampPrevUSClose(timestamp);
   
	for(int i=limit-1; i>=0; i--) {

		// array used to fetch price points
		double 		prices[3];
		
		// vars to hold price points`
		double high, low, close;
		
		// vars to hold calculated values
		double pivot, r1, r2, r3, s1, s2, s3;		
		
		// timestamp of the bar pivots being calculated for
		timestamp = iTime(NULL, 0, i);
		datetime    iCloseTime = GetTimestampPrevUSClose(timestamp);
//		bool        CalcNewDay = iClosetime > ReferenceCloseTime;
		
		// fetch price points based on set
		switch (pivotset) {
			case SET_DAILY_EST:
			   if(iCloseTime > ReferenceCloseTime)
			   {
   				GetDailyESTPoints(prices, timestamp);
					// Calculate the pivots
	     	      close = prices[PERIOD_CLOSE];
		         high  = prices[PERIOD_HIGH];
		         low   = prices[PERIOD_LOW];
		         pivot = (high + low + close) / 3;
		         r1 = 2 * pivot - low;
		         s1 = 2 * pivot - high;
		         r2 = pivot + (r1 - s1);
		         s2 = pivot - (r1 - s1);
		         r3 = high + 2 * (pivot - low);
		         s3 = low - 2 * (high - pivot);
//		         CalcNewDay=False;
               // Update ReferenceCloseTime to current day close so this block won't trigger until tomorrw.
		         ReferenceCloseTime = iCloseTime;
		       }
				break;
			case SET_PREVCLOSE:
				//GetPrevClosePoints(prices, timestamp);
				break;
			case SET_WEEKLY:
				GetWeeklyPoints(prices, timestamp);
					// Calculate the pivots
		       close = prices[PERIOD_CLOSE];
		       high  = prices[PERIOD_HIGH];
		       low   = prices[PERIOD_LOW];
		       pivot = (high + low + close) / 3;
		       r1 = 2 * pivot - low;
		       s1 = 2 * pivot - high;
		       r2 = pivot + (r1 - s1);
		       s2 = pivot - (r1 - s1);
		       r3 = high + 2 * (pivot - low);
		       s3 = low - 2 * (high - pivot);
				break;
			case SET_MONTHLY:
				GetMonthlyPoints(prices, timestamp);
					// Calculate the pivots
		       close = prices[PERIOD_CLOSE];
		       high  = prices[PERIOD_HIGH];
		       low   = prices[PERIOD_LOW];
		       pivot = (high + low + close) / 3;
		       r1 = 2 * pivot - low;
		       s1 = 2 * pivot - high;
		       r2 = pivot + (r1 - s1);
		       s2 = pivot - (r1 - s1);
		       r3 = high + 2 * (pivot - low);
		       s3 = low - 2 * (high - pivot);
				break;
		}
		
	
		// Place values in Buffers
		resist3[i] = r3;
		resist2[i] = r2;
		resist1[i] = r1;
		pivotpoint[i] = pivot;
		support1[i] = s1;
		support2[i] = s2;
		support3[i] = s3;	
	}

	return(0);
}


void GetDailyESTPoints(double& prices[], datetime timestamp) {
	
	double 	high;
	double 	low;
	double 	close;
	datetime origtime = timestamp;

	// set timestamp to previous US market close
	timestamp = GetTimestampPrevUSClose(timestamp);

	// subtract a day, so we can reference the start of the period.
	timestamp -= SECONDS_IN_DAY;
	//Print("Starting with ", TimeToStr(timestamp), " for ", TimeToStr(origtime));
	// Iterate through the 24 periods, grabbing prices for bars that exist.
	for (int i=1;i<=24;i++) {
		int offset = iBarShift(NULL,PERIOD_H1,timestamp,true);
		
		// If bar doesn't exist, it's because it's Sunday or Friday at 5pm Eastern (market is closed)
		if (offset != -1) {
			// grab high and low from the hour bar
			double periodHigh = iHigh(NULL, PERIOD_H1, offset);
			double periodLow = iLow(NULL, PERIOD_H1, offset);
			
			// set the close to the close of this hour bar
			// if it isn't the last bar, it will be overwritten
			close = iClose(NULL, PERIOD_H1, offset);
			
			// if high is still null or lower than period high, replace the value
			if ((high == NULL) || (high<periodHigh)) 
				high = periodHigh;
		
			// if low is still null or higher than period low, replace the value
			if ((low == NULL) || (low>periodLow)) 
				low = periodLow;
		}
		
		// Add another hour to timestamp, for next iteration
		timestamp += SECONDS_IN_HOUR;
	}
	
	//Print("Calculated Prices for ", TimeToStr(origtime), "   High: ", high, "   Low: ", low, "   Close: ", close);
		
	// Place the values back into the passed by ref array
	prices[PERIOD_CLOSE] = close;
	prices[PERIOD_HIGH]  = high;
	prices[PERIOD_LOW]   = low;
	
	return;
}



void GetWeeklyPoints(double& prices[], datetime timestamp) {
	
	int		 offset;
	
	// set timestamp to midnight of specified Day
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// subtract days until it's sunday
	timestamp -= TimeDayOfWeek(timestamp) * SECONDS_IN_DAY;
	
	// get the appropriate offset for the previous week
	offset = iBarShift(NULL,PERIOD_W1,timestamp, true) + 1;
	
	double 	high	= iHigh(NULL, PERIOD_W1, offset);
	double 	low		= iLow(NULL, PERIOD_W1, offset);
	double 	close	= iClose(NULL, PERIOD_W1, offset);

	// Place the values back into the passed by ref array
	prices[PERIOD_CLOSE] = close;
	prices[PERIOD_HIGH]  = high;
	prices[PERIOD_LOW]   = low;
	
	return;
}

void GetMonthlyPoints(double& prices[], datetime timestamp) {
	int		 offset;
	
	// set timestamp to midnight of specified Day
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// subtract days until it's the first of the month
	timestamp -= (TimeDay(timestamp) - 1) * SECONDS_IN_DAY;
	
	// get the appropriate offset for the previous month
	offset = iBarShift(NULL,PERIOD_MN1,timestamp, true) + 1;
	
	double 	high	= iHigh(NULL, PERIOD_MN1, offset);
	double 	low		= iLow(NULL, PERIOD_MN1, offset);
	double 	close	= iClose(NULL, PERIOD_MN1, offset);

	// Place the values back into the passed by ref array
	prices[PERIOD_CLOSE] = close;
	prices[PERIOD_HIGH]  = high;
	prices[PERIOD_LOW]   = low;
	
	return;
}


// Takes a timestamp (in server time) and returns a timestamp (in server time)
// indicating the previous US market close.
// Note:  This still returns 5pm for Fridays!
datetime GetTimestampPrevUSClose(datetime timestamp) {

	// Convert to Eastern time so head doesn't hurt.
	timestamp = GetEasternTime(timestamp);
	
	// If it's not yet past 5pm Eastern, must use previous day by subtracting seconds in day
	if (TimeHour(timestamp) < 17)	
		timestamp -= SECONDS_IN_DAY;
	
	// Is timestamp on Sunday?  Subtract a Day
	if (TimeDayOfWeek(timestamp) == 0)
		timestamp -= SECONDS_IN_DAY;
		
	// Is timestamp on Saturday?  Subtract a Day
	if (TimeDayOfWeek(timestamp) == 6)
		timestamp -= SECONDS_IN_DAY;
		
	// Strip hours, minutes and seconds from timestamp
	timestamp -= TimeHour(timestamp) * SECONDS_IN_HOUR;
	timestamp -= TimeMinute(timestamp) * SECONDS_IN_MINUTE;
	timestamp -= TimeSeconds(timestamp);
	
	// Add 17 hours back in so it's 5pm
	timestamp += 17 * SECONDS_IN_HOUR;
	
	// convert back to server Time
	timestamp = GetServerTimeFromEastern(timestamp);

	return (timestamp);
}


datetime GetEasternTime(datetime timestamp) {

   int   iEasternOffset = GlobalVariableGet("EasternTimeOffset");
	return ((timestamp) + (iEasternOffset * SECONDS_IN_HOUR));

}

datetime GetServerTimeFromEastern(datetime timestamp) {

   int   iEasternOffset = GlobalVariableGet("EasternTimeOffset");
	return ((timestamp) - (iEasternOffset * SECONDS_IN_HOUR));

}

