//------------------------------------------------------------------
#property copyright "mladen"
#property link      "www,forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_color1  clrLimeGreen  
#property indicator_color2  clrDimGray
#property indicator_color3  clrPaleVioletRed
#property indicator_style1  STYLE_DOT
#property indicator_style2  STYLE_DOT
#property indicator_style3  STYLE_DOT
#property strict

//
//
//

enum enTimeFrames
{
         tf_cu  = 0,                                            // Current time frame
         tf_m1  = PERIOD_M1,                                    // 1 minute
         tf_m5  = PERIOD_M5,                                    // 5 minutes
         tf_m15 = PERIOD_M15,                                   // 15 minutes
         tf_m30 = PERIOD_M30,                                   // 30 minutes
         tf_h1  = PERIOD_H1,                                    // 1 hour
         tf_h4  = PERIOD_H4,                                    // 4 hours
         tf_d1  = PERIOD_D1,                                    // Daily
         tf_w1  = PERIOD_W1,                                    // Weekly
         tf_mn1 = PERIOD_MN1,                                   // Monthly
         tf_n1  = -1,                                           // First higher time frame
         tf_n2  = -2,                                           // Second higher time frame
         tf_n3  = -3                                            // Third higher time frame
      };
input enTimeFrames        inpTimeFrame        = tf_cu;           // Time frame to use 
input int                 CCIPeriod           = 14;
input ENUM_APPLIED_PRICE  CCIPrice            = PRICE_TYPICAL;
input int                 AvgPeriod           = 5;
input double              levelOs             = 10;
input double              levelOb             = 90;
input int                 MinMaxPeriod        = 50;
input string              __arr__01           = "";                      //.Arrow settings
input bool                ShowArrows          = false;
input string              arrowsIdentifier    = "cci Arrows1";
input double              arrowsUpperGap      = 0.5;
input double              arrowsLowerGap      = 0.5;
input color               arrowsUpColor       = clrLimeGreen;
input color               arrowsDnColor       = clrRed;
input int                 arrowsUpCode        = 241;
input int                 arrowsDnCode        = 242;
input string              __dis__01           = "";                      //. Display settings
input int                 inpLinesWidth       = 3;                       // Cci lines width
input color               inpColorUp          = clrLimeGreen;            // Up color for lines
input color               inpColorDn          = clrRed;                  // Down color for lines
input color               inpColorNu          = clrDarkGray;             // Neutral color for lines
input color               inpClrFillNtz       = clrGainsboro;            // Color for no trade zone fill                              
input bool                Interpolate         = true;

double levu[],levm[],levd[],val[],trend[],valUa[],valUb[],valDa[],valDb[],count[];
struct sGlobalStruct
{
   string indiFileName;
   int    indiTimeFrame;
};
sGlobalStruct glo;
#define _mtfCall(_buff,_ind) iCustom(_Symbol,glo.indiTimeFrame,glo.indiFileName,tf_cu,CCIPeriod,CCIPrice,AvgPeriod,levelOs,levelOb,MinMaxPeriod,"",ShowArrows,arrowsIdentifier,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,"",inpLinesWidth,inpColorUp,inpColorDn,inpColorNu,_buff,_ind)

//------------------------------------------------------------------
//
//------------------------------------------------------------------

int OnInit()
{
   IndicatorBuffers(10);
   SetIndexBuffer(0, levu, INDICATOR_DATA); SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(1, levm, INDICATOR_DATA); SetIndexStyle(1,DRAW_LINE); 
   SetIndexBuffer(2, levd, INDICATOR_DATA); SetIndexStyle(2,DRAW_LINE);
   SetIndexBuffer(3, val,  INDICATOR_DATA); SetIndexStyle(3,DRAW_LINE,EMPTY,inpLinesWidth,inpColorNu);
   SetIndexBuffer(4, valUa,INDICATOR_DATA); SetIndexStyle(4,DRAW_LINE,EMPTY,inpLinesWidth,inpColorUp);
   SetIndexBuffer(5, valUb,INDICATOR_DATA); SetIndexStyle(5,DRAW_LINE,EMPTY,inpLinesWidth,inpColorUp);
   SetIndexBuffer(6, valDa,INDICATOR_DATA); SetIndexStyle(6,DRAW_LINE,EMPTY,inpLinesWidth,inpColorDn);
   SetIndexBuffer(7, valDb,INDICATOR_DATA); SetIndexStyle(7,DRAW_LINE,EMPTY,inpLinesWidth,inpColorDn);
   SetIndexBuffer(8, trend,INDICATOR_CALCULATIONS);
   SetIndexBuffer(9, count,INDICATOR_CALCULATIONS);
      
   iSmoother.init(AvgPeriod);
   glo.indiFileName  = WindowExpertName();
   glo.indiTimeFrame = (enTimeFrames)timeFrameValue(inpTimeFrame);

   IndicatorSetString(INDICATOR_SHORTNAME,timeFrameToString(glo.indiTimeFrame)+" Smoother Cci ("+(string)CCIPeriod+")");
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)  {  ObjectsDeleteAll(0,arrowsIdentifier+":"); return; }

//
//
//

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 i,r,limit=fmin(rates_total-prev_calculated+1,rates_total-1); count[0] = limit;
      if (glo.indiTimeFrame!=_Period)
      {
         limit = (int)fmax(limit,fmin(rates_total-1,_mtfCall(9,0)*glo.indiTimeFrame/_Period));
         if (trend[limit]== 1) iCleanPoint(limit,rates_total,valUa,valUb);
         if (trend[limit]==-1) iCleanPoint(limit,rates_total,valDa,valDb);
         for(i=limit; i>=0 && !_StopFlag; i--)
         {
            int y = iBarShift(_Symbol,glo.indiTimeFrame,time[i]);
               levu[i]  = _mtfCall(0,y);
               levm[i]  = _mtfCall(1,y);
               levd[i]  = _mtfCall(2,y);
               val[i]   = _mtfCall(3,y);
               trend[i] = _mtfCall(8,y);
               
               //
               //
               //
                     
               if (!Interpolate || (i>0 && y==iBarShift(_Symbol,glo.indiTimeFrame,time[i-1]))) continue;
                 #define _interpolate(buff) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
                 int n,k; datetime btime = iTime(_Symbol,glo.indiTimeFrame,y);
                    for(n = 1; (i+n)<rates_total && time[i+n] >= btime; n++) continue;	
                    for(k = 1; k<n && (i+n)<rates_total && (i+k)<rates_total; k++) 
                    {
                       _interpolate(levu); 
                       _interpolate(levm);
                       _interpolate(levd);
                       _interpolate(val);
                    }                 
         }
         for (i=limit;i>=0 && !_StopFlag; i--)
         {
            if(trend[i]==-1) iPlotPoint(i,rates_total,valDa,valDb,val); else valDa[i] = valDb[i] = EMPTY_VALUE;
            if(trend[i]== 1) iPlotPoint(i,rates_total,valUa,valUb,val); else valUa[i] = valUb[i] = EMPTY_VALUE;
         }
   return(rates_total);
   }  
  
   //
   //
   //
   
   struct sWorkStruct
   {
     double prc;
   };
   static sWorkStruct wrk[];
   static int         wrkSize = -1;
                  if (wrkSize<rates_total) wrkSize = ArrayResize(wrk,rates_total+500);
   
   //
   //
   //
   
   if (trend[limit]== 1) iCleanPoint(limit,rates_total,valUa,valUb);
   if (trend[limit]==-1) iCleanPoint(limit,rates_total,valDa,valDb);
   for(i=limit, r=rates_total-limit-1; i>=0; i--,r++)
   {
      wrk[r].prc = iSmoother.OnCalculate(iMA(_Symbol,_Period,1,0,MODE_SMA,CCIPrice,i),r,rates_total);
      val[i]     = iCci(wrk[r].prc,CCIPeriod,i,rates_total);
      double hi  = val[ArrayMaximum(val,MinMaxPeriod,i)];
      double lo  = val[ArrayMinimum(val,MinMaxPeriod,i)];
      double rn  = hi-lo;
         levu[i] = lo+rn*levelOb/100.0;
         levd[i] = lo+rn*levelOs/100.0;
         levm[i] = (levu[i]+levd[i])/2.0;
         trend[i] = (r>0) ? (val[i]>levu[i]) ? 1 : (val[i]<levd[i]) ? -1 : (val[i]<levu[i] && val[i]>levd[i]) ? 0 : trend[i+1] : 0;  
         if(trend[i]==-1) iPlotPoint(i,rates_total,valDa,valDb,val); else valDa[i] = valDb[i] = EMPTY_VALUE;
         if(trend[i]== 1) iPlotPoint(i,rates_total,valUa,valUb,val); else valUa[i] = valUb[i] = EMPTY_VALUE;
         
         //
         //
         //
     
         if (ShowArrows)
         {
            string lookFor = arrowsIdentifier+":"+(string)time[i]; if (ObjectFind(0,lookFor)==0) ObjectDelete(0,lookFor);             
            if (r>0 && trend[i]!= trend[i+1])
            {
               if (trend[i+1] ==  1 && trend[i] != 1) drawArrow(i,arrowsDnColor,arrowsDnCode,true);
               if (trend[i+1] == -1 && trend[i] !=-1) drawArrow(i,arrowsUpColor,arrowsUpCode,false);
            }
         }
   }
return(rates_total);
}      

//
//
//

void drawArrow(int i,color theColor,int theCode,bool up)
{
   string name = arrowsIdentifier+":"+(string)Time[i];
   double gap  = 3.0*iATR(NULL,0,20,i)/4.0;   
   
      //
      //
      //
      
      ObjectCreate(name,OBJ_ARROW,0,Time[i],0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i]+ arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i] - arrowsUpperGap * gap);
}

//
//
//

void iCleanPoint(int i,int bars,double& first[],double& second[])
{
   if (i>=bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i]  != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
           first[i+1] = EMPTY_VALUE;
}

void iPlotPoint(int i,int bars,double& first[],double& second[],double& from[])
{
   if (i>=bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE) 
            { first[i]  = from[i]; first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] = from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                          second[i] = EMPTY_VALUE; }
}

//------------------------------------------------------------------
//   Mladen cci function
//------------------------------------------------------------------

#define _cciInstances 1
double workCci[][_cciInstances];
#define _price  0
double iCci(double price, int period, int r, int bars, int instanceNo=0)
{
   if (ArrayRange(workCci,0)!= bars) ArrayResize(workCci,bars); r = bars-r-1;
   
   //
   //
   //
   
   workCci[r][instanceNo+_price]=price;
         double avg  = 0; for(int k=0; k<period && (r-k)>=0; k++) avg +=      workCci[r-k][instanceNo];      avg /= period;
         double dev  = 0; for(int k=0; k<period && (r-k)>=0; k++) dev += fabs(workCci[r-k][instanceNo]-avg); dev /= period;
         double tcci = (dev!=0) ? (price-avg)/(0.015*dev) : 0;
   return(tcci);
}

//------------------------------------------------------------------
// Custom functions
//------------------------------------------------------------------

class CSmoother
{
   private :
      double m_period;
      double m_alpha;
      double m_beta;
      int    m_arraySize;
      struct CSmootherStruct
      {
         double l0;
         double l1;
         double l2;
         double l3;
         double l4;
      };
      CSmootherStruct m_array[];
   public :
      CSmoother() : m_arraySize(-1), m_alpha(1), m_beta(1) {}
     ~CSmoother() {}
  
      //
      //
      //
      
      bool init(double period)
      {
         m_period = (period>1) ? period : 1;
         m_beta   = 0.45 * (m_period-1.0)/(0.45 * (m_period-1)+2.0);
         m_alpha  = m_beta*m_beta*m_beta;
            return(true);
      }
      
      double OnCalculate(double value, int i, int bars)
      {
         if (m_arraySize<bars) { m_arraySize = ArrayResize(m_array,bars+500); if (m_arraySize<bars) return(0); }

         //
         //
         //
                    
         if (i>0)
         {
               m_array[i].l0 = (1.0-m_alpha)*value + m_alpha*m_array[i-1].l0;
               m_array[i].l1 = (value-m_array[i].l0)*(1-m_beta) + m_beta*m_array[i-1].l1;
               m_array[i].l2 =  m_array[i].l0 + m_array[i].l1;
               m_array[i].l3 = (m_array[i].l2 - m_array[i-1].l4)*(1.0-m_alpha)*(1.0-m_alpha) + m_alpha*m_alpha*m_array[i-1].l3;
               m_array[i].l4 =  m_array[i-1].l4 + m_array[i].l3;
         }                    
         else { m_array[i].l0 = m_array[i].l1 = m_array[i].l4 = value; m_array[i].l2 = m_array[i].l3 = 0; }
         return(m_array[i].l4);
      }
};
CSmoother iSmoother;

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}
int timeFrameValue(int _tf)
{
   int add  = (_tf>=0) ? 0 : fabs(_tf);
   if (add != 0) _tf = _Period;
   int size = ArraySize(iTfTable); 
      int i =0; for (;i<size; i++) if (iTfTable[i]==_tf) break;
                                   if (i==size) return(_Period);
                                                return(iTfTable[(int)fmin(i+add,size-1)]);
}
