//+------------------------------------------------------------------+
//|                                                          CCI.mq4 |
//|                   Copyright 2005-204, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
#property description "Commodity Channel Index with permanent synchronization"
#property strict

#include <MovingAverages.mqh>

#property indicator_separate_window
#property indicator_buffers    1
#property indicator_level1    -100.0
#property indicator_level2     100.0
#property indicator_levelcolor clrSilver
#property indicator_levelstyle STYLE_DOT

// MODIFICATION: The fixed difference is now a hardcoded constant.
#define SYNC_DIFFERENCE 238

//--- input parameters
input string             CCI_Settings= "--- CCI Settings ---";
input int                InpCCIPeriod=2200;         // CCI Period
input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_CLOSE; // Applied Price

input string             Display_Settings= "--- Display Settings ---";
input color              InpCCIColor=Crimson;   // CCI Line Color
input int                InpCCIWidth=2;               // CCI Line Width

// MODIFICATION: The following inputs have been removed from the user interface:
// - InpBarsToCalculate
// - InpSyncPeriodAndBars
// - InpSyncDifference

//--- buffers
double ExtCCIBuffer[];
double ExtPriceBuffer[];
double ExtMovBuffer[];


//--- Helper function to get a string for the indicator name
string PriceEnumToString(ENUM_APPLIED_PRICE price_type)
  {
   switch(price_type)
     {
      case PRICE_CLOSE:      return "Close";
      case PRICE_OPEN:       return "Open";
      case PRICE_HIGH:       return "High";
      case PRICE_LOW:        return "Low";
      case PRICE_TYPICAL:    return "Typical";
      case PRICE_WEIGHTED:   return "Weighted";
      case PRICE_MEDIAN:     return "Median";
     }
   return "Unknown";
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   string short_name;
   IndicatorBuffers(3);
   SetIndexBuffer(1,ExtPriceBuffer);
   SetIndexBuffer(2,ExtMovBuffer);
   SetIndexBuffer(0,ExtCCIBuffer);
   SetIndexStyle(0, DRAW_LINE, EMPTY, InpCCIWidth, InpCCIColor);

   if(InpCCIPeriod<=1)
     {
      Print("Wrong input parameter CCI Period=",InpCCIPeriod);
      return(INIT_FAILED);
     }
   SetIndexDrawBegin(0,InpCCIPeriod);
   
   string price_str = PriceEnumToString(InpAppliedPrice);
   short_name = StringFormat("CCI(%d, %s)", InpCCIPeriod, price_str);
   IndicatorShortName(short_name);
   SetIndexLabel(0,short_name);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Commodity Channel Index                                          |
//+------------------------------------------------------------------+
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,k,pos;
   double dSum,dMul;
//---
   if(rates_total<=InpCCIPeriod) return(0);

// MODIFICATION: Synchronization is now permanent.
   // The lookback period is always calculated based on the CCI Period and the fixed difference.
   int actual_bars_to_calculate = InpCCIPeriod + SYNC_DIFFERENCE;

//--- "bars back" logic (now uses the permanently synchronized value)
   int first_bar=0;
   if(actual_bars_to_calculate > 0 && rates_total > actual_bars_to_calculate)
     {
      first_bar=rates_total - actual_bars_to_calculate;
     }
   SetIndexDrawBegin(0, first_bar + InpCCIPeriod -1);

//--- CCI Calculation Loops
   ArraySetAsSeries(ExtCCIBuffer,false); ArraySetAsSeries(ExtPriceBuffer,false); ArraySetAsSeries(ExtMovBuffer,false);
   ArraySetAsSeries(high,false); ArraySetAsSeries(low,false); ArraySetAsSeries(close,false);

   pos=prev_calculated-1;
   if(pos<InpCCIPeriod) pos=InpCCIPeriod;
   if(pos < first_bar) pos = first_bar;

   for(i=pos; i<rates_total; i++)
     {
      switch(InpAppliedPrice) {
         case PRICE_CLOSE: ExtPriceBuffer[i] = close[i]; break;
         case PRICE_OPEN: ExtPriceBuffer[i] = open[i]; break;
         case PRICE_HIGH: ExtPriceBuffer[i] = high[i]; break;
         case PRICE_LOW: ExtPriceBuffer[i] = low[i]; break;
         case PRICE_WEIGHTED: ExtPriceBuffer[i] = (high[i] + low[i] + 2.0*close[i]) / 4.0; break;
         case PRICE_MEDIAN: ExtPriceBuffer[i] = (high[i] + low[i]) / 2.0; break;
         default: ExtPriceBuffer[i] = (high[i] + low[i] + close[i]) / 3.0; break;
      }
      ExtMovBuffer[i]=SimpleMA(i,InpCCIPeriod,ExtPriceBuffer);
     }
     
   dMul=0.015/InpCCIPeriod;
   pos=InpCCIPeriod-1;
   if(pos<prev_calculated-1) pos=prev_calculated-2;
   if(pos < first_bar) pos = first_bar;

   i=pos;
   while(i<rates_total)
     {
      if(i < InpCCIPeriod -1) { i++; continue; }
      dSum=0.0;
      k=i+1-InpCCIPeriod;
      while(k<=i) { dSum+=MathAbs(ExtPriceBuffer[k]-ExtMovBuffer[i]); k++; }
      dSum*=dMul;
      if(dSum==0.0) ExtCCIBuffer[i]=0.0; else ExtCCIBuffer[i]=(ExtPriceBuffer[i]-ExtMovBuffer[i])/dSum;
      i++;
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+