//+------------------------------------------------------------------+
//|                                              Ichimoku_Width.mq5    |
//|                                     Copyright 2026, FX Blog Lab.   |
//|                                     https://fx-omoshiro-lab.com/   |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link      "https://fx-omoshiro-lab.com/"
#property version   "1.02"
#property description "一目均衡表の「横幅（時間）」を可視化するインジケータ"
#property description "基準線や雲がフラットな期間（もみ合い・トレンド調整期間）を計測"

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2

//--- enums
enum ENUM_DISPLAY_MODE
{
   MODE_BOTH  = 0, // Both (Single Window)
   MODE_TK    = 1, // TK Width Only
   MODE_CLOUD = 2  // Cloud Width Only
};

//--- inputs
input ENUM_DISPLAY_MODE DisplayMode = MODE_BOTH; // Display Mode
input int      TenkanSen      = 9;
input int      KijunSen       = 26;
input int      SenkouSpanB    = 52;
input int      SearchMax      = 100; // Search Max Bars

//--- plot TK_Width
#property indicator_label1  "TK Width"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  clrMediumSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- plot Cloud_Width
#property indicator_label2  "Cloud Width"
#property indicator_type2   DRAW_HISTOGRAM
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- buffers
double         TKWidthBuffer[];
double         CloudWidthBuffer[];

//--- handle
int            ichiHandle;
//--- temp buffers for caching
double         TenkanBuffer[];
double         KijunBuffer[];
double         SpanABuffer[];
double         SpanBBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, TKWidthBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, CloudWidthBuffer, INDICATOR_DATA);
   
   // プロット初期値（空）
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);

   // 時系列配列として扱う (Index 0 = 最新)
   // これにより CopyBuffer との連携がスムーズになり、
   // i - j (過去) ではなく i + j (過去) という直感的な操作が可能になる
   ArraySetAsSeries(TKWidthBuffer, true);
   ArraySetAsSeries(CloudWidthBuffer, true);
   ArraySetAsSeries(TenkanBuffer, true);
   ArraySetAsSeries(KijunBuffer, true);
   ArraySetAsSeries(SpanABuffer, true);
   ArraySetAsSeries(SpanBBuffer, true);

   // モードによる表示制御
   if(DisplayMode == MODE_TK)
   {
      PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE); // Hide Cloud
      IndicatorSetString(INDICATOR_SHORTNAME, "Ichimoku Width (TK)");
   }
   else if(DisplayMode == MODE_CLOUD)
   {
      PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE); // Hide TK
      IndicatorSetString(INDICATOR_SHORTNAME, "Ichimoku Width (Cloud)");
   }
   else
   {
      IndicatorSetString(INDICATOR_SHORTNAME, "Ichimoku Width (Both)");
   }

   // 一目均衡表ハンドル取得
   ichiHandle = iIchimoku(_Symbol, _Period, TenkanSen, KijunSen, SenkouSpanB);
   if(ichiHandle == INVALID_HANDLE)
   {
      Print("Failed to create Ichimoku handle");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 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[])
{
   if(rates_total < KijunSen + SearchMax) return(0);

   // 計算範囲の決定 (時系列配列使用: 0が最新、rates_total-1が最古)
   int limit;
   if(prev_calculated == 0)
   {
      // 初回計算: 全期間（ただしデータ不足分は除く）
      limit = rates_total - 1; 
   }
   else
   {
      // 差分計算: 新しいバーの分だけ再計算
      limit = rates_total - prev_calculated;
   }

   // 必要なデータ数のみコピーする（最適化）
   // limit (計算対象) + SearchMax (過去参照分) + 余裕(10)
   int copy_count = limit + SearchMax + 10;
   if(copy_count > rates_total) copy_count = rates_total;

   // データコピー (0 = 最新から copy_count 個)
   if(CopyBuffer(ichiHandle, 0, 0, copy_count, TenkanBuffer) <= 0) return(0);
   if(CopyBuffer(ichiHandle, 1, 0, copy_count, KijunBuffer) <= 0) return(0);
   if(CopyBuffer(ichiHandle, 2, 0, copy_count, SpanABuffer) <= 0) return(0);
   if(CopyBuffer(ichiHandle, 3, 0, copy_count, SpanBBuffer) <= 0) return(0);

   // メインループ (limit から 0(最新) に向かって計算)
   // ArraySetAsSeries(true) なので、i=0 が最新バー
   for(int i = limit; i >= 0; i--)
   {
      // 計算に必要な過去データが足りない場合はスキップ
      if (i + SearchMax >= rates_total) continue;
      
      // --- 1. TK Width (Tenkan vs Current Kijun Level) ---
      if(DisplayMode == MODE_BOTH || DisplayMode == MODE_TK)
      {
         double currentKijun = KijunBuffer[i];
         int tkWidth = 0;

         // 過去に遡って、TenkanがcurrentKijunをクロスした地点を探す
         // i が現在、i + j が過去
         for(int j = 1; j <= SearchMax; j++)
         {
            int idx = i + j;       // 過去のインデックス
            
            // バウンズチェック (既に外側で行っているが念の為)
            if(idx >= rates_total) break;

            double t1 = TenkanBuffer[idx];
            double t2 = TenkanBuffer[idx - 1]; // 「未来」側だが、クロス判定用として一つ手前（最新寄り）を見る
            // ArraySetAsSeries=trueなので、t2はidxより「新しい」。
            // つまり、idx(過去) から idx-1(現在寄り) への変化を見る。
            
            // クロス判定ロジックの見直し
            // 時系列: [Old] ... idx ... idx-1 ... [New]
            // 時間の流れは idx -> idx-1
            // idx時点の値(t1)と、idx-1時点の値(t2)の間にcurrentKijunをまたいでいるか？
            
            // t1(Old) -> t2(New)
            bool upCross = (t1 < currentKijun && t2 >= currentKijun);   // 下から上へ
            bool downCross = (t1 > currentKijun && t2 <= currentKijun); // 上から下へ
            
            // もし「タッチしている期間」を測るならこれでよいが、
            // 「転換線と基準線が離れてから再び交差するまでの期間」を測る場合、
            // 「現在の基準線と同じ価格レベル」を転換線がいつ横切ったかを探す。
            
            // 元のロジック:
            // t1 = Tenkan[idx] (Old)
            // t2 = Tenkan[idx-1] (New)
            // upCross = (t1 >= currentKijun && t2 < currentKijun); 
            // これは「過去(t1)では上だったが、現在(t2)では下になった」 = デッドクロス(下抜け)
            // 逆方向のクロスを探している。
            // つまり、「過去に遡って、いつ交差したか」を探す。
            
            // 過去に向かって探索: j=1, 2, 3...
            // idx = i+1, i+2...
            // idx(過去) における値。
            // ここで t2 = idx-1 としているのは、idxより「1つ新しい」足。
            // つまり、idx と idx-1 の間でクロスがあったか？
            
            // 元コード:
            // idx = i - j (Series=falseなので iがNew, idxがOld)
            // t1 = buf[idx] (Old)
            // t2 = buf[idx-1] (More Old)
            // おっと、元コードでは idx-1 は「さらに過去」だった！
            // 元コード: idx = i - j (Old). idx - 1 (More Old).
            // t1(Old) と t2(Older) の間でクロス？
            // upCross = (t1 >= K && t2 < K). Older(low) -> Old(high). 上抜け。
            
            // 今回のコード: Series=true.
            // i = New.
            // j = 1. idx = i+1 (Old).
            // idx+1 (Older).
            
            // 元コードと同じロジックにするなら、
            // t1 = Tenkan[idx] (Old)
            // t2 = Tenkan[idx + 1] (Older)
            // とすべき。
            
            if(idx + 1 >= rates_total) break; // 安全策
            
            double val_Old = TenkanBuffer[idx];
            double val_Older = TenkanBuffer[idx + 1];
            
            // Older(昔) -> Old(今) への変化でクロスしたか
            // upCross: Older < K && Old >= K
            bool up = (val_Older < currentKijun && val_Old >= currentKijun);
            bool down = (val_Older > currentKijun && val_Old <= currentKijun);
            
            if(up || down)
            {
               tkWidth = j;
               break;
            }
            tkWidth = j;
         }
         TKWidthBuffer[i] = (double)tkWidth;
      }
      else
      {
         TKWidthBuffer[i] = 0.0;
      }


      // --- 2. Cloud Width (Span A vs Current Span B Level) ---
      if(DisplayMode == MODE_BOTH || DisplayMode == MODE_CLOUD)
      {
         double currentSpanB = SpanBBuffer[i];
         int cloudWidth = 0;

         for(int j = 1; j <= SearchMax; j++)
         {
            int idx = i + j;
            if(idx + 1 >= rates_total) break;

            double A_Old = SpanABuffer[idx];
            double A_Older = SpanABuffer[idx + 1];

            // Older -> Old
            bool up = (A_Older < currentSpanB && A_Old >= currentSpanB);
            bool down = (A_Older > currentSpanB && A_Old <= currentSpanB);

            if(up || down)
            {
               cloudWidth = j;
               break;
            }
            cloudWidth = j;
         }
         CloudWidthBuffer[i] = (double)cloudWidth;
      }
      else
      {
         CloudWidthBuffer[i] = 0.0;
      }
   }

   return(rates_total);
}
//+------------------------------------------------------------------+
