//+------------------------------------------------------------------+
//|                                                  CombinedIndicator.mq4 |
//|                        Combines BoxFibo and CustomTimeLine features |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window

// Input parameters for BoxFibo
extern string StartTimeBoxFibo = "00:00"; 
extern string EndTimeBoxFibo = "09:00"; 
extern int    NumDays = 30;
extern string FibPercentLevels = "0, 50, 100, 200, -100";
extern string EXTRA_Corresponding_FibLevelDesc = ",,,,";
extern bool   ShowPercentPerLevel = true;
extern bool   ShowPricePerlevel = true;
extern int    DisplayPercentWithNumberOfDigits = 0; 
extern color  BoxColor = clrLightBlue;
extern color  FiboColor = clrBlack;
extern bool   AutoFlipColorsIfDarkBG = true;
extern int    SupressIndicatorForTFsAbove_minutes = 60;
extern int    ShiftFiboTime2By_seconds = 3600; 
extern int    FiboLineWidth = 2; 
extern ENUM_LINE_STYLE FiboLineStyle = STYLE_SOLID; 
extern bool   EnableAlerts = true; 
extern string AlertFibLevels = "0, 100"; 
extern bool   ShowPopUpAlert = true; 
extern string AlertStartTime = "08:00"; 
extern string AlertEndTime = "17:00"; 
extern int    TimeLabelFontSize = 8; 
extern string TimeLabelFont = "Arial"; 

// Input parameters for CustomTimeLine
input color LineColor = clrLime;          // Main line color
input color LabelColor = clrLime;         // Label text color
input int MinutesToExtend = 60;           // Minutes to extend main line
input string StartTimeCustom = "10:00";   // Main line start time
// Vertical Line 1 Settings
input bool ShowVertLine1 = true;          // Show Vertical Line 1
input string VertLine1Time = "09:30";     // Vertical Line 1 time
input color VertLine1Color = clrRoyalBlue;// Vertical Line 1 color
input ENUM_LINE_STYLE VertLine1Style = STYLE_DASH; // Vertical Line 1 style
input int VertLine1Width = 1;             // Vertical Line 1 width
// Vertical Line 2 Settings
input bool ShowVertLine2 = true;          // Show Vertical Line 2
input string VertLine2Time = "16:00";     // Vertical Line 2 time
input color VertLine2Color = clrOrangeRed;// Vertical Line 2 color
input ENUM_LINE_STYLE VertLine2Style = STYLE_DOT; // Vertical Line 2 style
input int VertLine2Width = 1;             // Vertical Line 2 width
// Font settings
input int FontSize = 10;                  // Font size for labels

// Button Parameters
input string             button_note1_          = "------------------------------";
input int                btn_Subwindow          = 0;                               
input ENUM_BASE_CORNER   btn_corner             = CORNER_LEFT_UPPER;               
input string             btn_text               = "BoxFibo";                       
input string             btn_Font               = "Arial";                         
input int                btn_FontSize           = 9;                               
input color              btn_text_ON_color      = clrLime;                         
input color              btn_text_OFF_color     = clrLightSalmon;                  
input color              btn_background_color   = clrDimGray;                      
input color              btn_border_color       = clrBlack;                        
input int                button_x               = 20;                              
input int                button_y               = 45;                              
input int                btn_Width              = 60;                              
input int                btn_Height             = 20;                              
input bool               btn_Initial_Default_ON = true;                           
input string             button_note2           = "------------------------------";

// Global Variables
string createdObjectPrefix;
datetime dtStartTimeBoxFibo;
datetime dtEndTimeBoxFibo;
string fibLevelText = "";
string fibLevelsStr[];
string fibLevelsPrefix[];
double fibLevels[];
int fibCount;
bool debugmicro = true; // Set to false for production
string buttonId;
bool buttonStatus;
// Variables for Fibonacci Level Alerts
double alertFibLevels[]; // Array to store Fibonacci levels for alerts
int alertFibCount; // Number of Fibonacci levels to monitor for alerts
bool alertTriggered[]; // Array to track if an alert has been triggered for each level
// Variables for Time Range
datetime alertStartTime; // Start time for alerts
datetime alertEndTime;   // End time for alerts

// Global variables for CustomTimeLine
string lineName = "CustomTimeLine";
string labelName = "CustomTimeLabel";
string vertLine1Name = "VertLine1";
string vertLine2Name = "VertLine2";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
    // Generate unique object prefix for BoxFibo
    createdObjectPrefix = StringConcatenate("_BoxFibo_", StartTimeBoxFibo, "_", EndTimeBoxFibo, "_");
    // Validate input parameters for BoxFibo
    if (!ValidateInputs()) return INIT_FAILED;
    // Initialize Fibonacci levels for BoxFibo
    InitializeFibonacciLevels();
    // Initialize Fibonacci levels for alerts
    InitializeAlertFibLevels();
    // Convert StartTime and EndTime to datetime for BoxFibo
    dtStartTimeBoxFibo = StrToTime("1970.01.01 " + StartTimeBoxFibo);
    dtEndTimeBoxFibo = StrToTime("1970.01.01 " + EndTimeBoxFibo);
    // Convert AlertStartTime and AlertEndTime to datetime
    alertStartTime = StrToTime("1970.01.01 " + AlertStartTime);
    alertEndTime = StrToTime("1970.01.01 " + AlertEndTime);
    // Create the on/off button for BoxFibo
    CreateButton();
    // Adjust colors for dark background
    if (AutoFlipColorsIfDarkBG && !IsBackgroundLight())
    {
        BoxColor = ColorToVisibleRGBFlip(BoxColor);
        FiboColor = ColorToVisibleRGBFlip(FiboColor);
    }
    // Plot objects if timeframe is supported
    if (Period() <= SupressIndicatorForTFsAbove_minutes) PlotObjects();
    
    // Initialize CustomTimeLine
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Clean up BoxFibo objects
    DeleteObjects();
    // Clean up CustomTimeLine objects
    for(int i = 0; i < NumDays; i++)
    {
        datetime currentDate = iTime(_Symbol, PERIOD_D1, i);
        string customLineName = "CustomTimeLine_" + IntegerToString(i);
        ObjectDelete(0, customLineName);
        string customLabelName = "CustomLabel_" + IntegerToString(i);
        ObjectDelete(0, customLabelName);
        string customVertLine1Name = "VertLine1_" + IntegerToString(i);
        ObjectDelete(0, customVertLine1Name);
        string customVertLine2Name = "VertLine2_" + IntegerToString(i);
        ObjectDelete(0, customVertLine2Name);
    }
}

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // Handle button click event
    if (id == CHARTEVENT_OBJECT_CLICK && sparam == buttonId)
    {
        buttonStatus = ObjectGetInteger(0, buttonId, OBJPROP_STATE);
        if (buttonStatus) // On
        {
            ObjectSetInteger(0, buttonId, OBJPROP_COLOR, btn_text_ON_color);
            DeleteObjects();
            if (Period() < PERIOD_D1) PlotObjects(); // Force display on <= H4
        }
        else // Off
        {
            ObjectSetInteger(0, buttonId, OBJPROP_COLOR, btn_text_OFF_color);
            DeleteObjects();
        }
        ChartRedraw();
    }
}

//+------------------------------------------------------------------+
//| Main calculation 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 (Period() > SupressIndicatorForTFsAbove_minutes) return 0; // Suppress for higher timeframes
    if (Period() >= PERIOD_D1) return 0; // Not intended for D1 and above
    
    static datetime lastTimeCurrent = 0;
    bool isNewBar = IsNewBar();
    // Run no more than once per second
    if (TimeCurrent() == lastTimeCurrent && !isNewBar) return 0;
    lastTimeCurrent = TimeCurrent();
    
    // Plot objects for BoxFibo
    PlotObjects();
    // Check for Fibonacci level breakouts
    if (EnableAlerts) CheckFibLevelBreakouts();

    // Draw main horizontal line and vertical lines for CustomTimeLine for NumDays
    for(int i = 0; i < NumDays; i++)
    {
        datetime currentDate = iTime(_Symbol, PERIOD_D1, i);
        int startHour, startMinute;
        if (StringFind(StartTimeCustom, ":") != -1)
        {
            startHour = (int)StringToInteger(StringSubstr(StartTimeCustom, 0, 2));
            startMinute = (int)StringToInteger(StringSubstr(StartTimeCustom, 3, 2));
            
            datetime startTime = currentDate + startHour * 3600 + startMinute * 60;
            
            int startIndex = iBarShift(_Symbol, _Period, startTime, true);
            if(startIndex >= 0)
            {
                double startPrice = open[startIndex];
                datetime futureTime = startTime + MinutesToExtend * 60;
                
                string customLineName = "CustomTimeLine_" + IntegerToString(i);
                if(ObjectFind(0, customLineName) >= 0) ObjectDelete(0, customLineName);
                ObjectCreate(0, customLineName, OBJ_TREND, 0, time[startIndex], startPrice, futureTime, startPrice);
                ObjectSetInteger(0, customLineName, OBJPROP_COLOR, LineColor);
                ObjectSetInteger(0, customLineName, OBJPROP_STYLE, STYLE_DOT);
                ObjectSetInteger(0, customLineName, OBJPROP_WIDTH, 1);
                
                string customLabelName = "CustomLabel_" + IntegerToString(i); // Renamed local variable
                ObjectCreate(0, customLabelName, OBJ_TEXT, 0, futureTime, startPrice);
                ObjectSetString(0, customLabelName, OBJPROP_TEXT, StartTimeCustom);
                ObjectSetInteger(0, customLabelName, OBJPROP_COLOR, LabelColor);
                ObjectSetInteger(0, customLabelName, OBJPROP_FONTSIZE, FontSize);
                ObjectSetInteger(0, customLabelName, OBJPROP_BACK, true);
                ObjectSetInteger(0, customLabelName, OBJPROP_BGCOLOR, clrBlack);
            }
        }

        // Handle vertical lines visibility for CustomTimeLine
        if(ShowVertLine1) {
            string customVertLine1Name = "VertLine1_" + IntegerToString(i);
            CreateVerticalLine(customVertLine1Name, VertLine1Time, VertLine1Color, VertLine1Style, VertLine1Width, currentDate);
        } else {
            string customVertLine1Name = "VertLine1_" + IntegerToString(i);
            if(ObjectFind(0, customVertLine1Name) >= 0) ObjectDelete(0, customVertLine1Name);
        }
        
        if(ShowVertLine2) {
            string customVertLine2Name = "VertLine2_" + IntegerToString(i);
            CreateVerticalLine(customVertLine2Name, VertLine2Time, VertLine2Color, VertLine2Style, VertLine2Width, currentDate);
        } else {
            string customVertLine2Name = "VertLine2_" + IntegerToString(i);
            if(ObjectFind(0, customVertLine2Name) >= 0) ObjectDelete(0, customVertLine2Name);
        }
    }

    return rates_total;
}

//+------------------------------------------------------------------+
//| Helper Functions                                                 |
//+------------------------------------------------------------------+

// Validate input parameters
bool ValidateInputs()
{
    if (StringFind(StartTimeBoxFibo, ":") == -1 || StringFind(EndTimeBoxFibo, ":") == -1)
    {
        Alert("Invalid time format. Please use HH:MM format for StartTime and EndTime.");
        return false;
    }
    return true;
}

// Initialize Fibonacci levels
void InitializeFibonacciLevels()
{
    FibPercentLevels = StringReplaceEveryMatch(FibPercentLevels, ",", " ");
    FibPercentLevels = StringTrimLeft(StringTrimRight(FibPercentLevels));
    fibCount = StringSplitBySpaces(fibLevelsStr, FibPercentLevels);
    ArrayResize(fibLevels, fibCount);
    for (int c = 0; c < fibCount; c++)
    {
        fibLevels[c] = StringToDouble(fibLevelsStr[c]) / 100.0;
    }
    if (ShowPricePerlevel) fibLevelText = " %$";
    StringSplitByDelimiters(fibLevelsPrefix, EXTRA_Corresponding_FibLevelDesc, ",", true, fibCount, "");
}

// Initialize Fibonacci levels for alerts
void InitializeAlertFibLevels()
{
    AlertFibLevels = StringReplaceEveryMatch(AlertFibLevels, ",", " ");
    AlertFibLevels = StringTrimLeft(StringTrimRight(AlertFibLevels));
    alertFibCount = StringSplitBySpaces(fibLevelsStr, AlertFibLevels);
    ArrayResize(alertFibLevels, alertFibCount);
    ArrayResize(alertTriggered, alertFibCount);
    for (int c = 0; c < alertFibCount; c++)
    {
        alertFibLevels[c] = StringToDouble(fibLevelsStr[c]) / 100.0;
        alertTriggered[c] = false; // Initialize alert triggers
    }
}

// Create the on/off button
void CreateButton()
{
    buttonId = "_" + createdObjectPrefix + "_BT_";
    if (ObjectFind(buttonId) < 0)
    {
        ObjectCreate(0, buttonId, OBJ_BUTTON, btn_Subwindow, 0, 0);
        ObjectSetInteger(0, buttonId, OBJPROP_COLOR, btn_text_ON_color);
        ObjectSetInteger(0, buttonId, OBJPROP_BGCOLOR, btn_background_color);
        ObjectSetInteger(0, buttonId, OBJPROP_BORDER_COLOR, btn_border_color);
        ObjectSetString(0, buttonId, OBJPROP_TEXT, btn_text);
        ObjectSetInteger(0, buttonId, OBJPROP_FONTSIZE, btn_FontSize);
        ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE, button_x);
        ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE, button_y);
        ObjectSetInteger(0, buttonId, OBJPROP_XSIZE, btn_Width);
        ObjectSetInteger(0, buttonId, OBJPROP_YSIZE, btn_Height);
        ObjectSetInteger(0, buttonId, OBJPROP_STATE, btn_Initial_Default_ON);
    }
}

// Plot boxes and Fibonacci levels
void PlotObjects(int limit = INT_MAX)
{
    datetime timeCurrent = TimeCurrent();
    timeCurrent -= (datetime)MathMod(timeCurrent, 86400);
    datetime startTimeWindow = timeCurrent + dtStartTimeBoxFibo;
    datetime endTimeWindow = timeCurrent + dtEndTimeBoxFibo;
    int NumBars = (int)(endTimeWindow - startTimeWindow) / PeriodSeconds();
    limit = MathMin(limit, Bars - NumBars);
    for (int i = 0; i < limit; i++)
    {
        datetime timei = Time[i] - (datetime)MathMod(Time[i], 86400);
        startTimeWindow = timei + dtStartTimeBoxFibo;
        endTimeWindow = timei + dtEndTimeBoxFibo;
        int ib1 = iBarShift(NULL, 0, startTimeWindow);
        if (Time[ib1] < startTimeWindow && ib1 >= 1) { ib1--; startTimeWindow = Time[ib1]; }
        int ib2 = iBarShift(NULL, 0, endTimeWindow);
        if (Time[ib2] < endTimeWindow && ib2 >= 1) { ib2--; endTimeWindow = Time[ib2]; }
        if (endTimeWindow != Time[i]) continue;
        int barHigh = iHighest(NULL, 0, MODE_HIGH, ib1 - ib2, ib2 + 1);
        int barLow = iLowest(NULL, 0, MODE_LOW, ib1 - ib2, ib2 + 1);
        double boxHigh = High[barHigh];
        double boxLow = Low[barLow];
        DrawBox(startTimeWindow, endTimeWindow, boxHigh, boxLow);
        DrawFibonacci(startTimeWindow, endTimeWindow, boxHigh, boxLow);
        DrawBoxTimeLabel(startTimeWindow, endTimeWindow, boxHigh); // Draw box time label
    }
}

// Draw a box
void DrawBox(datetime startTime, datetime endTime, double highPrice, double lowPrice)
{
    string objname = createdObjectPrefix + IntegerToString(startTime) + "_R";
    if (ObjectFind(objname) < 0)
    {
        ObjectCreate(objname, OBJ_RECTANGLE, 0, startTime, highPrice, endTime - 60, lowPrice);
    }
    else
    {
        ObjectSetInteger(0, objname, OBJPROP_TIME1, startTime);
        ObjectSetInteger(0, objname, OBJPROP_TIME2, endTime - 60);
        ObjectSetDouble(0, objname, OBJPROP_PRICE1, highPrice);
        ObjectSetDouble(0, objname, OBJPROP_PRICE2, lowPrice);
    }
    ObjectSet(objname, OBJPROP_COLOR, BoxColor);
    ObjectSet(objname, OBJPROP_SELECTABLE, false);
}

// Draw Fibonacci levels
void DrawFibonacci(datetime startTime, datetime endTime, double highPrice, double lowPrice)
{
    if (fibCount > 0)
    {
        string objname = createdObjectPrefix + IntegerToString(startTime) + "_F";
        if (ObjectFind(objname) < 0)
        {
            ObjectCreate(objname, OBJ_FIBO, 0, endTime - ShiftFiboTime2By_seconds, highPrice, endTime, lowPrice);
        }
        else
        {
            ObjectSetInteger(0, objname, OBJPROP_TIME1, endTime - ShiftFiboTime2By_seconds);
            ObjectSetInteger(0, objname, OBJPROP_TIME2, endTime);
            ObjectSetDouble(0, objname, OBJPROP_PRICE1, highPrice);
            ObjectSetDouble(0, objname, OBJPROP_PRICE2, lowPrice);
        }
        ObjectSet(objname, OBJPROP_RAY, false);
        ObjectSet(objname, OBJPROP_SELECTABLE, false);
        ObjectSet(objname, OBJPROP_COLOR, BoxColor);
        ObjectSet(objname, OBJPROP_LEVELCOLOR, FiboColor);
        ObjectSet(objname, OBJPROP_LEVELWIDTH, FiboLineWidth); // Set Fibonacci line width
        ObjectSet(objname, OBJPROP_LEVELSTYLE, FiboLineStyle); // Set Fibonacci line style
        ObjectSet(objname, OBJPROP_FIBOLEVELS, fibCount);
        for (int c = 0; c < fibCount; c++)
        {
            string use_fibLevelText = fibLevelText;
            ObjectSet(objname, OBJPROP_FIRSTLEVEL + c, fibLevels[c]);
            if (ShowPercentPerLevel) use_fibLevelText = DoubleToStr(fibLevels[c] * 100.0, DisplayPercentWithNumberOfDigits) + " " + use_fibLevelText;
            if (!StringIsBlank(fibLevelsPrefix[c])) use_fibLevelText = fibLevelsPrefix[c] + "  " + use_fibLevelText;
            ObjectSetFiboDescription(objname, c, use_fibLevelText);
        }
    }
}

// Draw Box Time Label
void DrawBoxTimeLabel(datetime startTime, datetime endTime, double boxHigh)
{
    string localLabelName = "BoxTimeLabel_" + IntegerToString(startTime); // Renamed local variable
    string labelText = TimeToString(startTime, TIME_MINUTES) + " - " + TimeToString(endTime, TIME_MINUTES);
    if (ObjectFind(localLabelName) < 0)
    {
        ObjectCreate(localLabelName, OBJ_TEXT, 0, startTime + (endTime - startTime) / 2, boxHigh + 10 * Point); // Position above the box
    }
    else
    {
        ObjectSetInteger(0, localLabelName, OBJPROP_TIME1, startTime + (endTime - startTime) / 2);
        ObjectSetDouble(0, localLabelName, OBJPROP_PRICE, boxHigh + 10 * Point); // Position above the box
    }
    ObjectSetString(0, localLabelName, OBJPROP_TEXT, labelText);
    ObjectSetInteger(0, localLabelName, OBJPROP_COLOR, FiboColor);
    ObjectSetInteger(0, localLabelName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, localLabelName, OBJPROP_FONTSIZE, TimeLabelFontSize); // Set font size for time label
    ObjectSetString(0, localLabelName, OBJPROP_FONT, TimeLabelFont); // Set font for time label
}

// Delete all objects created by the indicator
void DeleteObjects(string prefix = "")
{
    if (StringLen(prefix) == 0) prefix = createdObjectPrefix;
    int limit = ObjectsTotal() - 1;
    for (int k = limit; k >= 0; k--)
    {
        string objname = ObjectName(k);
        if (StringFind(objname, prefix, 0) == 0) ObjectDelete(objname);
    }
}

// Check for Fibonacci level breakouts
void CheckFibLevelBreakouts()
{
    // Get the current time (in seconds since midnight)
    datetime currentTime = TimeCurrent() % 86400; // Extract time part only
    // Check if the current time is within the alert time range
    if (currentTime >= alertStartTime && currentTime <= alertEndTime)
    {
        double currentPrice = Close[0]; // Current price
        for (int i = 0; i < alertFibCount; i++)
        {
            double fibLevel = alertFibLevels[i];
            if (!alertTriggered[i] && (currentPrice >= fibLevel || currentPrice <= fibLevel))
            {
                string alertMessage = "Price crossed Fibonacci level: " + DoubleToString(fibLevel * 100, 2) + "%";
                Alert(alertMessage); // Trigger alert
                if (ShowPopUpAlert) MessageBox(alertMessage, "Fibonacci Level Breakout", MB_OK | MB_ICONINFORMATION); // Show pop-up window
                alertTriggered[i] = true; // Mark alert as triggered
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Utility Functions                                                |
//+------------------------------------------------------------------+

// Check if a new bar has formed
bool IsNewBar()
{
    static datetime lastBar;
    datetime currentBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
    if (lastBar != currentBar)
    {
        lastBar = currentBar;
        return true;
    }
    return false;
}

// Check if the chart background is light
bool IsBackgroundLight()
{
    return (ColorToRGBSum((int)ChartGetInteger(0, CHART_COLOR_BACKGROUND)) > 382);
}

// Flip colors for visibility
color ColorToVisibleRGBFlip(color c)
{
    if (IsBackgroundLight())
    {
        return (color)((~c & 0xFFFFFF) & 0xBBBBBB);
    }
    else
    {
        return (color)((~c & 0xFFFFFF) | 0x444444);
    }
}

// Calculate the sum of RGB components
int ColorToRGBSum(color c)
{
    return ((c & 0xFF) + ((c >> 8) & 0xFF) + ((c >> 16) & 0xFF));
}

// Replace every match in a string
string StringReplaceEveryMatch(string str, string toFind, string toReplace)
{
    int pos = 0;
    while ((pos = StringFind(str, toFind, pos)) != -1)
    {
        str = StringConcatenate(StringSubstr(str, 0, pos), toReplace, StringSubstr(str, pos + StringLen(toFind)));
        pos += StringLen(toReplace);
    }
    return str;
}

// Split a string by spaces
int StringSplitBySpaces(string &output[], string s_input)
{
    int pos, arraysize;
    ArrayResize(output, 0);
    s_input = StringTrimLeft(StringTrimRight(s_input));
    while (true)
    {
        pos = StringFind(s_input, " ");
        arraysize = ArraySize(output);
        ArrayResize(output, arraysize + 1);
        if (pos != -1)
        {
            output[arraysize] = StringTrimLeft(StringTrimRight(StringSubstr(s_input, 0, pos)));
            s_input = StringTrimLeft(StringTrimRight(StringSubstr(s_input, pos + 1)));
        }
        else
        {
            output[arraysize] = StringTrimLeft(StringTrimRight(s_input));
            break;
        }
    }
    return ArraySize(output);
}

// Split a string by delimiters
int StringSplitByDelimiters(string &output[], string s_input, string delimiters, bool trimLeftRight = true, int finalSize = 0, string defaultValue = "")
{
    int p, d, len, pos, size, cycles = 0;
    int countDelim = StringLen(delimiters);
    if (countDelim == 0) return -1; // Error, no delimiter specified
    int delim[];
    ArrayResize(delim, countDelim);
    for (p = 0; p < countDelim; p++) delim[p] = (uchar)StringGetChar(delimiters, p);
    if (trimLeftRight) s_input = StringTrimRight(s_input);
    ArrayResize(output, 0);
    while (true)
    {
        cycles++;
        if (cycles > 10000) return -1;
        if (trimLeftRight) s_input = StringTrimLeft(s_input);
        pos = 0;
        len = StringLen(s_input);
        while (true)
        {
            cycles++;
            if (cycles > 10000) return -1;
            ushort asciiCode = StringGetCharacter(s_input, pos);
            bool delimFound = false;
            for (d = 0; d < countDelim; d++)
            {
                if (pos >= len || asciiCode == delim[d])
                {
                    delimFound = true;
                    break;
                }
            }
            if (delimFound) break;
            pos++;
        }
        size = ArraySize(output);
        ArrayResize(output, size + 1);
        if (pos == 0) output[size] = defaultValue;
        else
        {
            output[size] = StringSubstr(s_input, 0, pos);
            if (trimLeftRight) output[size] = StringTrimLeft(StringTrimRight(output[size]));
        }
        if (pos >= len) break;
        s_input = StringSubstr(s_input, pos + 1);
    }
    int arSize = ArraySize(output);
    if (finalSize > arSize)
    {
        ArrayResize(output, finalSize);
        for (int k = arSize; k < finalSize; k++) output[k] = defaultValue;
        arSize = finalSize;
    }
    return arSize;
}

// Check if a string is blank
bool StringIsBlank(string str, bool do_StringTrim_LR = true)
{
    if (do_StringTrim_LR) str = StringTrimLeft(StringTrimRight(str));
    if (StringGetChar(str, 0) > 0) return false;
    return true;
}

// Create vertical line function
bool CreateVerticalLine(string name, string timeStr, color lineColor, ENUM_LINE_STYLE style, int width, datetime currentDate)
{
    // Parse time input
    int hour, minute;
    if (StringFind(timeStr, ":") == -1) return false;
    
    hour = (int)StringToInteger(StringSubstr(timeStr, 0, 2));
    minute = (int)StringToInteger(StringSubstr(timeStr, 3, 2));
    
    datetime lineTime = currentDate + hour * 3600 + minute * 60;
    
    if(ObjectFind(0, name) >= 0) ObjectDelete(0, name);
    
    if(!ObjectCreate(0, name, OBJ_VLINE, 0, lineTime, 0))
    {
        Print("Failed to create vertical line: ", GetLastError());
        return false;
    }
    
    ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
    ObjectSetInteger(0, name, OBJPROP_STYLE, style);
    ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
    ObjectSetInteger(0, name, OBJPROP_BACK, true);
    
    return true;
}