#property copyright "KK"
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

#property description "VWAP indicator with H1 / H4 / Daily / Weekly / Monthly reset."
#property description "Selectable via on-chart buttons."

#property indicator_color1  clrWhite
#property indicator_type1   DRAW_LINE
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
#property indicator_label1  "VWAP"

input int ButtonX = 1450;
input int ButtonY = 200;

double dVWAP[];

string ButtonNames[5];
string BtnLabels[5] = {"H1", "H4", "Daily", "Weekly", "Monthly"};
int    CurrentIndex = 2; // default: Daily

string GV_Name() { return "VWAP_Index_" + _Symbol + "_" + IntegerToString(_Period); }

void OnInit()
{
    string gvName = GV_Name();
    if(GlobalVariableCheck(gvName))
    {
        CurrentIndex = (int)GlobalVariableGet(gvName);
        if(CurrentIndex < 0 || CurrentIndex > 4) CurrentIndex = 2;
    }

    SetIndexBuffer(0, dVWAP, INDICATOR_DATA);
    ArraySetAsSeries(dVWAP, true);
    PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
    PlotIndexSetString(0, PLOT_LABEL, "VWAP " + BtnLabels[CurrentIndex]);

    UpdateIndicatorName();
    CreateButtons();
}

void OnDeinit(const int reason) { DeleteButtons(); }

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if(id == CHARTEVENT_OBJECT_CLICK)
    {
        for(int i = 0; i < 5; i++)
        {
            if(sparam == ButtonNames[i])
            {
                ObjectSetInteger(0, ButtonNames[i], OBJPROP_STATE, false);
                if(CurrentIndex == i) return;
                CurrentIndex = i;
                GlobalVariableSet(GV_Name(), CurrentIndex);
                PlotIndexSetString(0, PLOT_LABEL, "VWAP " + BtnLabels[CurrentIndex]);
                UpdateButtons();
                UpdateIndicatorName();
                ChartSetSymbolPeriod(ChartID(), _Symbol, (ENUM_TIMEFRAMES)_Period);
                return;
            }
        }
    }
}

void CreateButtons()
{
    int bW = 70, bH = 25, bS = 5;
    for(int i = 0; i < 5; i++)
    {
        ButtonNames[i] = "VWAP_Button_" + BtnLabels[i];
        if(ObjectCreate(0, ButtonNames[i], OBJ_BUTTON, 0, 0, 0))
        {
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_XDISTANCE,    ButtonX);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_YDISTANCE,    ButtonY + (bH + bS) * i);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_XSIZE,        bW);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_YSIZE,        bH);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_CORNER,       CORNER_LEFT_UPPER);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_FONTSIZE,     9);
            ObjectSetString(0,  ButtonNames[i], OBJPROP_TEXT,         BtnLabels[i]);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_COLOR,        clrWhite);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_BGCOLOR,      (i == CurrentIndex) ? clrGreen : clrDarkSlateGray);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_BORDER_COLOR, clrBlack);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_SELECTABLE,   false);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_SELECTED,     false);
        }
    }
}

void UpdateButtons()
{
    for(int i = 0; i < 5; i++)
        if(ObjectFind(0, ButtonNames[i]) >= 0)
        {
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_BGCOLOR, (i == CurrentIndex) ? clrGreen : clrDarkSlateGray);
            ObjectSetInteger(0, ButtonNames[i], OBJPROP_STATE,   false);
        }
    ChartRedraw();
}

void DeleteButtons()
{
    for(int i = 0; i < 5; i++)
        if(ObjectFind(0, ButtonNames[i]) >= 0)
            ObjectDelete(0, ButtonNames[i]);
}

void UpdateIndicatorName()
{
    IndicatorSetString(INDICATOR_SHORTNAME, "KK_VWAP (" + BtnLabels[CurrentIndex] + ")");
}

bool IsNewPeriod(int i, const datetime &time[], int total)
{
    if(i >= total - 1) return true;

    MqlDateTime dt, dtPrev;
    TimeToStruct(time[i],     dt);
    TimeToStruct(time[i + 1], dtPrev);

    if(CurrentIndex == 0) // H1
        return (dt.hour != dtPrev.hour || dt.day != dtPrev.day || dt.mon != dtPrev.mon);
    if(CurrentIndex == 1) // H4
    {
        int session     = dt.hour / 4;
        int sessionPrev = dtPrev.hour / 4;
        return (session != sessionPrev || dt.day != dtPrev.day || dt.mon != dtPrev.mon);
    }
    if(CurrentIndex == 2) // Daily
        return (dt.day != dtPrev.day || dt.mon != dtPrev.mon || dt.year != dtPrev.year);
    if(CurrentIndex == 3) // Weekly
        return (dt.day_of_week < dtPrev.day_of_week || dt.day_of_week == 0);
    if(CurrentIndex == 4) // Monthly
        return (dt.mon != dtPrev.mon || dt.year != dtPrev.year);

    return false;
}

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 < 2) return 0;

    ArraySetAsSeries(high,        true);
    ArraySetAsSeries(low,         true);
    ArraySetAsSeries(close,       true);
    ArraySetAsSeries(time,        true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(volume,      true);

    double cumTPV = 0.0;
    double cumVol = 0.0;

    for(int i = rates_total - 1; i >= 0; i--)
    {
        if(IsNewPeriod(i, time, rates_total))
        {
            cumTPV = 0.0;
            cumVol = 0.0;
        }

        double typicalPrice = (high[i] + low[i] + close[i]) / 3.0;
        double vol = (volume[i] > 0) ? (double)volume[i] : (double)tick_volume[i];
        if(vol <= 0) vol = 1.0;

        cumTPV += typicalPrice * vol;
        cumVol += vol;

        dVWAP[i] = (cumVol > 0) ? cumTPV / cumVol : typicalPrice;
    }

    return rates_total;
}
