//+------------------------------------------------------------------+
//| Extensive update and re-write of:                                |
//|                                                   JJN-Nugget.mq4 |
//|                                      Copyright © 2012, JJ Newark |
//|                                            http:/jjnewark.atw.hu |
//|                                             jjnewark@freemail.hu |
//| http://www.forexfactory.com/showthread.php?p=9247641#post9247641 |
//+------------------------------------------------------------------+
#property copyright "LukeB"
#property link      "http://www.forexfactory.com/lukeb"
#property indicator_chart_window
//
#property indicator_buffers 3
#property indicator_color1 clrGold
#property indicator_color2 clrPurple
#property indicator_color3 clrOrchid
#property indicator_width1 1
#property indicator_width2 1
#property indicator_width3 1
#property indicator_style1 STYLE_DOT
#property indicator_style2 STYLE_SOLID
#property indicator_style3 STYLE_SOLID
//
#property strict
//
#include <stdlib.mqh>
//---- indicator settings
enum ON_OFF {ON, OFF};
enum LONG_OR_SHORT {FLAT, LONG, SHORT};
const int NOSHIFT     = 0;
const int CHARTWINDOW = 0;
const int FIBOCOUNT   = 8;
//---- indicator parameters
int uniqueNo = 723; // number to make this instance of the indicator unique
extern int        DisplayDecimals             = -1;  //  Use -1 to dipaplay with "Digits" Precision
extern int        AnchorX                     = 10;
extern int        AnchorY                     = 20;
extern color      LongClr                     = clrSkyBlue;
extern color      ShortClr                    = clrCrimson;
extern ON_OFF     Alerts                      = ON;
//---- indicator buffers
double     indi_goldLine[];
double     indi_sums[];
double     indi_2Bar[];
//
class LabelObject
 {
   private:
      static int xAnchorPos, yAnchorPos;
      string objName, objFont, objDispText;
      color  objClr;
      int    textSize, xOffset, yOffset;     
   protected:
      void DisplayObject()
       {
         long chartID = ChartID();
         if( ObjectFind(chartID,objName) < 0 )  // Object not found, create it
          {
            ObjectCreate(chartID,objName,OBJ_LABEL,CHARTWINDOW,0,0);
            ObjectSet(objName,OBJPROP_CORNER,ANCHOR_LEFT_UPPER);
            ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN,    true);
            ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE,false);
            ObjectSetInteger(chartID, objName, OBJPROP_SELECTED,  false);
          }
         // object Exists, update it
         ObjectSet(objName,OBJPROP_XDISTANCE,xAnchorPos+xOffset);
         ObjectSet(objName,OBJPROP_YDISTANCE,yAnchorPos+yOffset);
         ObjectSetText(objName,objDispText,textSize,objFont,objClr);
       }
   public:
      void LabelObject(string name, string font, int size, int xPos, int yPos, color initColor)
       {
         this.objName = name; this.objFont = font; this.textSize = size; 
         this.xOffset = xPos; this.yOffset = yPos; this.objClr   = initColor;
       }
      void ~LabelObject()
       {
         ObjectDelete(objName);
       }
      void setTextClr( color textColor)
       {
         this.objClr = textColor;
       }
      void setText(string dispText)
       {
         this.objDispText = dispText;
         DisplayObject();
       }
      void ShowTheObject(void)
       {
         DisplayObject();
       }
      void ShowValues(void)
       {
         Print( objName+", XAnch: ",IntegerToString(xAnchorPos),", YAnch: ",IntegerToString(yAnchorPos),", xPos: ",
                IntegerToString(xOffset),", yPos: ",IntegerToString(yOffset),", Color: ",ColorToString(objClr,true) );
       }
 };
static int LabelObject::xAnchorPos = AnchorX;
static int LabelObject::yAnchorPos = AnchorY;
//==== label Object Parameters ============
enum   label_OBJ_INDEX     { INDI_NAME,   LINE_ZERO,   LINE_ONE, SIGNAL_DIRECTION,   SIGNAL_VALUE,   endLblObjIdx};
LabelObject* ary_lblObjs[endLblObjIdx];
string lblObjNames[]     = { "IndName",     "Line0",    "Line1",      "Direction",        "Level"};
color  lblObjInitClr[]   = {clrSkyBlue,  clrSkyBlue, clrSkyBlue,       clrSkyBlue, clrYellowGreen};
string lblObjFont[]      = { "Calibri",     "Arial",    "Arial",        "Calibri",      "Calibri"};
int    lblObjSize[]      = {         8,           8,          8,               26,             10};
int    lblObjXoffset[]   = {         0,           0,          0,                5,              0};
int    lblObjYoffset[]   = {         0,           8,         10,               12,             48};
//
class PriceObjectMgr   // A singleton - there can be only one object instantiated by this class
 {
   private:
      static PriceObjectMgr *m_PriceObjectMgr;           // Static pointer to the only instance of the price object manager
      long objCount;
      datetime lastMarkTime;
      color longColor, shortColor;
      string foundationName;
      string GetObjName(void)
       {
         objCount++;
         string priceName = foundationName+IntegerToString(objCount);
         return(priceName);
       }
      void PriceObjectMgr(void) // Private constructor, prevents multiple objets being created, part of Singleton pattern
       {
         objCount= 0; lastMarkTime = 0;
       }
      PriceObjectMgr(PriceObjectMgr const&){};          // Private Copy Constructor prevents copy of the object as part of Singleton pattern
      PriceObjectMgr operator=(PriceObjectMgr const&);  // Private Assignment Operator, prevents assignment as part of Singleton pattern
   protected:
      void DisplayPrice(LONG_OR_SHORT direction, datetime time, double price)
       {
         if(time > lastMarkTime)  // else, mark is already made, don't make another one.
          {
            lastMarkTime = time;
            static long chartID = ChartID();
            string objName = GetObjName();
            ObjectCreate(chartID,objName,OBJ_ARROW_RIGHT_PRICE,CHARTWINDOW,time,price);
            ObjectSetInteger(chartID,objName,OBJPROP_COLOR,direction==LONG?longColor:shortColor);
            ObjectSetInteger(chartID,objName,OBJPROP_STYLE,STYLE_SOLID);
            ObjectSetInteger(chartID,objName,OBJPROP_WIDTH,2);
            ObjectSetInteger(chartID,objName,OBJPROP_BACK,true);
            ObjectSetInteger(chartID,objName,OBJPROP_SELECTABLE,false);
            ObjectSetInteger(chartID,objName,OBJPROP_SELECTED,false);
            ObjectSetInteger(chartID,objName,OBJPROP_HIDDEN,true);
          }
       }
   public:
      void SetBaseValues(color buyClr, color sellClr, string baseName)
       {
         longColor = buyClr; shortColor = sellClr; foundationName = baseName;
       }
      static PriceObjectMgr *GetInstance(void) // A Static Function that will provide a pointer to 'this'
       {
         if(!m_PriceObjectMgr) // Does the private m_yoLinux pointer point or not?
          {
            m_PriceObjectMgr=new PriceObjectMgr();     // Create Class Object on Demand, put the pointer in the pivate, static pointer
          }
         return(m_PriceObjectMgr);               // Return a pointer to the one and only object.
       }
      void ~PriceObjectMgr(void)
       {
         for( long i = 1; i <= objCount; i++ )
          {
            ObjectDelete(foundationName+IntegerToString(i));
          }
       }
      void MakeNewPriceObj(LONG_OR_SHORT direction, datetime processBarTime, double price1 )
       {
         DisplayPrice(direction, processBarTime, price1);
       }
 };
PriceObjectMgr* PriceObjectMgr::m_PriceObjectMgr=NULL;  // Initialize Global Pointer;
//
int workingPrecision;
LONG_OR_SHORT LONGORSHORT;
const string arrowName = "MACross_ZigZag"+IntegerToString(uniqueNo);
//
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
 {
   int initStatus = INIT_SUCCEEDED;
   EventSetTimer(1); // create timer events / set frequency of timer events
   workingPrecision = DisplayDecimals;
   if ( (workingPrecision < 0) || (workingPrecision > (Digits+1)) )
    {
      workingPrecision = Digits;
    }
   //---- drawing settings
   IndicatorBuffers(3);
   //
   SetIndexBuffer    (0, indi_goldLine);
   SetIndexStyle     (0, DRAW_SECTION);
   SetIndexLabel     (0, lblObjNames[INDI_NAME]);
   SetIndexEmptyValue(0, EMPTY_VALUE);
   //
   SetIndexBuffer    (1, indi_sums);
   SetIndexStyle     (1, DRAW_LINE);
   SetIndexLabel     (1, "SLOW_MA");
   SetIndexEmptyValue(1, EMPTY_VALUE);
   //
   SetIndexBuffer    (2, indi_2Bar);
   SetIndexStyle     (2, DRAW_LINE);
   SetIndexLabel     (2, "2_BAR_MA");
   SetIndexEmptyValue(2, EMPTY_VALUE);
   //--- Make the display objets
   for(label_OBJ_INDEX i = 0; i < endLblObjIdx; i++)
    {
      ary_lblObjs[i] = new LabelObject( lblObjNames[i], lblObjFont[i], lblObjSize[i], lblObjXoffset[i], lblObjYoffset[i], lblObjInitClr[i] );
    }
   // INDI_NAME,   LINE_ZERO,   LINE_ONE, SIGNAL_DIRECTION,   SIGNAL_VALUE
   ary_lblObjs[INDI_NAME].setText(arrowName);
   ary_lblObjs[LINE_ZERO].setText("-----------------------");
   ary_lblObjs[LINE_ONE].setText ("-----------------------");
   ary_lblObjs[SIGNAL_DIRECTION].setText("NONE");
   //
   PriceObjectMgr::GetInstance().SetBaseValues(clrSkyBlue, clrRed, arrowName);  // makes the object and sets the base values
   //----
   IndicatorShortName( arrowName );
   //---- initialization done
   return(initStatus);
 } // END int OnInit()
//+------------------------------------------------------------------+
//| Custor indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
 {
   EventKillTimer();                    // Clean up on Exit
   for(label_OBJ_INDEX i = 0; i < endLblObjIdx; i++)
    {  // (string objName, string objFont, int size, int xOffset, int yOffset)
      if( ary_lblObjs[i] != NULL )
       {
         delete ary_lblObjs[i];
         ary_lblObjs[i] = NULL;
       }
    }
   delete PriceObjectMgr::GetInstance();
   Comment("");
 }
//+------------------------------------------------------------------+
//|  Timer Function                                                  |
//+------------------------------------------------------------------+
void OnTimer()
 {
   //--- Refresh the Object Display
   for(label_OBJ_INDEX i = 0; i < endLblObjIdx; i++)
    {
      ary_lblObjs[i].ShowTheObject();
    }
 }
//+------------------------------------------------------------------+
//|   On Chart Event                                                 |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,  const long &lparam, const double &dparam,  const string &sparam)
 {

 }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[],
                const double &low[], const double &close[], const long& tick_volume[], const long& volume[], const int& spread[] )
 {
   int firstCalcBar = (rates_total - prev_calculated);
   firstCalcBar = (firstCalcBar>=rates_total)?rates_total-2:firstCalcBar;
   //----
   CalculateLongMA(firstCalcBar);
   //
   SetMainIndicator(firstCalcBar);
   //---- done
   return(rates_total);
 } // ------ end OnCalculate ----------
//
void CalculateLongMA( const int& firstCalcBar )
 {
   const static int fibos[]={5,8,13,21,34,55,89,144};  // Fibbonocci's
   static bool printed = false;
   double valSumTemp=0;
   for(int processBar=firstCalcBar; processBar>=0; processBar--)
    {
      for(int j=0; j<FIBOCOUNT; j++)
       {  // double  iMA(symbol, timeframe, ma_period, ma_shift, ma_method, applied_price, shift);
         valSumTemp += iMA(NULL,PERIOD_CURRENT,fibos[j],NOSHIFT,MODE_EMA,PRICE_CLOSE,processBar);  // Waits the average favorably towards the most recent, as does MODE_LWMA and MODE_EMA 
       }
      indi_sums[processBar] = valSumTemp/FIBOCOUNT;   // Waits the average favorably towards the most recent, as does MODE_LWMA and MODE_EMA, which would be much faster to use.
      valSumTemp = 0;
    }
 }
//
void SetMainIndicator(const int& firstCalcBar)
 {
   static int shortCount = 0; static int longCount = 0;
   LONGORSHORT = FLAT;
   for(int processBar=firstCalcBar; processBar>=0; processBar--)  //Will not process Bar zero; only runs when firstCalcBar >0.
    {
      double oneTwoBarAvrg = (High[processBar]+Low[processBar])/2;
      double twoTwoBarAvrg = (High[processBar+1]+Low[processBar+1])/2;
      indi_2Bar[processBar] = oneTwoBarAvrg;
      //
      if( (oneTwoBarAvrg > indi_sums[processBar])
         && (twoTwoBarAvrg < indi_sums[processBar+1]) ) 
       { // Rising (long)Crossover, The current sum is greater than the last two bar average and the previous bar sum was less than the previous two bar average
         indi_goldLine[processBar]=High[processBar];      // set a rising indicator value.
         LONGORSHORT = LONG;
         longCount++;
         PriceObjectMgr::GetInstance().MakeNewPriceObj( LONG, Time[processBar], processBar==0?Ask:Close[processBar]+(Ask-Bid));
       }
      else if( (oneTwoBarAvrg < indi_sums[processBar])
              && (twoTwoBarAvrg > indi_sums[processBar+1]) ) 
       { // Falling (Short)Crossover, The current sum is less than the last two bar average and the previous bar sum was greater than the previous two bar average
         indi_goldLine[processBar]=Low[processBar];       // set a falling indicator value.
         LONGORSHORT = SHORT;
         shortCount++;
         PriceObjectMgr::GetInstance().MakeNewPriceObj( SHORT, Time[processBar], Close[processBar] );
       }
      else
       {
         indi_goldLine[processBar]=EMPTY_VALUE;
         LONGORSHORT = FLAT;  // No cross occured.
       }
      if(LONGORSHORT != FLAT)
       {
         ary_lblObjs[SIGNAL_DIRECTION].setTextClr(LONGORSHORT==LONG?LongClr:ShortClr);
         ary_lblObjs[SIGNAL_DIRECTION].setText(LONGORSHORT==LONG?"BUY":"SELL");
         string sigString = StringConcatenate(LONGORSHORT==LONG?"above ":"under ", DoubleToStr(indi_goldLine[processBar],workingPrecision));
         ary_lblObjs[SIGNAL_VALUE].setText(sigString);
         static datetime lastPlayTime = 0;
         if( lastPlayTime < Time[processBar] )
          {
            lastPlayTime = Time[processBar];
            if( (Alerts==ON) && (processBar<3) )
             {
               PlaySound("alert.wav");
             }
          }
       }
    }
 }
//+------------------------------------------------------------------+