//+------------------------------------------------------------------+
//|                                         AI ChatGPT EA Part 5.mq5 |
//|                           Copyright 2025, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"
#property strict
#property icon "1. Forex Algo-Trader.ico"

#resource "AI MQL5.bmp"
#define resourceImg "::AI MQL5.bmp"
#resource "AI LOGO.bmp"
#define resourceImgLogo "::AI LOGO.bmp"
#resource "AI NEW CHAT.bmp"
#define resourceNewChat "::AI NEW CHAT.bmp"
#resource "AI CLEAR.bmp"
#define resourceClear "::AI CLEAR.bmp"
#resource "AI HISTORY.bmp"
#define resourceHistory "::AI HISTORY.bmp"
#include "AI JSON FILE.mqh"
#include "AI CREATE OBJECTS FNS.mqh"

enum ENUM_SCROLLBAR_MODE
{
   SCROLL_DYNAMIC_ALWAYS,
   SCROLL_DYNAMIC_HOVER,
   SCROLL_WHEEL_ONLY
};

input ENUM_SCROLLBAR_MODE ScrollbarMode = SCROLL_DYNAMIC_HOVER;
#define SCROLL_LEADER "ChatGPT_Scroll_Leader"
#define SCROLL_UP_REC "ChatGPT_Scroll_Up_Rec"
#define SCROLL_UP_LABEL "ChatGPT_Scroll_Up_Label"
#define SCROLL_DOWN_REC "ChatGPT_Scroll_Down_Rec"
#define SCROLL_DOWN_LABEL "ChatGPT_Scroll_Down_Label"
#define SCROLL_SLIDER "ChatGPT_Scroll_Slider"
#define P_SCROLL_LEADER "ChatGPT_P_Scroll_Leader"
#define P_SCROLL_UP_REC "ChatGPT_P_Scroll_Up_Rec"
#define P_SCROLL_UP_LABEL "ChatGPT_P_Scroll_Up_Label"
#define P_SCROLL_DOWN_REC "ChatGPT_P_Scroll_Down_Rec"
#define P_SCROLL_DOWN_LABEL "ChatGPT_P_Scroll_Down_Label"
#define P_SCROLL_SLIDER "ChatGPT_P_Scroll_Slider"
#define BIG_SCROLL_LEADER "ChatGPT_Big_Scroll_Leader"
#define BIG_SCROLL_UP_REC "ChatGPT_Big_Scroll_Up_Rec"
#define BIG_SCROLL_UP_LABEL "ChatGPT_Big_Scroll_Up_Label"
#define BIG_SCROLL_DOWN_REC "ChatGPT_Big_Scroll_Down_Rec"
#define BIG_SCROLL_DOWN_LABEL "ChatGPT_Big_Scroll_Down_Label"
#define BIG_SCROLL_SLIDER "ChatGPT_Big_Scroll_Slider"
input string OpenAI_Model = "gpt-4o";
input string OpenAI_Endpoint = "https://api.openai.com/v1/chat/completions";
input int MaxResponseLength = 3000;
input string LogFileName = "ChatGPT_EA_Log.txt";
input int MaxChartBars = 10;

string OpenAI_API_Key = "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";
string conversationHistory = "";
string currentPrompt = "";
int logFileHandle = INVALID_HANDLE;
bool button_hover = false;
color button_original_bg = clrRoyalBlue;
color button_darker_bg;
bool clear_hover = false;
bool new_chat_hover = false;
color clear_original_bg = clrLightCoral;
color clear_darker_bg;
color new_chat_original_bg = clrLightBlue;
color new_chat_darker_bg;
color chart_button_bg = clrLightGreen;
color chart_button_darker_bg;
bool chart_hover = false;
color close_original_bg = clrLightGray;
color close_darker_bg;
bool close_hover = false;
color toggle_original_bg = clrLightGray;
color toggle_darker_bg;
bool toggle_hover = false;
color history_original_bg = clrWhite;
color history_darker_bg;
bool history_hover = false;
color see_more_original_bg = clrRoyalBlue;
color see_more_darker_bg;
bool see_more_hover = false;
color big_close_original_bg = clrLightGray;
color big_close_darker_bg;
bool big_close_hover = false;
int expandedSidebarWidth = 150;
int contractedSidebarWidth = 50;
bool sidebarExpanded = true;
int g_sidebarWidth = 150;
int g_dashboardX = 10;
int g_mainContentX = g_dashboardX + g_sidebarWidth;
int g_mainY = 30;
int g_mainWidth = 550;
int g_dashboardWidth = g_sidebarWidth + g_mainWidth;
int g_mainHeight = 0;
int g_padding = 10;
int g_sidePadding = 6;
int g_textPadding = 10;
int g_headerHeight = 40;
int g_displayHeight = 280;
int g_footerHeight = 180;
int g_promptHeight = 130;
int g_margin = 5;
int g_buttonHeight = 36;
int g_editHeight = 25;
int g_lineSpacing = 2;
int g_editW = 0;
bool scroll_visible = false;
bool mouse_in_display = false;
int scroll_pos = 0;
int prev_scroll_pos = -1;
int slider_height = 20;
bool movingStateSlider = false;
int mlbDownX_Slider = 0;
int mlbDownY_Slider = 0;
int mlbDown_YD_Slider = 0;
int g_total_height = 0;
int g_visible_height = 0;
bool p_scroll_visible = false;
bool mouse_in_prompt = false;
int p_scroll_pos = 0;
int p_slider_height = 20;
bool p_movingStateSlider = false;
int p_mlbDownX_Slider = 0;
int p_mlbDownY_Slider = 0;
int p_mlbDown_YD_Slider = 0;
int p_total_height = 0;
int p_visible_height = 0;
color g_promptBg = clrOldLace;
string g_scaled_image_resource = "";
string g_scaled_sidebar_resource = "";
string g_scaled_sidebar_small = "";
string g_scaled_newchat_resource = "";
string g_scaled_clear_resource = "";
string g_scaled_history_resource = "";
bool dashboard_visible = true;
string dashboardObjects[20];
int objCount = 0;
bool showing_small_history_popup = false;
int small_popup_x, small_popup_y, small_popup_w, small_popup_h;
string small_popup_objects[];
string small_chat_bgs[];
bool showing_big_history_popup = false;
int big_popup_x, big_popup_y, big_popup_w, big_popup_h;
string big_popup_objects[];
string big_chat_bgs[];
string side_chat_bgs[];
int big_scroll_pos = 0;
bool big_scroll_visible = false;
int big_total_height = 0;
int big_visible_height = 0;
int big_slider_height = 20;
bool big_movingStateSlider = false;
int big_mlbDownX_Slider = 0;
int big_mlbDownY_Slider = 0;
int big_mlbDown_YD_Slider = 0;
bool just_opened_big = false;
bool just_opened_small = false;

color DarkenColor(color colorValue, double factor = 0.8) {
   int red = int((colorValue & 0xFF) * factor);
   int green = int(((colorValue >> 8) & 0xFF) * factor);
   int blue = int(((colorValue >> 16) & 0xFF) * factor);
   return (color)(red | (green << 8) | (blue << 16));
}

int getFontSizeByDPI(int baseFontSize, int baseDPI = 96) {
   int currentDPI = (int)TerminalInfoInteger(TERMINAL_SCREEN_DPI);
   int scaledFontSize = (int)(baseFontSize * (double)baseDPI / currentDPI);
   return MathMax(scaledFontSize, 8);
}

void ScaleImage(uint &pixels[], int original_width, int original_height, int new_width, int new_height) {
   uint scaled_pixels[];
   ArrayResize(scaled_pixels, new_width * new_height);
   for (int y = 0; y < new_height; y++) {
      for (int x = 0; x < new_width; x++) {
         double original_x = (double)x * original_width / new_width;
         double original_y = (double)y * original_height / new_height;
         uint pixel = BicubicInterpolate(pixels, original_width, original_height, original_x, original_y);
         scaled_pixels[y * new_width + x] = pixel;
      }
   }
   ArrayResize(pixels, new_width * new_height);
   ArrayCopy(pixels, scaled_pixels);
}

uint BicubicInterpolate(uint &pixels[], int width, int height, double x, double y) {
   int x0 = (int)x;
   int y0 = (int)y;
   double fractional_x = x - x0;
   double fractional_y = y - y0;
   int x_indices[4], y_indices[4];
   for (int i = -1; i <= 2; i++) {
      x_indices[i + 1] = MathMin(MathMax(x0 + i, 0), width - 1);
      y_indices[i + 1] = MathMin(MathMax(y0 + i, 0), height - 1);
   }
   uint neighborhood_pixels[16];
   for (int j = 0; j < 4; j++) {
      for (int i = 0; i < 4; i++) {
         neighborhood_pixels[j * 4 + i] = pixels[y_indices[j] * width + x_indices[i]];
      }
   }
   uchar alpha_components[16], red_components[16], green_components[16], blue_components[16];
   for (int i = 0; i < 16; i++) {
      GetArgb(neighborhood_pixels[i], alpha_components[i], red_components[i], green_components[i], blue_components[i]);
   }
   uchar alpha_out = (uchar)BicubicInterpolateComponent(alpha_components, fractional_x, fractional_y);
   uchar red_out = (uchar)BicubicInterpolateComponent(red_components, fractional_x, fractional_y);
   uchar green_out = (uchar)BicubicInterpolateComponent(green_components, fractional_x, fractional_y);
   uchar blue_out = (uchar)BicubicInterpolateComponent(blue_components, fractional_x, fractional_y);
   return (alpha_out << 24) | (red_out << 16) | (green_out << 8) | blue_out;
}

double BicubicInterpolateComponent(uchar &components[], double fractional_x, double fractional_y) {
   double weights_x[4];
   double t = fractional_x;
   weights_x[0] = (-0.5 * t * t * t + t * t - 0.5 * t);
   weights_x[1] = (1.5 * t * t * t - 2.5 * t * t + 1);
   weights_x[2] = (-1.5 * t * t * t + 2 * t * t + 0.5 * t);
   weights_x[3] = (0.5 * t * t * t - 0.5 * t * t);
   double y_values[4];
   for (int j = 0; j < 4; j++) {
      y_values[j] = weights_x[0] * components[j * 4 + 0] + weights_x[1] * components[j * 4 + 1] +
                    weights_x[2] * components[j * 4 + 2] + weights_x[3] * components[j * 4 + 3];
   }
   double weights_y[4];
   t = fractional_y;
   weights_y[0] = (-0.5 * t * t * t + t * t - 0.5 * t);
   weights_y[1] = (1.5 * t * t * t - 2.5 * t * t + 1);
   weights_y[2] = (-1.5 * t * t * t + 2 * t * t + 0.5 * t);
   weights_y[3] = (0.5 * t * t * t - 0.5 * t * t);
   double result = weights_y[0] * y_values[0] + weights_y[1] * y_values[1] +
                   weights_y[2] * y_values[2] + weights_y[3] * y_values[3];
   return MathMax(0, MathMin(255, result));
}

void GetArgb(uint pixel, uchar &alpha, uchar &red, uchar &green, uchar &blue) {
   alpha = (uchar)((pixel >> 24) & 0xFF);
   red = (uchar)((pixel >> 16) & 0xFF);
   green = (uchar)((pixel >> 8) & 0xFF);
   blue = (uchar)(pixel & 0xFF);
}

void CreateScrollbar() {
   int displayX = g_mainContentX + g_sidePadding;
   int displayY = g_mainY + g_headerHeight + g_padding;
   int displayW = g_mainWidth - 2 * g_sidePadding;
   int scrollbar_x = displayX + displayW - 16;
   int scrollbar_y = displayY + 16;
   int scrollbar_width = 16;
   int scrollbar_height = g_displayHeight - 2 * 16;
   int button_size = 16;
   createRecLabel(SCROLL_LEADER, scrollbar_x, scrollbar_y, scrollbar_width, scrollbar_height, C'220,220,220', 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createRecLabel(SCROLL_UP_REC, scrollbar_x, displayY, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(SCROLL_UP_LABEL, scrollbar_x + 2, displayY + -2, CharToString(0x35), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   createRecLabel(SCROLL_DOWN_REC, scrollbar_x, displayY + g_displayHeight - button_size, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(SCROLL_DOWN_LABEL, scrollbar_x + 2, displayY + g_displayHeight - button_size + -2, CharToString(0x36), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   slider_height = CalculateSliderHeight();
   createRecLabel(SCROLL_SLIDER, scrollbar_x, displayY + g_displayHeight - button_size - slider_height, scrollbar_width, slider_height, clrSilver, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
}

void CreatePromptScrollbar() {
   int promptX = g_mainContentX + g_sidePadding;
   int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding;
   int promptY = footerY + g_margin;
   int promptW = g_mainWidth - 2 * g_sidePadding;
   int scrollbar_x = promptX + promptW - 16;
   int scrollbar_y = promptY + 16;
   int scrollbar_width = 16;
   int scrollbar_height = g_promptHeight - 2 * 16;
   int button_size = 16;
   createRecLabel(P_SCROLL_LEADER, scrollbar_x, scrollbar_y, scrollbar_width, scrollbar_height, C'220,220,220', 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createRecLabel(P_SCROLL_UP_REC, scrollbar_x, promptY, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(P_SCROLL_UP_LABEL, scrollbar_x + 2, promptY + -2, CharToString(0x35), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   createRecLabel(P_SCROLL_DOWN_REC, scrollbar_x, promptY + g_promptHeight - button_size, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(P_SCROLL_DOWN_LABEL, scrollbar_x + 2, promptY + g_promptHeight - button_size + -2, CharToString(0x36), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   p_slider_height = CalculatePromptSliderHeight();
   createRecLabel(P_SCROLL_SLIDER, scrollbar_x, promptY + g_promptHeight - button_size - p_slider_height, scrollbar_width, p_slider_height, clrSilver, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
}

void DeleteScrollbar() {
   ObjectDelete(0, SCROLL_LEADER);
   ObjectDelete(0, SCROLL_UP_REC);
   ObjectDelete(0, SCROLL_UP_LABEL);
   ObjectDelete(0, SCROLL_DOWN_REC);
   ObjectDelete(0, SCROLL_DOWN_LABEL);
   ObjectDelete(0, SCROLL_SLIDER);
}

void DeletePromptScrollbar() {
   ObjectDelete(0, P_SCROLL_LEADER);
   ObjectDelete(0, P_SCROLL_UP_REC);
   ObjectDelete(0, P_SCROLL_UP_LABEL);
   ObjectDelete(0, P_SCROLL_DOWN_REC);
   ObjectDelete(0, P_SCROLL_DOWN_LABEL);
   ObjectDelete(0, P_SCROLL_SLIDER);
}

int CalculateSliderHeight() {
   int scroll_area_height = g_displayHeight - 2 * 16;
   int slider_min_height = 20;
   if (g_total_height <= g_visible_height) return scroll_area_height;
   double visible_ratio = (double)g_visible_height / g_total_height;
   int height = (int)MathFloor(scroll_area_height * visible_ratio);
   return MathMax(slider_min_height, height);
}

int CalculatePromptSliderHeight() {
   int scroll_area_height = g_promptHeight - 2 * 16;
   int slider_min_height = 20;
   if (p_total_height <= p_visible_height) return scroll_area_height;
   double visible_ratio = (double)p_visible_height / p_total_height;
   int height = (int)MathFloor(scroll_area_height * visible_ratio);
   return MathMax(slider_min_height, height);
}

void UpdateSliderPosition() {
   int displayX = g_mainContentX + g_sidePadding;
   int displayY = g_mainY + g_headerHeight + g_padding;
   int scrollbar_x = displayX + (g_mainWidth - 2 * g_sidePadding) - 16;
   int scrollbar_y = displayY + 16;
   int scroll_area_height = g_displayHeight - 2 * 16;
   int max_scroll = MathMax(0, g_total_height - g_visible_height);
   if (max_scroll <= 0) return;
   double scroll_ratio = (double)scroll_pos / max_scroll;
   int scroll_area_y_max = scrollbar_y + scroll_area_height - slider_height;
   int scroll_area_y_min = scrollbar_y;
   int new_y = scroll_area_y_min + (int)(scroll_ratio * (scroll_area_y_max - scroll_area_y_min));
   new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
   ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
}

void UpdatePromptSliderPosition() {
   int promptX = g_mainContentX + g_sidePadding;
   int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding;
   int promptY = footerY + g_margin;
   int scrollbar_x = promptX + (g_mainWidth - 2 * g_sidePadding) - 16;
   int scrollbar_y = promptY + 16;
   int scroll_area_height = g_promptHeight - 2 * 16;
   int max_scroll = MathMax(0, p_total_height - p_visible_height);
   if (max_scroll <= 0) return;
   double scroll_ratio = (double)p_scroll_pos / max_scroll;
   int scroll_area_y_max = scrollbar_y + scroll_area_height - p_slider_height;
   int scroll_area_y_min = scrollbar_y;
   int new_y = scroll_area_y_min + (int)(scroll_ratio * (scroll_area_y_max - scroll_area_y_min));
   new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
   ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
}

void UpdateButtonColors() {
   int max_scroll = MathMax(0, g_total_height - g_visible_height);
   if (scroll_pos == 0) {
      ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_COLOR, clrDimGray);
   }
   if (scroll_pos == max_scroll) {
      ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrDimGray);
   }
}

void UpdatePromptButtonColors() {
   int max_scroll = MathMax(0, p_total_height - p_visible_height);
   if (p_scroll_pos == 0) {
      ObjectSetInteger(0, P_SCROLL_UP_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, P_SCROLL_UP_LABEL, OBJPROP_COLOR, clrDimGray);
   }
   if (p_scroll_pos == max_scroll) {
      ObjectSetInteger(0, P_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, P_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrDimGray);
   }
}

void ScrollUp() {
   if (scroll_pos > 0) {
      scroll_pos = MathMax(0, scroll_pos - 30);
      UpdateResponseDisplay();
      if (scroll_visible) {
         UpdateSliderPosition();
         UpdateButtonColors();
      }
   }
}

void ScrollDown() {
   int max_scroll = MathMax(0, g_total_height - g_visible_height);
   if (scroll_pos < max_scroll) {
      scroll_pos = MathMin(max_scroll, scroll_pos + 30);
      UpdateResponseDisplay();
      if (scroll_visible) {
         UpdateSliderPosition();
         UpdateButtonColors();
      }
   }
}

void PromptScrollUp() {
   if (p_scroll_pos > 0) {
      p_scroll_pos = MathMax(0, p_scroll_pos - 30);
      UpdatePromptDisplay();
      if (p_scroll_visible) {
         UpdatePromptSliderPosition();
         UpdatePromptButtonColors();
      }
   }
}

void PromptScrollDown() {
   int max_scroll = MathMax(0, p_total_height - p_visible_height);
   if (p_scroll_pos < max_scroll) {
      p_scroll_pos = MathMin(max_scroll, p_scroll_pos + 30);
      UpdatePromptDisplay();
      if (p_scroll_visible) {
         UpdatePromptSliderPosition();
         UpdatePromptButtonColors();
      }
   }
}

int SplitOnString(string inputText, string delim, string &result[]) {
   ArrayResize(result, 0);
   int pos = 0;
   int delim_len = StringLen(delim);
   while (true) {
      int found = StringFind(inputText, delim, pos);
      if (found == -1) {
         string part = StringSubstr(inputText, pos);
         if (StringLen(part) > 0 || ArraySize(result) > 0) {
            int size = ArraySize(result);
            ArrayResize(result, size + 1);
            result[size] = part;
         }
         break;
      }
      string part = StringSubstr(inputText, pos, found - pos);
      int size = ArraySize(result);
      ArrayResize(result, size + 1);
      result[size] = part;
      pos = found + delim_len;
   }
   return ArraySize(result);
}

string ReplaceExactDoublePeriods(string text) {
   string result = "";
   int len = StringLen(text);
   for (int i = 0; i < len; i++) {
      if (i + 1 < len && StringGetCharacter(text, i) == '.' && StringGetCharacter(text, i + 1) == '.') {
         bool preceded = (i > 0 && StringGetCharacter(text, i - 1) == '.');
         bool followed = (i + 2 < len && StringGetCharacter(text, i + 2) == '.');
         if (!preceded && !followed) {
            result += "\n";
            i++;
         } else {
            result += ".";
         }
      } else {
         result += StringSubstr(text, i, 1);
      }
   }
   return result;
}

void WrapText(const string inputText, const string font, const int fontSize, const int maxWidth, string &wrappedLines[], int offset = 0) {
   const int maxChars = 60;
   ArrayResize(wrappedLines, 0);
   TextSetFont(font, fontSize);
   string processedText = ReplaceExactDoublePeriods(inputText);
   string paragraphs[];
   int numParagraphs = StringSplit(processedText, '\n', paragraphs);
   for (int p = 0; p < numParagraphs; p++) {
      string para = paragraphs[p];
      if (StringLen(para) == 0) {
         int size = ArraySize(wrappedLines);
         ArrayResize(wrappedLines, size + 1);
         wrappedLines[size] = " ";
         continue;
      }
      string words[];
      int numWords = StringSplit(para, ' ', words);
      string currentLine = "";
      for (int w = 0; w < numWords; w++) {
         if (StringLen(words[w]) == 0) continue;
         string testLine = currentLine + (StringLen(currentLine) > 0 ? " " : "") + words[w];
         uint wid, hei;
         TextGetSize(testLine, wid, hei);
         int textWidth = (int)wid;
         if (textWidth + offset <= maxWidth && StringLen(testLine) <= maxChars) {
            currentLine = testLine;
         } else {
            if (StringLen(currentLine) > 0) {
               int size = ArraySize(wrappedLines);
               ArrayResize(wrappedLines, size + 1);
               wrappedLines[size] = currentLine;
            }
            currentLine = words[w];
            TextGetSize(currentLine, wid, hei);
            textWidth = (int)wid;
            if (textWidth + offset > maxWidth || StringLen(currentLine) > maxChars) {
               string wrappedWord = "";
               for (int c = 0; c < StringLen(words[w]); c++) {
                  string testWord = wrappedWord + StringSubstr(words[w], c, 1);
                  TextGetSize(testWord, wid, hei);
                  int wordWidth = (int)wid;
                  if (wordWidth + offset > maxWidth || StringLen(testWord) > maxChars) {
                     if (StringLen(wrappedWord) > 0) {
                        int size = ArraySize(wrappedLines);
                        ArrayResize(wrappedLines, size + 1);
                        wrappedLines[size] = wrappedWord;
                     }
                     wrappedWord = StringSubstr(words[w], c, 1);
                  } else {
                     wrappedWord = testWord;
                  }
               }
               currentLine = wrappedWord;
            }
         }
      }
      if (StringLen(currentLine) > 0) {
         int size = ArraySize(wrappedLines);
         ArrayResize(wrappedLines, size + 1);
         wrappedLines[size] = currentLine;
      }
   }
}

void CreatePlaceholder() {
   if (ObjectFind(0, "ChatGPT_PromptPlaceholder") < 0 && StringLen(currentPrompt) == 0) {
      int placeholderFontSize = 10;
      string placeholderFont = "Arial";
      int lineHeight = TextGetHeight("A", placeholderFont, placeholderFontSize);
      int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding;
      int promptY = footerY + g_margin;
      int editY = promptY + g_promptHeight - g_editHeight - 5;
      int editX = g_mainContentX + g_sidePadding + g_textPadding;
      int labelY = editY + (g_editHeight - lineHeight) / 2;
      createLabel("ChatGPT_PromptPlaceholder", editX + 2, labelY, "Type your prompt here...", clrGray, placeholderFontSize, placeholderFont, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      ChartRedraw();
   }
}

void DeletePlaceholder() {
   if (ObjectFind(0, "ChatGPT_PromptPlaceholder") >= 0) {
      ObjectDelete(0, "ChatGPT_PromptPlaceholder");
      ChartRedraw();
   }
}

struct Chat {
   int id;
   string title;
   string history;
};
Chat chats[];
int current_chat_id = -1;
string current_title = "";
string chatsFileName = "ChatGPT_Chats.txt";

string EncodeID(int id) {
   string chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
   string res = "";
   if (id == 0) return "0";
   while (id > 0) {
      res = StringSubstr(chars, id % 62, 1) + res;
      id /= 62;
   }
   return res;
}

int DecodeID(string enc) {
   string chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
   int id = 0;
   for (int i = 0; i < StringLen(enc); i++) {
      id = id * 62 + StringFind(chars, StringSubstr(enc, i, 1));
   }
   return id;
}

void LoadChats() {
   if (!FileIsExist(chatsFileName)) {
      CreateNewChat();
      return;
   }
   int handle = FileOpen(chatsFileName, FILE_READ | FILE_BIN);
   if (handle == INVALID_HANDLE) {
      Print("Failed to load chats: ", GetLastError());
      CreateNewChat();
      return;
   }
   int file_size = (int)FileSize(handle);
   uchar encoded_file[];
   ArrayResize(encoded_file, file_size);
   FileReadArray(handle, encoded_file, 0, file_size);
   FileClose(handle);
   uchar empty_key[];
   uchar key[32];
   uchar api_bytes[];
   StringToCharArray(OpenAI_API_Key, api_bytes);
   uchar hash[];
   CryptEncode(CRYPT_HASH_SHA256, api_bytes, empty_key, hash);
   ArrayCopy(key, hash, 0, 0, 32);
   uchar decoded_aes[];
   int res_dec = CryptDecode(CRYPT_AES256, encoded_file, key, decoded_aes);
   if (res_dec <= 0) {
      Print("Failed to decrypt chats: ", GetLastError());
      CreateNewChat();
      return;
   }
   uchar decoded_zip[];
   int res_zip = CryptDecode(CRYPT_ARCH_ZIP, decoded_aes, empty_key, decoded_zip);
   if (res_zip <= 0) {
      Print("Failed to decompress chats: ", GetLastError());
      CreateNewChat();
      return;
   }
   string jsonStr = CharArrayToString(decoded_zip);
   char charArray[];
   int len = StringToCharArray(jsonStr, charArray, 0, WHOLE_ARRAY, CP_UTF8);
   JsonValue json;
   int index = 0;
   if (!json.DeserializeFromArray(charArray, len, index)) {
      Print("Failed to parse chats JSON");
      CreateNewChat();
      return;
   }
   if (json.m_type != JsonArray) {
      Print("Chats JSON not an array");
      CreateNewChat();
      return;
   }
   int size = ArraySize(json.m_children);
   ArrayResize(chats, size);
   int max_id = 0;
   for (int i = 0; i < size; i++) {
      JsonValue obj = json.m_children[i];
      chats[i].id = (int)obj["id"].ToInteger();
      chats[i].title = obj["title"].ToString();
      chats[i].history = obj["history"].ToString();
      max_id = MathMax(max_id, chats[i].id);
   }
   if (size > 0) {
      current_chat_id = chats[size - 1].id;
      current_title = chats[size - 1].title;
      conversationHistory = chats[size - 1].history;
   } else {
      CreateNewChat();
   }
}

void SaveChats() {
   JsonValue jsonArr;
   jsonArr.m_type = JsonArray;
   for (int i = 0; i < ArraySize(chats); i++) {
      JsonValue obj;
      obj.m_type = JsonObject;
      obj["id"] = chats[i].id;
      obj["title"] = chats[i].title;
      obj["history"] = chats[i].history;
      jsonArr.AddChild(obj);
   }
   string jsonStr = jsonArr.SerializeToString();
   uchar data[];
   StringToCharArray(jsonStr, data);
   uchar empty_key[];
   uchar zipped[];
   int res_zip = CryptEncode(CRYPT_ARCH_ZIP, data, empty_key, zipped);
   if (res_zip <= 0) {
      Print("Failed to compress chats: ", GetLastError());
      return;
   }
   uchar key[32];
   uchar api_bytes[];
   StringToCharArray(OpenAI_API_Key, api_bytes);
   uchar hash[];
   CryptEncode(CRYPT_HASH_SHA256, api_bytes, empty_key, hash);
   ArrayCopy(key, hash, 0, 0, 32);
   uchar encoded[];
   int res_enc = CryptEncode(CRYPT_AES256, zipped, key, encoded);
   if (res_enc <= 0) {
      Print("Failed to encrypt chats: ", GetLastError());
      return;
   }
   int handle = FileOpen(chatsFileName, FILE_WRITE | FILE_BIN);
   if (handle == INVALID_HANDLE) {
      Print("Failed to save chats: ", GetLastError());
      return;
   }
   FileWriteArray(handle, encoded, 0, res_enc);
   FileClose(handle);
}

int GetChatIndex(int id) {
   for (int i = 0; i < ArraySize(chats); i++) {
      if (chats[i].id == id) return i;
   }
   return -1;
}

void CreateNewChat() {
   int max_id = 0;
   for (int i = 0; i < ArraySize(chats); i++) {
      max_id = MathMax(max_id, chats[i].id);
   }
   int new_id = max_id + 1;
   int size = ArraySize(chats);
   ArrayResize(chats, size + 1);
   chats[size].id = new_id;
   chats[size].title = "Chat " + IntegerToString(new_id);
   chats[size].history = "";
   current_chat_id = new_id;
   current_title = chats[size].title;
   conversationHistory = "";
   SaveChats();
   UpdateSidebarDynamic();
   UpdateResponseDisplay();
   UpdatePromptDisplay();
   CreatePlaceholder();
   ChartRedraw();
}

void UpdateCurrentHistory() {
   int idx = GetChatIndex(current_chat_id);
   if (idx >= 0) {
      chats[idx].history = conversationHistory;
      chats[idx].title = current_title;
      SaveChats();
   }
}

void UpdateSidebarDynamic() {
   int total = ObjectsTotal(0, 0, -1);
   for (int j = total - 1; j >= 0; j--) {
      string name = ObjectName(0, j, 0, -1);
      if (StringFind(name, "ChatGPT_NewChatButton") == 0 || StringFind(name, "ChatGPT_ClearButton") == 0 || StringFind(name, "ChatGPT_HistoryButton") == 0 || StringFind(name, "ChatGPT_ChatLabel_") == 0 || StringFind(name, "ChatGPT_ChatBg_") == 0 || StringFind(name, "ChatGPT_SidebarLogo") == 0 || StringFind(name, "ChatGPT_NewChatIcon") == 0 || StringFind(name, "ChatGPT_NewChatLabel") == 0 || StringFind(name, "ChatGPT_ClearIcon") == 0 || StringFind(name, "ChatGPT_ClearLabel") == 0 || StringFind(name, "ChatGPT_HistoryIcon") == 0 || StringFind(name, "ChatGPT_HistoryLabel") == 0 || StringFind(name, "ChatGPT_ToggleButton") == 0) {
         ObjectDelete(0, name);
      }
   }
   ArrayResize(side_chat_bgs, 0);
   int sidebarX = g_dashboardX;
   int itemY = g_mainY + 10;
   string sidebar_logo_resource = sidebarExpanded ? ((StringLen(g_scaled_sidebar_resource) > 0) ? g_scaled_sidebar_resource : resourceImgLogo) : ((StringLen(g_scaled_sidebar_small) > 0) ? g_scaled_sidebar_small : resourceImgLogo);
   int logo_size = sidebarExpanded ? 81 : 30;
   createBitmapLabel("ChatGPT_SidebarLogo", sidebarX + (g_sidebarWidth - logo_size)/2, itemY, logo_size, logo_size, sidebar_logo_resource, clrNONE, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "ChatGPT_SidebarLogo", OBJPROP_ZORDER, 1);
   itemY += logo_size + 10;
   createButton("ChatGPT_NewChatButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrWhite, 11, new_chat_original_bg, clrRoyalBlue);
   ObjectSetInteger(0, "ChatGPT_NewChatButton", OBJPROP_ZORDER, 1);
   string newchat_icon_resource = (StringLen(g_scaled_newchat_resource) > 0) ? g_scaled_newchat_resource : resourceNewChat;
   int iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2;
   createBitmapLabel("ChatGPT_NewChatIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, newchat_icon_resource, clrNONE, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "ChatGPT_NewChatIcon", OBJPROP_ZORDER, 2);
   ObjectSetInteger(0, "ChatGPT_NewChatIcon", OBJPROP_SELECTABLE, false);
   if (sidebarExpanded) {
      createLabel("ChatGPT_NewChatLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "New Chat", clrWhite, 11, "Arial", CORNER_LEFT_UPPER);
      ObjectSetInteger(0, "ChatGPT_NewChatLabel", OBJPROP_ZORDER, 2);
      ObjectSetInteger(0, "ChatGPT_NewChatLabel", OBJPROP_SELECTABLE, false);
   }
   itemY += g_buttonHeight + 5;
   createButton("ChatGPT_ClearButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrWhite, 11, clear_original_bg, clrIndianRed);
   ObjectSetInteger(0, "ChatGPT_ClearButton", OBJPROP_ZORDER, 1);
   string clear_icon_resource = (StringLen(g_scaled_clear_resource) > 0) ? g_scaled_clear_resource : resourceClear;
   iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2;
   createBitmapLabel("ChatGPT_ClearIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, clear_icon_resource, clrNONE, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "ChatGPT_ClearIcon", OBJPROP_ZORDER, 2);
   ObjectSetInteger(0, "ChatGPT_ClearIcon", OBJPROP_SELECTABLE, false);
   if (sidebarExpanded) {
      createLabel("ChatGPT_ClearLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "Clear", clrWhite, 11, "Arial", CORNER_LEFT_UPPER);
      ObjectSetInteger(0, "ChatGPT_ClearLabel", OBJPROP_ZORDER, 2);
      ObjectSetInteger(0, "ChatGPT_ClearLabel", OBJPROP_SELECTABLE, false);
   }
   itemY += g_buttonHeight + 10;
   createButton("ChatGPT_HistoryButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, "", clrBlack, 12, history_original_bg, clrGray);
   ObjectSetInteger(0, "ChatGPT_HistoryButton", OBJPROP_ZORDER, 1);
   string history_icon_resource = (StringLen(g_scaled_history_resource) > 0) ? g_scaled_history_resource : resourceHistory;
   iconX = sidebarExpanded ? sidebarX + 5 + 10 : sidebarX + 5 + (g_sidebarWidth - 10 - 30)/2;
   createBitmapLabel("ChatGPT_HistoryIcon", iconX, itemY + (g_buttonHeight - 30)/2, 30, 30, history_icon_resource, clrNONE, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "ChatGPT_HistoryIcon", OBJPROP_ZORDER, 2);
   ObjectSetInteger(0, "ChatGPT_HistoryIcon", OBJPROP_SELECTABLE, false);
   if (sidebarExpanded) {
      createLabel("ChatGPT_HistoryLabel", sidebarX + 5 + 10 + 30 + 5, itemY + (g_buttonHeight - 20)/2, "History", clrBlack, 12, "Arial", CORNER_LEFT_UPPER);
      ObjectSetInteger(0, "ChatGPT_HistoryLabel", OBJPROP_ZORDER, 2);
      ObjectSetInteger(0, "ChatGPT_HistoryLabel", OBJPROP_SELECTABLE, false);
   }
   itemY += g_buttonHeight + 5;
   if (sidebarExpanded) {
      int numChats = MathMin(ArraySize(chats), 7);
      int chatIndices[7];
      for (int i = 0; i < numChats; i++) {
         chatIndices[i] = ArraySize(chats) - 1 - i;
      }
      for (int i = 0; i < numChats; i++) {
         int chatIdx = chatIndices[i];
         string hashed_id = EncodeID(chats[chatIdx].id);
         string fullText = chats[chatIdx].title + " > " + hashed_id;
         string labelText = fullText;
         if (StringLen(fullText) > 19) {
            labelText = StringSubstr(fullText, 0, 16) + "...";
         }
         string bgName = "ChatGPT_ChatBg_" + hashed_id;
         createRecLabel(bgName, sidebarX + 5 + 10, itemY, g_sidebarWidth - 10 - 10, 25, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID);
         ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 1);
         color textColor = (chats[chatIdx].id == current_chat_id) ? clrBlue : clrBlack;
         createLabel("ChatGPT_ChatLabel_" + hashed_id, sidebarX + 10 + 10, itemY + 3, labelText, textColor, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
         ObjectSetInteger(0, "ChatGPT_ChatLabel_" + hashed_id, OBJPROP_ZORDER, 2);
         int size = ArraySize(side_chat_bgs);
         ArrayResize(side_chat_bgs, size + 1);
         side_chat_bgs[size] = bgName;
         itemY += 25 + 5;
      }
   }
   itemY += 10;
   string toggle_text = sidebarExpanded ? "<<" : ">>";
   createButton("ChatGPT_ToggleButton", sidebarX + 5, itemY, g_sidebarWidth - 10, g_buttonHeight, toggle_text, clrBlack, 12, toggle_original_bg, clrGray);
   ObjectSetInteger(0, "ChatGPT_ToggleButton", OBJPROP_ZORDER, 1);
   ChartRedraw();
}

void ShowSmallHistoryPopup(int button_y) {
   ArrayResize(small_popup_objects, 0);
   ArrayResize(small_chat_bgs, 0);
   long history_x = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XDISTANCE);
   long history_w = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XSIZE);
   int popup_x = (int)(history_x + history_w);
   int popup_y = button_y;
   int popup_w = 200;
   int item_height = 25;
   int num_chats = MathMin(ArraySize(chats), 7);
   int items_h = num_chats * (item_height + 5) - 5;
   int popup_h = items_h + g_buttonHeight + 20;
   string popup_bg = "ChatGPT_SmallHistoryBg";
   createRecLabel(popup_bg, popup_x, popup_y, popup_w, popup_h, clrWhite, 1, clrLightGray);
   int size = ArraySize(small_popup_objects);
   ArrayResize(small_popup_objects, size + 1);
   small_popup_objects[size] = popup_bg;
   int item_y = popup_y + 10;
   for (int i = 0; i < num_chats; i++) {
      int chatIdx = ArraySize(chats) - 1 - i;
      string hashed_id = EncodeID(chats[chatIdx].id);
      string labelText = chats[chatIdx].title;
      if (StringLen(labelText) > 25) labelText = StringSubstr(labelText, 0, 22) + "...";
      string bgName = "ChatGPT_SmallChatBg_" + hashed_id;
      createRecLabel(bgName, popup_x + 10, item_y, popup_w - 20, item_height, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID);
      string labelName = "ChatGPT_SmallChatLabel_" + hashed_id;
      createLabel(labelName, popup_x + 20, item_y + 3, labelText, clrBlack, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      size = ArraySize(small_popup_objects);
      ArrayResize(small_popup_objects, size + 2);
      small_popup_objects[size] = bgName;
      small_popup_objects[size + 1] = labelName;
      int bg_size = ArraySize(small_chat_bgs);
      ArrayResize(small_chat_bgs, bg_size + 1);
      small_chat_bgs[bg_size] = bgName;
      item_y += item_height + 5;
   }
   string see_more = "ChatGPT_SeeMoreButton";
   createButton(see_more, popup_x + 10, item_y, popup_w - 20, g_buttonHeight, "See All", clrWhite, 11, see_more_original_bg, clrDarkBlue);
   size = ArraySize(small_popup_objects);
   ArrayResize(small_popup_objects, size + 1);
   small_popup_objects[size] = see_more;
   small_popup_x = popup_x;
   small_popup_y = popup_y;
   small_popup_w = popup_w;
   small_popup_h = popup_h;
   showing_small_history_popup = true;
   just_opened_small = true;
}

void DeleteSmallHistoryPopup() {
   for (int i = 0; i < ArraySize(small_popup_objects); i++) {
      ObjectDelete(0, small_popup_objects[i]);
   }
   ArrayResize(small_popup_objects, 0);
   ArrayResize(small_chat_bgs, 0);
   showing_small_history_popup = false;
}

void ShowBigHistoryPopup() {
   ArrayResize(big_popup_objects, 0);
   ArrayResize(big_chat_bgs, 0);
   big_scroll_pos = 0;
   big_scroll_visible = false;
   int popup_w = g_mainWidth - 20;
   int item_height = 25;
   int num_chats = ArraySize(chats);
   big_total_height = num_chats * (item_height + 5) - 5;
   int max_h = g_displayHeight - 20;
   int content_h = max_h - 40 - 10;
   big_visible_height = content_h - 35;
   int popup_h = MathMin(big_total_height +40+20 + 10, max_h);
   big_popup_w = popup_w;
   big_popup_h = popup_h;
   big_popup_x = g_mainContentX + 10;
   big_popup_y = g_mainY + g_headerHeight + g_padding + 10;
   string popup_bg = "ChatGPT_BigHistoryBg";
   createRecLabel(popup_bg, big_popup_x, big_popup_y, popup_w, popup_h, C'250,250,250', 1, clrDodgerBlue);
   int size = ArraySize(big_popup_objects);
   ArrayResize(big_popup_objects, size + 1);
   big_popup_objects[size] = popup_bg;
   string close_button = "ChatGPT_BigCloseButton";
   createButton(close_button, big_popup_x + popup_w - 80 - 10 + 40, big_popup_y + 5, 40, 30, "r", clrRed, 13, big_close_original_bg, clrGray,"Webdings");
   size = ArraySize(big_popup_objects);
   ArrayResize(big_popup_objects, size + 1);
   big_popup_objects[size] = close_button;
   int content_y = big_popup_y + 40;
   string content_bg = "ChatGPT_BigContentBg";
   createRecLabel(content_bg, big_popup_x + 10, content_y, popup_w - 20, content_h, clrWhite, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID);
   size = ArraySize(big_popup_objects);
   ArrayResize(big_popup_objects, size + 1);
   big_popup_objects[size] = content_bg;
   bool need_big_scroll = big_total_height > big_visible_height;
   int reserved_w = need_big_scroll ? 16 : 0;
   int item_w = popup_w - 20 - 20 - reserved_w;
   UpdateBigHistoryDisplay();
   if (need_big_scroll) {
      CreateBigScrollbar();
      big_scroll_visible = true;
      UpdateBigSliderPosition();
      UpdateBigButtonColors();
   }
   showing_big_history_popup = true;
   just_opened_big = true;
   ChartRedraw();
}

void CreateBigScrollbar() {
   int scrollbar_x = big_popup_x + big_popup_w - 10 - 16;
   int scrollbar_y = big_popup_y + 40 + 16;
   int scrollbar_width = 16;
   int scrollbar_height = big_popup_h - 40 - 10 - 2 * 16;
   int button_size = 16;
   createRecLabel(BIG_SCROLL_LEADER, scrollbar_x, scrollbar_y, scrollbar_width, scrollbar_height, C'220,220,220', 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createRecLabel(BIG_SCROLL_UP_REC, scrollbar_x, big_popup_y + 40, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(BIG_SCROLL_UP_LABEL, scrollbar_x + 2, big_popup_y + 40 + -2, CharToString(0x35), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   createRecLabel(BIG_SCROLL_DOWN_REC, scrollbar_x, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size, scrollbar_width, button_size, clrGainsboro, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   createLabel(BIG_SCROLL_DOWN_LABEL, scrollbar_x + 2, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size + -2, CharToString(0x36), clrDimGray, getFontSizeByDPI(10), "Webdings", CORNER_LEFT_UPPER);
   big_slider_height = CalculateBigSliderHeight();
   createRecLabel(BIG_SCROLL_SLIDER, scrollbar_x, big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size - big_slider_height, scrollbar_width, big_slider_height, clrSilver, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID, CORNER_LEFT_UPPER);
   int size = ArraySize(big_popup_objects);
   ArrayResize(big_popup_objects, size + 6);
   big_popup_objects[size] = BIG_SCROLL_LEADER;
   big_popup_objects[size + 1] = BIG_SCROLL_UP_REC;
   big_popup_objects[size + 2] = BIG_SCROLL_UP_LABEL;
   big_popup_objects[size + 3] = BIG_SCROLL_DOWN_REC;
   big_popup_objects[size + 4] = BIG_SCROLL_DOWN_LABEL;
   big_popup_objects[size + 5] = BIG_SCROLL_SLIDER;
}

int CalculateBigSliderHeight() {
   int scroll_area_height = big_popup_h - 40 - 25 - 2 * 16;
   int slider_min_height = 20;
   if (big_total_height <= big_visible_height) return scroll_area_height;
   double visible_ratio = (double)big_visible_height / big_total_height;
   int height = (int)MathFloor(scroll_area_height * visible_ratio);
   return MathMax(slider_min_height, height);
}

void UpdateBigSliderPosition() {
   int scrollbar_x = big_popup_x + big_popup_w - 10 - 16;
   int scrollbar_y = big_popup_y + 40 + 16;
   int scroll_area_height = big_popup_h - 40 - 25 - 2 * 16;
   int max_scroll = MathMax(0, big_total_height - big_visible_height);
   if (max_scroll <= 0) return;
   double scroll_ratio = (double)big_scroll_pos / max_scroll;
   int scroll_area_y_max = scrollbar_y + scroll_area_height - big_slider_height;
   int scroll_area_y_min = scrollbar_y;
   int new_y = scroll_area_y_min + (int)(scroll_ratio * (scroll_area_y_max - scroll_area_y_min));
   new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
   ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
}

void UpdateBigButtonColors() {
   int max_scroll = MathMax(0, big_total_height - big_visible_height);
   if (big_scroll_pos == 0) {
      ObjectSetInteger(0, BIG_SCROLL_UP_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, BIG_SCROLL_UP_LABEL, OBJPROP_COLOR, clrDimGray);
   }
   if (big_scroll_pos == max_scroll) {
      ObjectSetInteger(0, BIG_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrSilver);
   } else {
      ObjectSetInteger(0, BIG_SCROLL_DOWN_LABEL, OBJPROP_COLOR, clrDimGray);
   }
}

void BigScrollUp() {
   if (big_scroll_pos > 0) {
      big_scroll_pos = MathMax(0, big_scroll_pos - 30);
      UpdateBigHistoryDisplay();
      if (big_scroll_visible) {
         UpdateBigSliderPosition();
         UpdateBigButtonColors();
      }
   }
}

void BigScrollDown() {
   int max_scroll = MathMax(0, big_total_height - big_visible_height);
   if (big_scroll_pos < max_scroll) {
      big_scroll_pos = MathMin(max_scroll, big_scroll_pos + 30);
      UpdateBigHistoryDisplay();
      if (big_scroll_visible) {
         UpdateBigSliderPosition();
         UpdateBigButtonColors();
      }
   }
}

void UpdateBigHistoryDisplay() {
   int total = ObjectsTotal(0, 0, -1);
   for (int j = total - 1; j >= 0; j--) {
      string name = ObjectName(0, j, 0, -1);
      if (StringFind(name, "ChatGPT_BigChatBg_") == 0 || StringFind(name, "ChatGPT_BigChatLabel_") == 0) {
         ObjectDelete(0, name);
      }
   }
   int content_y = big_popup_y + 40;
   int content_h = big_popup_h - 40 - 10;
   int item_height = 25;
   int num_chats = ArraySize(chats);
   big_total_height = num_chats * (item_height + 5) - 5;
   bool need_big_scroll = big_total_height > big_visible_height;
   int reserved_w = need_big_scroll ? 16 : 0;
   int item_w = big_popup_w - 20 - 20 - reserved_w;
   int item_y = content_y + 10 - big_scroll_pos;
   int end_y = content_y + content_h - 25;
   int start_idx = 0;
   int current_h = 0;
   for (int i = 0; i < num_chats; i++) {
      if (current_h >= big_scroll_pos) {
         start_idx = i;
         item_y = content_y + 10 + (current_h - big_scroll_pos);
         break;
      }
      current_h += item_height + 5;
   }
   int visible_count = 0;
   current_h = 0;
   for (int i = start_idx; i < num_chats; i++) {
      if (current_h + item_height > big_visible_height) break;
      current_h += item_height + 5;
      visible_count++;
   }
   for (int i = 0; i < visible_count; i++) {
      int chatIdx = num_chats - 1 - (start_idx + i);
      string hashed_id = EncodeID(chats[chatIdx].id);
      string fullText = chats[chatIdx].title + " > " + hashed_id;
      string labelText = fullText;
      if (StringLen(fullText) > 35) {
         labelText = StringSubstr(fullText, 0, 32) + "...";
      }
      string bgName = "ChatGPT_BigChatBg_" + hashed_id;
      if (item_y >= content_y + 10 && item_y < end_y) {
         createRecLabel(bgName, big_popup_x + 20, item_y, item_w, item_height, clrBeige, 1, DarkenColor(clrBeige, 0.9), BORDER_FLAT, STYLE_SOLID);
      }
      string labelName = "ChatGPT_BigChatLabel_" + hashed_id;
      if (item_y >= content_y + 10 && item_y < end_y) {
         createLabel(labelName, big_popup_x + 30, item_y + 3, labelText, clrBlack, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      }
      int size = ArraySize(big_popup_objects);
      ArrayResize(big_popup_objects, size + 2);
      big_popup_objects[size] = bgName;
      big_popup_objects[size + 1] = labelName;
      int bg_size = ArraySize(big_chat_bgs);
      ArrayResize(big_chat_bgs, bg_size + 1);
      big_chat_bgs[bg_size] = bgName;
      item_y += item_height + 5;
   }
   ChartRedraw();
}

void DeleteBigHistoryPopup() {
   for (int i = 0; i < ArraySize(big_popup_objects); i++) {
      ObjectDelete(0, big_popup_objects[i]);
   }
   ArrayResize(big_popup_objects, 0);
   ArrayResize(big_chat_bgs, 0);
   showing_big_history_popup = false;
   big_scroll_visible = false;
}

int OnInit() {
   button_darker_bg = DarkenColor(button_original_bg);
   clear_darker_bg = DarkenColor(clear_original_bg);
   new_chat_darker_bg = DarkenColor(new_chat_original_bg);
   chart_button_darker_bg = DarkenColor(chart_button_bg);
   close_darker_bg = DarkenColor(close_original_bg);
   toggle_darker_bg = DarkenColor(toggle_original_bg);
   history_darker_bg = DarkenColor(history_original_bg, 0.9);
   see_more_darker_bg = DarkenColor(see_more_original_bg);
   big_close_darker_bg = DarkenColor(big_close_original_bg);
   logFileHandle = FileOpen(LogFileName, FILE_READ | FILE_WRITE | FILE_TXT);
   if (logFileHandle == INVALID_HANDLE) {
      Print("Failed to open log file: ", GetLastError());
      return(INIT_FAILED);
   }
   FileSeek(logFileHandle, 0, SEEK_END);
   uint img_pixels[];
   uint orig_width = 0, orig_height = 0;
   bool image_loaded = ResourceReadImage(resourceImg, img_pixels, orig_width, orig_height);
   if (image_loaded && orig_width > 0 && orig_height > 0) {
      ScaleImage(img_pixels, (int)orig_width, (int)orig_height, 104, 40);
      g_scaled_image_resource = "::ChatGPT_HeaderImageScaled";
      if (ResourceCreate(g_scaled_image_resource, img_pixels, 104, 40, 0, 0, 104, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled image resource created successfully");
      } else {
         Print("Failed to create scaled image resource");
      }
   } else {
      Print("Failed to load original image resource");
   }
   uint img_pixels_logo[];
   uint orig_width_logo = 0, orig_height_logo = 0;
   bool image_loaded_logo = ResourceReadImage(resourceImgLogo, img_pixels_logo, orig_width_logo, orig_height_logo);
   if (image_loaded_logo && orig_width_logo > 0 && orig_height_logo > 0) {
      ScaleImage(img_pixels_logo, (int)orig_width_logo, (int)orig_height_logo, 81, 81);
      g_scaled_sidebar_resource = "::ChatGPT_SidebarImageScaled";
      if (ResourceCreate(g_scaled_sidebar_resource, img_pixels_logo, 81, 81, 0, 0, 81, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled sidebar image resource created successfully");
      } else {
         Print("Failed to create scaled sidebar image resource");
      }
      uint img_pixels_logo_small[];
      ArrayCopy(img_pixels_logo_small, img_pixels_logo);
      ScaleImage(img_pixels_logo_small, 81, 81, 30, 30);
      g_scaled_sidebar_small = "::ChatGPT_SidebarImageSmall";
      if (ResourceCreate(g_scaled_sidebar_small, img_pixels_logo_small, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled small sidebar image resource created successfully");
      } else {
         Print("Failed to create scaled small sidebar image resource");
      }
   } else {
      Print("Failed to load sidebar image resource");
   }
   uint img_pixels_newchat[];
   uint orig_width_newchat = 0, orig_height_newchat = 0;
   bool image_loaded_newchat = ResourceReadImage(resourceNewChat, img_pixels_newchat, orig_width_newchat, orig_height_newchat);
   if (image_loaded_newchat && orig_width_newchat > 0 && orig_height_newchat > 0) {
      ScaleImage(img_pixels_newchat, (int)orig_width_newchat, (int)orig_height_newchat, 30, 30);
      g_scaled_newchat_resource = "::ChatGPT_NewChatIconScaled";
      if (ResourceCreate(g_scaled_newchat_resource, img_pixels_newchat, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled new chat icon resource created successfully");
      } else {
         Print("Failed to create scaled new chat icon resource");
      }
   } else {
      Print("Failed to load new chat icon resource");
   }
   uint img_pixels_clear[];
   uint orig_width_clear = 0, orig_height_clear = 0;
   bool image_loaded_clear = ResourceReadImage(resourceClear, img_pixels_clear, orig_width_clear, orig_height_clear);
   if (image_loaded_clear && orig_width_clear > 0 && orig_height_clear > 0) {
      ScaleImage(img_pixels_clear, (int)orig_width_clear, (int)orig_height_clear, 30, 30);
      g_scaled_clear_resource = "::ChatGPT_ClearIconScaled";
      if (ResourceCreate(g_scaled_clear_resource, img_pixels_clear, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled clear icon resource created successfully");
      } else {
         Print("Failed to create scaled clear icon resource");
      }
   } else {
      Print("Failed to load clear icon resource");
   }
   uint img_pixels_history[];
   uint orig_width_history = 0, orig_height_history = 0;
   bool image_loaded_history = ResourceReadImage(resourceHistory, img_pixels_history, orig_width_history, orig_height_history);
   if (image_loaded_history && orig_width_history > 0 && orig_height_history > 0) {
      ScaleImage(img_pixels_history, (int)orig_width_history, (int)orig_height_history, 30, 30);
      g_scaled_history_resource = "::ChatGPT_HistoryIconScaled";
      if (ResourceCreate(g_scaled_history_resource, img_pixels_history, 30, 30, 0, 0, 30, COLOR_FORMAT_ARGB_NORMALIZE)) {
         Print("Scaled history icon resource created successfully");
      } else {
         Print("Failed to create scaled history icon resource");
      }
   } else {
      Print("Failed to load history icon resource");
   }
   g_sidebarWidth = sidebarExpanded ? expandedSidebarWidth : contractedSidebarWidth;
   g_mainContentX = g_dashboardX + g_sidebarWidth;
   g_dashboardWidth = g_sidebarWidth + g_mainWidth;
   g_mainHeight = g_headerHeight + 2 * g_padding + g_displayHeight + g_footerHeight;
   createRecLabel("ChatGPT_DashboardBg", g_dashboardX, g_mainY, g_dashboardWidth, g_mainHeight, clrWhite, 1, clrLightGray);
   ObjectSetInteger(0, "ChatGPT_DashboardBg", OBJPROP_ZORDER, 0);
   createRecLabel("ChatGPT_SidebarBg", g_dashboardX+2, g_mainY+2, g_sidebarWidth - 2 - 1, g_mainHeight - 2 - 2, clrGainsboro, 1, clrNONE);
   ObjectSetInteger(0, "ChatGPT_SidebarBg", OBJPROP_ZORDER, 0);
   LoadChats();
   UpdateSidebarDynamic();
   CreateDashboard();
   UpdateResponseDisplay();
   UpdatePromptDisplay();
   CreatePlaceholder();
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
   ChartSetInteger(0, CHART_EVENT_MOUSE_WHEEL, true);
   ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
   UpdateCurrentHistory();
   ObjectsDeleteAll(0, "ChatGPT_");
   DeleteScrollbar();
   DeletePromptScrollbar();
   if (StringLen(g_scaled_image_resource) > 0) {
      ResourceFree(g_scaled_image_resource);
   }
   if (StringLen(g_scaled_sidebar_resource) > 0) {
      ResourceFree(g_scaled_sidebar_resource);
   }
   if (StringLen(g_scaled_sidebar_small) > 0) {
      ResourceFree(g_scaled_sidebar_small);
   }
   if (StringLen(g_scaled_newchat_resource) > 0) {
      ResourceFree(g_scaled_newchat_resource);
   }
   if (StringLen(g_scaled_clear_resource) > 0) {
      ResourceFree(g_scaled_clear_resource);
   }
   if (StringLen(g_scaled_history_resource) > 0) {
      ResourceFree(g_scaled_history_resource);
   }
   if (logFileHandle != INVALID_HANDLE) {
      FileClose(logFileHandle);
   }
}

void OnTick() {
}

void HideDashboard() {
   dashboard_visible = false;
   for (int i = 0; i < objCount; i++) {
      ObjectDelete(0, dashboardObjects[i]);
   }
   DeleteScrollbar();
   DeletePromptScrollbar();
   ObjectDelete(0, "ChatGPT_DashboardBg");
   ObjectDelete(0, "ChatGPT_SidebarBg");
   ChartRedraw();
}

void ShowDashboard() {
   dashboard_visible = true;
   for (int i = 0; i < objCount; i++) {
      ObjectSetInteger(0, dashboardObjects[i], OBJPROP_HIDDEN, false);
   }
   if (scroll_visible) {
      ObjectSetInteger(0, SCROLL_LEADER, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_HIDDEN, false);
   }
   if (p_scroll_visible) {
      ObjectSetInteger(0, P_SCROLL_LEADER, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, P_SCROLL_UP_REC, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, P_SCROLL_UP_LABEL, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, P_SCROLL_DOWN_REC, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, P_SCROLL_DOWN_LABEL, OBJPROP_HIDDEN, false);
      ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_HIDDEN, false);
   }
   UpdateResponseDisplay();
   UpdatePromptDisplay();
   ChartRedraw();
}

void UpdateDashboardPositions() {
   ObjectSetInteger(0, "ChatGPT_DashboardBg", OBJPROP_XSIZE, g_dashboardWidth);
   ObjectSetInteger(0, "ChatGPT_SidebarBg", OBJPROP_XSIZE, g_sidebarWidth - 2 - 1);
   int diff = g_mainContentX - (g_dashboardX + (sidebarExpanded ? contractedSidebarWidth : expandedSidebarWidth));
   for (int i = 0; i < objCount; i++) {
      string obj = dashboardObjects[i];
      if (ObjectFind(0, obj) >= 0) {
         long curX = ObjectGetInteger(0, obj, OBJPROP_XDISTANCE);
         ObjectSetInteger(0, obj, OBJPROP_XDISTANCE, curX + diff);
      }
   }
   uint date_wid, date_hei;
   string dateStr = TimeToString(TimeTradeServer(), TIME_MINUTES);
   TextSetFont("Arial", 12);
   TextGetSize(dateStr, date_wid, date_hei);
   int dateX = g_mainContentX + g_mainWidth / 2 - (int)(date_wid / 2) + 20;
   int dateY = (int)ObjectGetInteger(0, "ChatGPT_DateLabel", OBJPROP_YDISTANCE);
   ObjectSetInteger(0, "ChatGPT_DateLabel", OBJPROP_XDISTANCE, dateX);
   int closeWidth = 100;
   int closeX = g_mainContentX + g_mainWidth - closeWidth - g_sidePadding;
   ObjectSetInteger(0, "ChatGPT_CloseButton", OBJPROP_XDISTANCE, closeX);
   int promptW = g_mainWidth - 2 * g_sidePadding;
   int editX = g_mainContentX + g_sidePadding + g_textPadding;
   g_editW = promptW - 2 * g_textPadding;
   ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XDISTANCE, editX);
   ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XSIZE, g_editW);
   int buttonW = 140;
   int chartX = g_mainContentX + g_sidePadding;
   int sendX = g_mainContentX + g_mainWidth - g_sidePadding - buttonW;
   ObjectSetInteger(0, "ChatGPT_GetChartButton", OBJPROP_XDISTANCE, chartX);
   ObjectSetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_XDISTANCE, sendX);
   int displayX = g_mainContentX + g_sidePadding;
   int displayW = g_mainWidth - 2 * g_sidePadding;
   ObjectSetInteger(0, "ChatGPT_ResponseBg", OBJPROP_XDISTANCE, displayX);
   ObjectSetInteger(0, "ChatGPT_ResponseBg", OBJPROP_XSIZE, displayW);
   ObjectSetInteger(0, "ChatGPT_PromptBg", OBJPROP_XDISTANCE, displayX);
   ObjectSetInteger(0, "ChatGPT_PromptBg", OBJPROP_XSIZE, displayW);
   int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding;
   ObjectSetInteger(0, "ChatGPT_FooterBg", OBJPROP_XDISTANCE, g_mainContentX);
   ObjectSetInteger(0, "ChatGPT_FooterBg", OBJPROP_XSIZE, g_mainWidth);
   if (ObjectFind(0, "ChatGPT_PromptPlaceholder") >= 0) {
      int labelY = (int)ObjectGetInteger(0, "ChatGPT_PromptPlaceholder", OBJPROP_YDISTANCE);
      ObjectSetInteger(0, "ChatGPT_PromptPlaceholder", OBJPROP_XDISTANCE, editX + 2);
   }
   if (scroll_visible) {
      int displayY = g_mainY + g_headerHeight + g_padding;
      int scrollbar_x = displayX + displayW - 16;
      int button_size = 16;
      ObjectSetInteger(0, SCROLL_LEADER, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, SCROLL_UP_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2);
      ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, SCROLL_DOWN_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2);
      ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE, scrollbar_x);
      UpdateSliderPosition();
   }
   if (p_scroll_visible) {
      int promptX = g_mainContentX + g_sidePadding;
      int promptY = footerY + g_margin;
      int promptW = g_mainWidth - 2 * g_sidePadding;
      int scrollbar_x = promptX + promptW - 16;
      int button_size = 16;
      ObjectSetInteger(0, P_SCROLL_LEADER, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, P_SCROLL_UP_REC, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, P_SCROLL_UP_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2);
      ObjectSetInteger(0, P_SCROLL_DOWN_REC, OBJPROP_XDISTANCE, scrollbar_x);
      ObjectSetInteger(0, P_SCROLL_DOWN_LABEL, OBJPROP_XDISTANCE, scrollbar_x + 2);
      ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_XDISTANCE, scrollbar_x);
      UpdatePromptSliderPosition();
   }
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
   int displayX = g_mainContentX + g_sidePadding;
   int displayY = g_mainY + g_headerHeight + g_padding;
   int displayW = g_mainWidth - 2 * g_sidePadding;
   int displayH = g_displayHeight;
   int footerY = displayY + g_displayHeight + g_padding;
   int promptY = footerY + g_margin;
   int promptH = g_promptHeight;
   int closeX = g_mainContentX + g_mainWidth - 100 - g_sidePadding;
   int closeY = g_mainY + 4;
   int closeW = 100;
   int closeH = g_headerHeight - 8;
   int buttonsY = promptY + g_promptHeight + g_margin;
   int buttonW = 140;
   int chartX = g_mainContentX + g_sidePadding;
   int sendX = g_mainContentX + g_mainWidth - g_sidePadding - buttonW;
   int editY = promptY + g_promptHeight - g_editHeight - 5;
   int editX = displayX + g_textPadding;
   bool need_scroll = g_total_height > g_visible_height;
   bool p_need_scroll = p_total_height > p_visible_height;
   if (id == CHARTEVENT_CLICK) {
      int clickX = (int)lparam;
      int clickY = (int)dparam;
      if (showing_big_history_popup) {
         if (just_opened_big) {
            just_opened_big = false;
         } else if (clickX < big_popup_x || clickX > big_popup_x + big_popup_w || clickY < big_popup_y || clickY > big_popup_y + big_popup_h) {
            DeleteBigHistoryPopup();
            ChartRedraw();
         }
      }
      if (showing_small_history_popup) {
         if (just_opened_small) {
            just_opened_small = false;
         } else if (clickX < small_popup_x || clickX > small_popup_x + small_popup_w || clickY < small_popup_y || clickY > small_popup_y + small_popup_h) {
            DeleteSmallHistoryPopup();
            ChartRedraw();
         }
      }
      return;
   }
   if (id == CHARTEVENT_OBJECT_CLICK) {
      if (StringFind(sparam, "ChatGPT_ChatLabel_") == 0 || StringFind(sparam, "ChatGPT_SmallChatLabel_") == 0 || StringFind(sparam, "ChatGPT_BigChatLabel_") == 0) {
         string prefix = "";
         if (StringFind(sparam, "ChatGPT_ChatLabel_") == 0) prefix = "ChatGPT_ChatLabel_";
         else if (StringFind(sparam, "ChatGPT_SmallChatLabel_") == 0) prefix = "ChatGPT_SmallChatLabel_";
         else if (StringFind(sparam, "ChatGPT_BigChatLabel_") == 0) prefix = "ChatGPT_BigChatLabel_";
         string hashed_id = StringSubstr(sparam, StringLen(prefix));
         int new_id = DecodeID(hashed_id);
         int idx = GetChatIndex(new_id);
         if (idx >= 0 && new_id != current_chat_id) {
            UpdateCurrentHistory();
            current_chat_id = new_id;
            current_title = chats[idx].title;
            conversationHistory = chats[idx].history;
            if (showing_small_history_popup) DeleteSmallHistoryPopup();
            if (showing_big_history_popup) DeleteBigHistoryPopup();
            UpdateResponseDisplay();
            UpdateSidebarDynamic();
            ChartRedraw();
         }
         return;
      }
   }
   if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SendPromptButton") {
      SubmitMessage(currentPrompt);
      currentPrompt = "";
      UpdatePromptDisplay();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_GetChartButton") {
      GetAndAppendChartData();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_CloseButton") {
      HideDashboard();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_ClearButton") {
      conversationHistory = "";
      currentPrompt = "";
      scroll_pos = 0;
      p_scroll_pos = 0;
      prev_scroll_pos = -1;
      UpdateCurrentHistory();
      UpdateResponseDisplay();
      UpdatePromptDisplay();
      CreatePlaceholder();
      ChartRedraw();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_NewChatButton") {
      CreateNewChat();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_ToggleButton") {
      sidebarExpanded = !sidebarExpanded;
      g_sidebarWidth = sidebarExpanded ? expandedSidebarWidth : contractedSidebarWidth;
      g_mainContentX = g_dashboardX + g_sidebarWidth;
      g_dashboardWidth = g_sidebarWidth + g_mainWidth;
      if (showing_big_history_popup) DeleteBigHistoryPopup();
      if (showing_small_history_popup) DeleteSmallHistoryPopup();
      UpdateSidebarDynamic();
      UpdateDashboardPositions();
      UpdateResponseDisplay();
      UpdatePromptDisplay();
      ChartRedraw();
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_HistoryButton" && sidebarExpanded) {
      ShowBigHistoryPopup();
      just_opened_big = true;
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_SeeMoreButton") {
      DeleteSmallHistoryPopup();
      ShowBigHistoryPopup();
      just_opened_big = true;
   } else if (id == CHARTEVENT_OBJECT_CLICK && sparam == "ChatGPT_BigCloseButton") {
      DeleteBigHistoryPopup();
      ChartRedraw();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == SCROLL_UP_REC || sparam == SCROLL_UP_LABEL)) {
      ScrollUp();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == SCROLL_DOWN_REC || sparam == SCROLL_DOWN_LABEL)) {
      ScrollDown();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == P_SCROLL_UP_REC || sparam == P_SCROLL_UP_LABEL)) {
      PromptScrollUp();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == P_SCROLL_DOWN_REC || sparam == P_SCROLL_DOWN_LABEL)) {
      PromptScrollDown();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == BIG_SCROLL_UP_REC || sparam == BIG_SCROLL_UP_LABEL)) {
      BigScrollUp();
   } else if (id == CHARTEVENT_OBJECT_CLICK && (sparam == BIG_SCROLL_DOWN_REC || sparam == BIG_SCROLL_DOWN_LABEL)) {
      BigScrollDown();
   } else if (id == CHARTEVENT_OBJECT_ENDEDIT && sparam == "ChatGPT_PromptEdit") {
      string text = (string)ObjectGetString(0, sparam, OBJPROP_TEXT);
      if (StringLen(text) > 0) {
         if (StringLen(currentPrompt) > 0 && StringGetCharacter(currentPrompt, StringLen(currentPrompt) - 1) != ' ') {
            currentPrompt += " ";
         }
         currentPrompt += text;
         ObjectSetString(0, sparam, OBJPROP_TEXT, "");
      }
      string edit_text = (string)ObjectGetString(0, sparam, OBJPROP_TEXT);
      if (StringLen(currentPrompt) == 0 && StringLen(edit_text) == 0) {
         CreatePlaceholder();
      } else {
         DeletePlaceholder();
      }
      UpdatePromptDisplay();
      p_scroll_pos = MathMax(0, p_total_height - p_visible_height);
      if (p_scroll_visible) {
         UpdatePromptSliderPosition();
         UpdatePromptButtonColors();
      }
      ChartRedraw();
   } else if (id == CHARTEVENT_MOUSE_MOVE) {
      int mouseX = (int)lparam;
      int mouseY = (int)dparam;
      long chart_x = ObjectGetInteger(0, "ChatGPT_GetChartButton", OBJPROP_XDISTANCE);
      long chart_y = ObjectGetInteger(0, "ChatGPT_GetChartButton", OBJPROP_YDISTANCE);
      long chart_w = ObjectGetInteger(0, "ChatGPT_GetChartButton", OBJPROP_XSIZE);
      long chart_h = ObjectGetInteger(0, "ChatGPT_GetChartButton", OBJPROP_YSIZE);
      bool isOverChart = (mouseX >= chart_x && mouseX <= chart_x + chart_w && mouseY >= chart_y && mouseY <= chart_y + chart_h);
      if (isOverChart && !chart_hover) {
         ObjectSetInteger(0, "ChatGPT_GetChartButton", OBJPROP_BGCOLOR, chart_button_darker_bg);
         chart_hover = true;
         ChartRedraw();
      } else if (!isOverChart && chart_hover) {
         ObjectSetInteger(0, "ChatGPT_GetChartButton", OBJPROP_BGCOLOR, chart_button_bg);
         chart_hover = false;
         ChartRedraw();
      }
      long send_x = ObjectGetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_XDISTANCE);
      long send_y = ObjectGetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_YDISTANCE);
      long send_w = ObjectGetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_XSIZE);
      long send_h = ObjectGetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_YSIZE);
      bool isOverSend = (mouseX >= send_x && mouseX <= send_x + send_w && mouseY >= send_y && mouseY <= send_y + send_h);
      if (isOverSend && !button_hover) {
         ObjectSetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_BGCOLOR, button_darker_bg);
         button_hover = true;
         ChartRedraw();
      } else if (!isOverSend && button_hover) {
         ObjectSetInteger(0, "ChatGPT_SendPromptButton", OBJPROP_BGCOLOR, button_original_bg);
         button_hover = false;
         ChartRedraw();
      }
      long clear_x = ObjectGetInteger(0, "ChatGPT_ClearButton", OBJPROP_XDISTANCE);
      long clear_y = ObjectGetInteger(0, "ChatGPT_ClearButton", OBJPROP_YDISTANCE);
      long clear_w = ObjectGetInteger(0, "ChatGPT_ClearButton", OBJPROP_XSIZE);
      long clear_h = ObjectGetInteger(0, "ChatGPT_ClearButton", OBJPROP_YSIZE);
      bool isOverClear = (mouseX >= clear_x && mouseX <= clear_x + clear_w && mouseY >= clear_y && mouseY <= clear_y + clear_h);
      if (isOverClear && !clear_hover) {
         ObjectSetInteger(0, "ChatGPT_ClearButton", OBJPROP_BGCOLOR, clear_darker_bg);
         clear_hover = true;
         ChartRedraw();
      } else if (!isOverClear && clear_hover) {
         ObjectSetInteger(0, "ChatGPT_ClearButton", OBJPROP_BGCOLOR, clear_original_bg);
         clear_hover = false;
         ChartRedraw();
      }
      long newchat_x = ObjectGetInteger(0, "ChatGPT_NewChatButton", OBJPROP_XDISTANCE);
      long newchat_y = ObjectGetInteger(0, "ChatGPT_NewChatButton", OBJPROP_YDISTANCE);
      long newchat_w = ObjectGetInteger(0, "ChatGPT_NewChatButton", OBJPROP_XSIZE);
      long newchat_h = ObjectGetInteger(0, "ChatGPT_NewChatButton", OBJPROP_YSIZE);
      bool isOverNewChat = (mouseX >= newchat_x && mouseX <= newchat_x + newchat_w && mouseY >= newchat_y && mouseY <= newchat_y + newchat_h);
      if (isOverNewChat && !new_chat_hover) {
         ObjectSetInteger(0, "ChatGPT_NewChatButton", OBJPROP_BGCOLOR, new_chat_darker_bg);
         new_chat_hover = true;
         ChartRedraw();
      } else if (!isOverNewChat && new_chat_hover) {
         ObjectSetInteger(0, "ChatGPT_NewChatButton", OBJPROP_BGCOLOR, new_chat_original_bg);
         new_chat_hover = false;
         ChartRedraw();
      }
      long history_x = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XDISTANCE);
      long history_y = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_YDISTANCE);
      long history_w = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_XSIZE);
      long history_h = ObjectGetInteger(0, "ChatGPT_HistoryButton", OBJPROP_YSIZE);
      bool isOverHistory = (mouseX >= history_x && mouseX <= history_x + history_w && mouseY >= history_y && mouseY <= history_y + history_h);
      if (isOverHistory && !history_hover) {
         ObjectSetInteger(0, "ChatGPT_HistoryButton", OBJPROP_BGCOLOR, history_darker_bg);
         history_hover = true;
         ChartRedraw();
      } else if (!isOverHistory && history_hover) {
         ObjectSetInteger(0, "ChatGPT_HistoryButton", OBJPROP_BGCOLOR, history_original_bg);
         history_hover = false;
         ChartRedraw();
      }
      if (!sidebarExpanded) {
         if (isOverHistory && !showing_small_history_popup) {
            ShowSmallHistoryPopup((int)history_y);
            just_opened_small = true;
            ChartRedraw();
         } else if (showing_small_history_popup) {
            bool isOverPopup = (mouseX >= small_popup_x && mouseX <= small_popup_x + small_popup_w && mouseY >= small_popup_y && mouseY <= small_popup_y + small_popup_h);
            if (!isOverHistory && !isOverPopup) {
               DeleteSmallHistoryPopup();
               ChartRedraw();
            }
         }
      }
      long close_x = ObjectGetInteger(0, "ChatGPT_CloseButton", OBJPROP_XDISTANCE);
      long close_y = ObjectGetInteger(0, "ChatGPT_CloseButton", OBJPROP_YDISTANCE);
      long close_w = ObjectGetInteger(0, "ChatGPT_CloseButton", OBJPROP_XSIZE);
      long close_h = ObjectGetInteger(0, "ChatGPT_CloseButton", OBJPROP_YSIZE);
      bool isOverClose = (mouseX >= close_x && mouseX <= close_x + close_w && mouseY >= close_y && mouseY <= close_y + close_h);
      if (isOverClose && !close_hover) {
         ObjectSetInteger(0, "ChatGPT_CloseButton", OBJPROP_BGCOLOR, close_darker_bg);
         close_hover = true;
         ChartRedraw();
      } else if (!isOverClose && close_hover) {
         ObjectSetInteger(0, "ChatGPT_CloseButton", OBJPROP_BGCOLOR, close_original_bg);
         close_hover = false;
         ChartRedraw();
      }
      long toggle_x = ObjectGetInteger(0, "ChatGPT_ToggleButton", OBJPROP_XDISTANCE);
      long toggle_y = ObjectGetInteger(0, "ChatGPT_ToggleButton", OBJPROP_YDISTANCE);
      long toggle_w = ObjectGetInteger(0, "ChatGPT_ToggleButton", OBJPROP_XSIZE);
      long toggle_h = ObjectGetInteger(0, "ChatGPT_ToggleButton", OBJPROP_YSIZE);
      bool isOverToggle = (mouseX >= toggle_x && mouseX <= toggle_x + toggle_w && mouseY >= toggle_y && mouseY <= toggle_y + toggle_h);
      if (isOverToggle && !toggle_hover) {
         ObjectSetInteger(0, "ChatGPT_ToggleButton", OBJPROP_BGCOLOR, toggle_darker_bg);
         toggle_hover = true;
         ChartRedraw();
      } else if (!isOverToggle && toggle_hover) {
         ObjectSetInteger(0, "ChatGPT_ToggleButton", OBJPROP_BGCOLOR, toggle_original_bg);
         toggle_hover = false;
         ChartRedraw();
      }
      if (showing_big_history_popup) {
         long big_close_x = ObjectGetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_XDISTANCE);
         long big_close_y = ObjectGetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_YDISTANCE);
         long big_close_w = ObjectGetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_XSIZE);
         long big_close_h = ObjectGetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_YSIZE);
         bool isOverBigClose = (mouseX >= big_close_x && mouseX <= big_close_x + big_close_w && mouseY >= big_close_y && mouseY <= big_close_y + big_close_h);
         if (isOverBigClose && !big_close_hover) {
            ObjectSetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_BGCOLOR, big_close_darker_bg);
            big_close_hover = true;
            ChartRedraw();
         } else if (!isOverBigClose && big_close_hover) {
            ObjectSetInteger(0, "ChatGPT_BigCloseButton", OBJPROP_BGCOLOR, big_close_original_bg);
            big_close_hover = false;
            ChartRedraw();
         }
      }
      if (showing_small_history_popup) {
         long see_x = ObjectGetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_XDISTANCE);
         long see_y = ObjectGetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_YDISTANCE);
         long see_w = ObjectGetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_XSIZE);
         long see_h = ObjectGetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_YSIZE);
         bool isOverSee = (mouseX >= see_x && mouseX <= see_x + see_w && mouseY >= see_y && mouseY <= see_y + see_h);
         if (isOverSee && !see_more_hover) {
            ObjectSetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_BGCOLOR, see_more_darker_bg);
            see_more_hover = true;
            ChartRedraw();
         } else if (!isOverSee && see_more_hover) {
            ObjectSetInteger(0, "ChatGPT_SeeMoreButton", OBJPROP_BGCOLOR, see_more_original_bg);
            see_more_hover = false;
            ChartRedraw();
         }
      }
      if (showing_small_history_popup || showing_big_history_popup) {
         ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
      } else {
         bool is_in_display = (mouseX >= displayX && mouseX <= displayX + displayW &&
                               mouseY >= displayY && mouseY <= displayY + displayH);
         if (is_in_display != mouse_in_display) {
            mouse_in_display = is_in_display;
            ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_display && need_scroll));
            if (ScrollbarMode == SCROLL_DYNAMIC_HOVER) {
               UpdateResponseDisplay();
            }
         }
         bool is_in_prompt = (mouseX >= displayX && mouseX <= displayX + displayW &&
                              mouseY >= promptY && mouseY <= promptY + promptH);
         if (is_in_prompt != mouse_in_prompt) {
            mouse_in_prompt = is_in_prompt;
            ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_prompt && p_need_scroll));
            if (ScrollbarMode == SCROLL_DYNAMIC_HOVER) {
               UpdatePromptDisplay();
            }
         }
      }
      static int prevMouseState = 0;
      int MouseState = (int)sparam;
      bool is_over_edit = (mouseX >= editX && mouseX <= editX + g_editW &&
                           mouseY >= editY && mouseY <= editY + g_editHeight);
      if (prevMouseState == 0 && MouseState == 1 && is_over_edit) {
         DeletePlaceholder();
      }
      if (prevMouseState == 0 && MouseState == 1 && scroll_visible) {
         int scrollbar_x = displayX + displayW - 16;
         int xd_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE);
         if (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
             mouseY >= yd_slider && mouseY <= yd_slider + ys_slider) {
            movingStateSlider = true;
            mlbDownX_Slider = mouseX;
            mlbDownY_Slider = mouseY;
            mlbDown_YD_Slider = yd_slider;
            ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_BGCOLOR, clrDimGray);
            ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE, slider_height);
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
         }
      }
      if (prevMouseState == 0 && MouseState == 1 && p_scroll_visible) {
         int promptX = g_mainContentX + g_sidePadding;
         int scrollbar_x = promptX + displayW - 16;
         int xd_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_YSIZE);
         if (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
             mouseY >= yd_slider && mouseY <= yd_slider + ys_slider) {
            p_movingStateSlider = true;
            p_mlbDownX_Slider = mouseX;
            p_mlbDownY_Slider = mouseY;
            p_mlbDown_YD_Slider = yd_slider;
            ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_BGCOLOR, clrDimGray);
            ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_YSIZE, p_slider_height);
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
         }
      }
      if (prevMouseState == 0 && MouseState == 1 && showing_big_history_popup && big_scroll_visible) {
         int scrollbar_x = big_popup_x + big_popup_w - 10 - 16;
         int xd_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YSIZE);
         if (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
             mouseY >= yd_slider && mouseY <= yd_slider + ys_slider) {
            big_movingStateSlider = true;
            big_mlbDownX_Slider = mouseX;
            big_mlbDownY_Slider = mouseY;
            big_mlbDown_YD_Slider = yd_slider;
            ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_BGCOLOR, clrDimGray);
            ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YSIZE, big_slider_height);
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
         }
      }
      if (movingStateSlider) {
         int delta_y = mouseY - mlbDownY_Slider;
         int new_y = mlbDown_YD_Slider + delta_y;
         int scroll_area_y_min = (g_mainY + g_headerHeight + g_padding) + 16;
         int scroll_area_y_max = (g_mainY + g_headerHeight + g_padding + g_displayHeight - 16 - slider_height);
         new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
         ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
         int max_scroll = MathMax(0, g_total_height - g_visible_height);
         double scroll_ratio = (double)(new_y - scroll_area_y_min) / (scroll_area_y_max - scroll_area_y_min);
         int new_scroll_pos = (int)MathRound(scroll_ratio * max_scroll);
         if (new_scroll_pos != scroll_pos) {
            scroll_pos = new_scroll_pos;
            UpdateResponseDisplay();
            if (scroll_visible) {
               UpdateSliderPosition();
               UpdateButtonColors();
            }
         }
         ChartRedraw();
      }
      if (p_movingStateSlider) {
         int delta_y = mouseY - p_mlbDownY_Slider;
         int new_y = p_mlbDown_YD_Slider + delta_y;
         int scroll_area_y_min = promptY + 16;
         int scroll_area_y_max = promptY + g_promptHeight - 16 - p_slider_height;
         new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
         ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
         int max_scroll = MathMax(0, p_total_height - p_visible_height);
         double scroll_ratio = (double)(new_y - scroll_area_y_min) / (scroll_area_y_max - scroll_area_y_min);
         int new_scroll_pos = (int)MathRound(scroll_ratio * max_scroll);
         if (new_scroll_pos != p_scroll_pos) {
            p_scroll_pos = new_scroll_pos;
            UpdatePromptDisplay();
            if (p_scroll_visible) {
               UpdatePromptSliderPosition();
               UpdatePromptButtonColors();
            }
         }
         ChartRedraw();
      }
      if (big_movingStateSlider) {
         int delta_y = mouseY - big_mlbDownY_Slider;
         int new_y = big_mlbDown_YD_Slider + delta_y;
         int scroll_area_y_min = big_popup_y + 40 + 16;
         int scroll_area_y_max = big_popup_y + 40 + (big_popup_h - 40 - 10) - 16 - big_slider_height;
         new_y = MathMax(scroll_area_y_min, MathMin(new_y, scroll_area_y_max));
         ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YDISTANCE, new_y);
         int max_scroll = MathMax(0, big_total_height - big_visible_height);
         double scroll_ratio = (double)(new_y - scroll_area_y_min) / (scroll_area_y_max - scroll_area_y_min);
         int new_scroll_pos = (int)MathRound(scroll_ratio * max_scroll);
         if (new_scroll_pos != big_scroll_pos) {
            big_scroll_pos = new_scroll_pos;
            UpdateBigHistoryDisplay();
            if (big_scroll_visible) {
               UpdateBigSliderPosition();
               UpdateBigButtonColors();
            }
         }
         ChartRedraw();
      }
      if (MouseState == 0) {
         if (movingStateSlider) {
            movingStateSlider = false;
            if (scroll_visible) {
               ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_BGCOLOR, clrGray);
            }
            ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_display && need_scroll));
         }
         if (p_movingStateSlider) {
            p_movingStateSlider = false;
            if (p_scroll_visible) {
               ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_BGCOLOR, clrGray);
            }
            ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_prompt && p_need_scroll));
         }
         if (big_movingStateSlider) {
            big_movingStateSlider = false;
            if (big_scroll_visible) {
               ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_BGCOLOR, clrGray);
            }
            ChartSetInteger(0, CHART_MOUSE_SCROLL, true); // reset
         }
      }
      prevMouseState = MouseState;
      static bool prevMouseInsideScrollUp = false;
      static bool prevMouseInsideScrollDown = false;
      static bool prevMouseInsideSlider = false;
      static bool p_prevMouseInsideScrollUp = false;
      static bool p_prevMouseInsideScrollDown = false;
      static bool p_prevMouseInsideSlider = false;
      static bool big_prevMouseInsideScrollUp = false;
      static bool big_prevMouseInsideScrollDown = false;
      static bool big_prevMouseInsideSlider = false;
      if (scroll_visible) {
         int scrollbar_x = displayX + displayW - 16;
         int button_size = 16;
         int xd_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE);
         bool isMouseInsideUp = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                 mouseY >= displayY &&
                                 mouseY <= displayY + button_size);
         bool isMouseInsideDown = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                   mouseY >= displayY + g_displayHeight - button_size &&
                                   mouseY <= displayY + g_displayHeight);
         bool isMouseInsideSlider = (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
                                     mouseY >= yd_slider && mouseY <= yd_slider + ys_slider);
         if (isMouseInsideUp != prevMouseInsideScrollUp) {
            ObjectSetInteger(0, SCROLL_UP_REC, OBJPROP_BGCOLOR, isMouseInsideUp ? clrSilver : clrGainsboro);
            prevMouseInsideScrollUp = isMouseInsideUp;
            ChartRedraw();
         }
         if (isMouseInsideDown != prevMouseInsideScrollDown) {
            ObjectSetInteger(0, SCROLL_DOWN_REC, OBJPROP_BGCOLOR, isMouseInsideDown ? clrSilver : clrGainsboro);
            prevMouseInsideScrollDown = isMouseInsideDown;
            ChartRedraw();
         }
         if (isMouseInsideSlider != prevMouseInsideSlider && !movingStateSlider) {
            ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_BGCOLOR, isMouseInsideSlider ? clrDarkGray : clrSilver);
            prevMouseInsideSlider = isMouseInsideSlider;
            ChartRedraw();
         }
      }
      if (p_scroll_visible) {
         int promptX = g_mainContentX + g_sidePadding;
         int scrollbar_x = promptX + displayW - 16;
         int button_size = 16;
         int xd_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, P_SCROLL_SLIDER, OBJPROP_YSIZE);
         bool isMouseInsideUp = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                 mouseY >= promptY &&
                                 mouseY <= promptY + button_size);
         bool isMouseInsideDown = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                   mouseY >= promptY + g_promptHeight - button_size &&
                                   mouseY <= promptY + g_promptHeight);
         bool isMouseInsideSlider = (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
                                     mouseY >= yd_slider && mouseY <= yd_slider + ys_slider);
         if (isMouseInsideUp != p_prevMouseInsideScrollUp) {
            ObjectSetInteger(0, P_SCROLL_UP_REC, OBJPROP_BGCOLOR, isMouseInsideUp ? clrSilver : clrGainsboro);
            p_prevMouseInsideScrollUp = isMouseInsideUp;
            ChartRedraw();
         }
         if (isMouseInsideDown != p_prevMouseInsideScrollDown) {
            ObjectSetInteger(0, P_SCROLL_DOWN_REC, OBJPROP_BGCOLOR, isMouseInsideDown ? clrSilver : clrGainsboro);
            p_prevMouseInsideScrollDown = isMouseInsideDown;
            ChartRedraw();
         }
         if (isMouseInsideSlider != p_prevMouseInsideSlider && !p_movingStateSlider) {
            ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_BGCOLOR, isMouseInsideSlider ? clrDarkGray : clrSilver);
            p_prevMouseInsideSlider = isMouseInsideSlider;
            ChartRedraw();
         }
      }
      if (showing_big_history_popup && big_scroll_visible) {
         int scrollbar_x = big_popup_x + big_popup_w - 10 - 16;
         int button_size = 16;
         int xd_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_XDISTANCE);
         int yd_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YDISTANCE);
         int xs_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_XSIZE);
         int ys_slider = (int)ObjectGetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_YSIZE);
         bool isMouseInsideUp = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                 mouseY >= big_popup_y + 40 &&
                                 mouseY <= big_popup_y + 40 + button_size);
         bool isMouseInsideDown = (mouseX >= scrollbar_x && mouseX <= scrollbar_x + 16 &&
                                   mouseY >= big_popup_y + 40 + (big_popup_h - 40 - 10) - button_size &&
                                   mouseY <= big_popup_y + 40 + (big_popup_h - 40 - 10));
         bool isMouseInsideSlider = (mouseX >= xd_slider && mouseX <= xd_slider + xs_slider &&
                                     mouseY >= yd_slider && mouseY <= yd_slider + ys_slider);
         if (isMouseInsideUp != big_prevMouseInsideScrollUp) {
            ObjectSetInteger(0, BIG_SCROLL_UP_REC, OBJPROP_BGCOLOR, isMouseInsideUp ? clrSilver : clrGainsboro);
            big_prevMouseInsideScrollUp = isMouseInsideUp;
            ChartRedraw();
         }
         if (isMouseInsideDown != big_prevMouseInsideScrollDown) {
            ObjectSetInteger(0, BIG_SCROLL_DOWN_REC, OBJPROP_BGCOLOR, isMouseInsideDown ? clrSilver : clrGainsboro);
            big_prevMouseInsideScrollDown = isMouseInsideDown;
            ChartRedraw();
         }
         if (isMouseInsideSlider != big_prevMouseInsideSlider && !big_movingStateSlider) {
            ObjectSetInteger(0, BIG_SCROLL_SLIDER, OBJPROP_BGCOLOR, isMouseInsideSlider ? clrDarkGray : clrSilver);
            big_prevMouseInsideSlider = isMouseInsideSlider;
            ChartRedraw();
         }
      }
      if (sidebarExpanded) {
         for (int i = 0; i < ArraySize(side_chat_bgs); i++) {
            string bg = side_chat_bgs[i];
            long x = ObjectGetInteger(0, bg, OBJPROP_XDISTANCE);
            long y = ObjectGetInteger(0, bg, OBJPROP_YDISTANCE);
            long w = ObjectGetInteger(0, bg, OBJPROP_XSIZE);
            long h = ObjectGetInteger(0, bg, OBJPROP_YSIZE);
            bool isOver = (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h);
            color bg_color = isOver ? DarkenColor(clrBeige, 0.8) : clrBeige;
            ObjectSetInteger(0, bg, OBJPROP_COLOR, bg_color);
         }
      }
      if (showing_small_history_popup) {
         for (int i = 0; i < ArraySize(small_chat_bgs); i++) {
            string bg = small_chat_bgs[i];
            long x = ObjectGetInteger(0, bg, OBJPROP_XDISTANCE);
            long y = ObjectGetInteger(0, bg, OBJPROP_YDISTANCE);
            long w = ObjectGetInteger(0, bg, OBJPROP_XSIZE);
            long h = ObjectGetInteger(0, bg, OBJPROP_YSIZE);
            bool isOver = (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h);
            color bg_color = isOver ? DarkenColor(clrBeige, 0.8) : clrBeige;
            ObjectSetInteger(0, bg, OBJPROP_COLOR, bg_color);
         }
      }
      if (showing_big_history_popup) {
         for (int i = 0; i < ArraySize(big_chat_bgs); i++) {
            string bg = big_chat_bgs[i];
            long x = ObjectGetInteger(0, bg, OBJPROP_XDISTANCE);
            long y = ObjectGetInteger(0, bg, OBJPROP_YDISTANCE);
            long w = ObjectGetInteger(0, bg, OBJPROP_XSIZE);
            long h = ObjectGetInteger(0, bg, OBJPROP_YSIZE);
            bool isOver = (mouseX >= x && mouseX <= x + w && mouseY >= y && mouseY <= y + h);
            color bg_color = isOver ? DarkenColor(clrBeige, 0.8) : clrBeige;
            ObjectSetInteger(0, bg, OBJPROP_COLOR, bg_color);
         }
      }
      ChartRedraw();
   } else if (id == CHARTEVENT_MOUSE_WHEEL) {
      int mouseX = (int)lparam;
      int mouseY = (int)dparam;
      int delta = (int)sparam;
      bool in_display = (mouseX >= displayX && mouseX <= displayX + displayW &&
                         mouseY >= displayY && mouseY <= displayY + displayH);
      if (in_display != mouse_in_display) {
         mouse_in_display = in_display;
         ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_display && need_scroll));
         if (ScrollbarMode == SCROLL_DYNAMIC_HOVER) {
            UpdateResponseDisplay();
         }
      }
      bool in_prompt = (mouseX >= displayX && mouseX <= displayX + displayW &&
                        mouseY >= promptY && mouseY <= promptY + promptH);
      if (in_prompt != mouse_in_prompt) {
         mouse_in_prompt = in_prompt;
         ChartSetInteger(0, CHART_MOUSE_SCROLL, !(mouse_in_prompt && p_need_scroll));
         if (ScrollbarMode == SCROLL_DYNAMIC_HOVER) {
            UpdatePromptDisplay();
         }
      }
      bool in_big = showing_big_history_popup && (mouseX >= big_popup_x && mouseX <= big_popup_x + big_popup_w && mouseY >= big_popup_y + 40 && mouseY <= big_popup_y + big_popup_h);
      if (in_display && need_scroll && !showing_small_history_popup && !showing_big_history_popup) {
         int scroll_amount = 30 * (delta > 0 ? -1 : 1);
         scroll_pos = MathMax(0, MathMin(MathMax(0, g_total_height - g_visible_height), scroll_pos + scroll_amount));
         UpdateResponseDisplay();
         if (scroll_visible) {
            UpdateSliderPosition();
            UpdateButtonColors();
         }
         ChartRedraw();
      }
      if (in_prompt && p_need_scroll && !showing_small_history_popup && !showing_big_history_popup) {
         int scroll_amount = 30 * (delta > 0 ? -1 : 1);
         p_scroll_pos = MathMax(0, MathMin(MathMax(0, p_total_height - p_visible_height), p_scroll_pos + scroll_amount));
         UpdatePromptDisplay();
         if (p_scroll_visible) {
            UpdatePromptSliderPosition();
            UpdatePromptButtonColors();
         }
         ChartRedraw();
      }
      if (in_big && big_total_height > big_visible_height) {
         int scroll_amount = 30 * (delta > 0 ? -1 : 1);
         big_scroll_pos = MathMax(0, MathMin(MathMax(0, big_total_height - big_visible_height), big_scroll_pos + scroll_amount));
         UpdateBigHistoryDisplay();
         if (big_scroll_visible) {
            UpdateBigSliderPosition();
            UpdateBigButtonColors();
         }
         ChartRedraw();
      }
   }
}

bool IsTimestamp(string line) {
   StringTrimLeft(line);
   StringTrimRight(line);
   if (StringLen(line) != 5) return false;
   if (StringGetCharacter(line, 2) != ':') return false;
   string hh = StringSubstr(line, 0, 2);
   string mm = StringSubstr(line, 3, 2);
   int h = (int)StringToInteger(hh);
   int m = (int)StringToInteger(mm);
   if (h < 0 || h > 23 || m < 0 || m > 59) return false;
   return true;
}

void SubmitMessage(string prompt) {
   if (StringLen(prompt) == 0) return;
   string timestamp = TimeToString(TimeCurrent(), TIME_MINUTES);
   string response = "";
   bool send_to_api = true;
   if (StringFind(prompt, "set title ") == 0) {
      string new_title = StringSubstr(prompt, 10);
      current_title = new_title;
      response = "Title set to " + new_title;
      send_to_api = false;
      UpdateCurrentHistory();
      UpdateSidebarDynamic();
   }
   if (send_to_api) {
      Print("Chat ID: " + IntegerToString(current_chat_id) + ", Title: " + current_title);
      FileWrite(logFileHandle, "Chat ID: " + IntegerToString(current_chat_id) + ", Title: " + current_title);
      Print("User: " + prompt);
      FileWrite(logFileHandle, "User: " + prompt);
      response = GetChatGPTResponse(prompt);
      Print("AI: " + response);
      FileWrite(logFileHandle, "AI: " + response);
      if (StringFind(current_title, "Chat ") == 0) {
         current_title = StringSubstr(prompt, 0, 30);
         if (StringLen(prompt) > 30) current_title += "...";
         UpdateCurrentHistory();
         UpdateSidebarDynamic();
      }
   }
   conversationHistory += "You: " + prompt + "\n" + timestamp + "\nAI: " + response + "\n" + timestamp + "\n\n";
   UpdateCurrentHistory();
   UpdateResponseDisplay();
   scroll_pos = MathMax(0, g_total_height - g_visible_height);
   UpdateResponseDisplay();
   if (scroll_visible) {
      UpdateSliderPosition();
      UpdateButtonColors();
   }
   ChartRedraw();
}

void CreateDashboard() {
   objCount = 0;
   g_mainHeight = g_headerHeight + 2 * g_padding + g_displayHeight + g_footerHeight;
   int displayX = g_mainContentX + g_sidePadding;
   int displayY = g_mainY + g_headerHeight + g_padding;
   int displayW = g_mainWidth - 2 * g_sidePadding;
   int footerY = displayY + g_displayHeight + g_padding;
   int promptY = footerY + g_margin;
   int buttonsY = promptY + g_promptHeight + g_margin;
   int buttonW = 140;
   int chartX = g_mainContentX + g_sidePadding;
   int sendX = g_mainContentX + g_mainWidth - g_sidePadding - buttonW;
   dashboardObjects[objCount++] = "ChatGPT_MainContainer";
   createRecLabel("ChatGPT_MainContainer", g_mainContentX, g_mainY, g_mainWidth, g_mainHeight, clrWhite, 1, clrLightGray);
   dashboardObjects[objCount++] = "ChatGPT_HeaderBg";
   createRecLabel("ChatGPT_HeaderBg", g_mainContentX, g_mainY, g_mainWidth, g_headerHeight, clrWhiteSmoke, 0, clrNONE);
   string logo_resource = (StringLen(g_scaled_image_resource) > 0) ? g_scaled_image_resource : resourceImg;
   dashboardObjects[objCount++] = "ChatGPT_HeaderLogo";
   createBitmapLabel("ChatGPT_HeaderLogo", g_mainContentX + g_sidePadding, g_mainY + (g_headerHeight - 40)/2, 104, 40, logo_resource, clrWhite, CORNER_LEFT_UPPER);
   string title = "ChatGPT AI EA";
   string titleFont = "Arial Rounded MT Bold";
   int titleSize = 14;
   TextSetFont(titleFont, titleSize);
   uint titleWid, titleHei;
   TextGetSize(title, titleWid, titleHei);
   int titleY = g_mainY + (g_headerHeight - (int)titleHei) / 2 - 4;
   int titleX = g_mainContentX + g_sidePadding + 104 + 5;
   dashboardObjects[objCount++] = "ChatGPT_TitleLabel";
   createLabel("ChatGPT_TitleLabel", titleX, titleY, title, clrDarkSlateGray, titleSize, titleFont, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
   string dateStr = TimeToString(TimeTradeServer(), TIME_MINUTES);
   string dateFont = "Arial";
   int dateSize = 12;
   TextSetFont(dateFont, dateSize);
   uint dateWid, dateHei;
   TextGetSize(dateStr, dateWid, dateHei);
   int dateX = g_mainContentX + g_mainWidth / 2 - (int)(dateWid / 2) + 20;
   int dateY = g_mainY + (g_headerHeight - (int)dateHei) / 2 - 4;
   dashboardObjects[objCount++] = "ChatGPT_DateLabel";
   createLabel("ChatGPT_DateLabel", dateX, dateY, dateStr, clrSlateGray, dateSize, dateFont, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
   int closeWidth = 100;
   int closeX = g_mainContentX + g_mainWidth - closeWidth - g_sidePadding;
   int closeY = g_mainY + 4;
   dashboardObjects[objCount++] = "ChatGPT_CloseButton";
   createButton("ChatGPT_CloseButton", closeX, closeY, closeWidth, g_headerHeight - 8, "Close", clrWhite, 11, close_original_bg, clrGray);
   dashboardObjects[objCount++] = "ChatGPT_ResponseBg";
   createRecLabel("ChatGPT_ResponseBg", displayX, displayY, displayW, g_displayHeight, clrWhite, 1, clrGainsboro, BORDER_FLAT, STYLE_SOLID);
   dashboardObjects[objCount++] = "ChatGPT_FooterBg";
   createRecLabel("ChatGPT_FooterBg", g_mainContentX, footerY, g_mainWidth, g_footerHeight, clrGainsboro, 0, clrNONE);
   dashboardObjects[objCount++] = "ChatGPT_PromptBg";
   createRecLabel("ChatGPT_PromptBg", displayX, promptY, displayW, g_promptHeight, g_promptBg, 1, g_promptBg, BORDER_FLAT, STYLE_SOLID);
   int editY = promptY + g_promptHeight - g_editHeight - 5;
   int editX = displayX + g_textPadding;
   g_editW = displayW - 2 * g_textPadding;
   dashboardObjects[objCount++] = "ChatGPT_PromptEdit";
   createEdit("ChatGPT_PromptEdit", editX, editY, g_editW, g_editHeight, "", clrBlack, 13, DarkenColor(g_promptBg,0.93), DarkenColor(g_promptBg,0.87),"Calibri");
   ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_BORDER_TYPE, BORDER_FLAT);
   dashboardObjects[objCount++] = "ChatGPT_GetChartButton";
   createButton("ChatGPT_GetChartButton", chartX, buttonsY, buttonW, g_buttonHeight, "Get Chart Data", clrWhite, 11, chart_button_bg, clrDarkGreen);
   dashboardObjects[objCount++] = "ChatGPT_SendPromptButton";
   createButton("ChatGPT_SendPromptButton", sendX, buttonsY, buttonW, g_buttonHeight, "Send Prompt", clrWhite, 11, button_original_bg, clrDarkBlue);
   ChartRedraw();
}

int TextGetHeight(string text, string font, int fontSize) {
   uint wid, hei;
   TextSetFont(font, fontSize);
   TextGetSize(text, wid, hei);
   return (int)hei;
}

void ComputeLinesAndHeight(const string &font, const int fontSize, const int timestampFontSize,
                           const int adjustedLineHeight, const int adjustedTimestampHeight,
                           const int messageMargin, const int maxTextWidth,
                           const string &msgRoles[], const string &msgContents[], const string &msgTimestamps[],
                           const int numMessages, int &totalHeight_out, int &totalLines_out,
                           string &allLines_out[], string &lineRoles_out[], int &lineHeights_out[]) {
   ArrayResize(allLines_out, 0);
   ArrayResize(lineRoles_out, 0);
   ArrayResize(lineHeights_out, 0);
   totalLines_out = 0;
   totalHeight_out = 0;
   for (int m = 0; m < numMessages; m++) {
      string wrappedLines[];
      WrapText(msgContents[m], font, fontSize, maxTextWidth, wrappedLines);
      int numLines = ArraySize(wrappedLines);
      int currSize = ArraySize(allLines_out);
      ArrayResize(allLines_out, currSize + numLines + 1);
      ArrayResize(lineRoles_out, currSize + numLines + 1);
      ArrayResize(lineHeights_out, currSize + numLines + 1);
      for (int l = 0; l < numLines; l++) {
         allLines_out[currSize + l] = wrappedLines[l];
         lineRoles_out[currSize + l] = msgRoles[m];
         lineHeights_out[currSize + l] = adjustedLineHeight;
         totalHeight_out += adjustedLineHeight;
      }
      allLines_out[currSize + numLines] = msgTimestamps[m];
      lineRoles_out[currSize + numLines] = msgRoles[m] + "_timestamp";
      lineHeights_out[currSize + numLines] = adjustedTimestampHeight;
      totalHeight_out += adjustedTimestampHeight;
      totalLines_out += numLines + 1;
      if (m < numMessages - 1) {
         totalHeight_out += messageMargin;
      }
   }
}

void UpdateResponseDisplay() {
   if (showing_small_history_popup || showing_big_history_popup) return;
   int total = ObjectsTotal(0, 0, -1);
   for (int j = total - 1; j >= 0; j--) {
      string name = ObjectName(0, j, 0, -1);
      if (StringFind(name, "ChatGPT_ResponseLine_") == 0 ||
          StringFind(name, "ChatGPT_MessageBg_") == 0 ||
          StringFind(name, "ChatGPT_MessageText_") == 0 ||
          StringFind(name, "ChatGPT_Timestamp_") == 0) {
         ObjectDelete(0, name);
      }
   }
   string displayText = conversationHistory;
   int textX = g_mainContentX + g_sidePadding + g_textPadding;
   int textY = g_mainY + g_headerHeight + g_padding + g_textPadding;
   int fullMaxWidth = g_mainWidth - 2 * g_sidePadding - 2 * g_textPadding;
   if (displayText == "") {
      string objName = "ChatGPT_ResponseLine_0";
      createLabel(objName, textX, textY, "Type your prompt here and click Send to chat with the AI.", clrGray, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      g_total_height = 0;
      g_visible_height = g_displayHeight - 2 * g_textPadding;
      if (scroll_visible) {
         DeleteScrollbar();
         scroll_visible = false;
      }
      ChartRedraw();
      return;
   }
   string parts[];
   int numParts = StringSplit(displayText, '\n', parts);
   string msgRoles[];
   string msgContents[];
   string msgTimestamps[];
   string currentRole = "";
   string currentContent = "";
   string currentTimestamp = "";
   for (int p = 0; p < numParts; p++) {
      string line = parts[p];
      string trimmed = line;
      StringTrimLeft(trimmed);
      StringTrimRight(trimmed);
      if (StringLen(trimmed) == 0) {
         if (currentRole != "") currentContent += "\n";
         continue;
      }
      if (StringFind(trimmed, "You: ") == 0) {
         if (currentRole != "") {
            int size = ArraySize(msgRoles);
            ArrayResize(msgRoles, size + 1);
            ArrayResize(msgContents, size + 1);
            ArrayResize(msgTimestamps, size + 1);
            msgRoles[size] = currentRole;
            msgContents[size] = currentContent;
            msgTimestamps[size] = currentTimestamp;
         }
         currentRole = "User";
         currentContent = StringSubstr(line, StringFind(line, "You: ") + 5);
         currentTimestamp = "";
         continue;
      } else if (StringFind(trimmed, "AI: ") == 0) {
         if (currentRole != "") {
            int size = ArraySize(msgRoles);
            ArrayResize(msgRoles, size + 1);
            ArrayResize(msgContents, size + 1);
            ArrayResize(msgTimestamps, size + 1);
            msgRoles[size] = currentRole;
            msgContents[size] = currentContent;
            msgTimestamps[size] = currentTimestamp;
         }
         currentRole = "AI";
         currentContent = StringSubstr(line, StringFind(line, "AI: ") + 4);
         currentTimestamp = "";
         continue;
      } else if (IsTimestamp(trimmed)) {
         currentTimestamp = trimmed;
         int size = ArraySize(msgRoles);
         ArrayResize(msgRoles, size + 1);
         ArrayResize(msgContents, size + 1);
         ArrayResize(msgTimestamps, size + 1);
         msgRoles[size] = currentRole;
         msgContents[size] = currentContent;
         msgTimestamps[size] = currentTimestamp;
         currentRole = "";
         currentContent = "";
         currentTimestamp = "";
      } else {
         if (currentRole != "") {
            currentContent += "\n" + line;
         }
      }
   }
   if (currentRole != "") {
      int size = ArraySize(msgRoles);
      ArrayResize(msgRoles, size + 1);
      ArrayResize(msgContents, size + 1);
      ArrayResize(msgTimestamps, size + 1);
      msgRoles[size] = currentRole;
      msgContents[size] = currentContent;
      msgTimestamps[size] = currentTimestamp;
   }
   int numMessages = ArraySize(msgRoles);
   if (numMessages == 0) {
      string objName = "ChatGPT_ResponseLine_0";
      createLabel(objName, textX, textY, "Type your prompt here and click Send to chat with the AI.", clrGray, 10, "Arial", CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      g_total_height = 0;
      g_visible_height = g_displayHeight - 2 * g_textPadding;
      if (scroll_visible) {
         DeleteScrollbar();
         scroll_visible = false;
      }
      ChartRedraw();
      return;
   }
   string font = "Arial";
   int fontSize = 10;
   int timestampFontSize = 8;
   int lineHeight = TextGetHeight("A", font, fontSize);
   int timestampHeight = TextGetHeight("A", font, timestampFontSize);
   int adjustedLineHeight = lineHeight + g_lineSpacing;
   int adjustedTimestampHeight = timestampHeight + g_lineSpacing;
   int messageMargin = 12;
   int visibleHeight = g_displayHeight - 2 * g_textPadding;
   g_visible_height = visibleHeight;
   string tentativeAllLines[];
   string tentativeLineRoles[];
   int tentativeLineHeights[];
   int tentativeTotalHeight, tentativeTotalLines;
   ComputeLinesAndHeight(font, fontSize, timestampFontSize, adjustedLineHeight, adjustedTimestampHeight,
                         messageMargin, fullMaxWidth, msgRoles, msgContents, msgTimestamps, numMessages,
                         tentativeTotalHeight, tentativeTotalLines, tentativeAllLines, tentativeLineRoles, tentativeLineHeights);
   bool need_scroll = tentativeTotalHeight > visibleHeight;
   bool should_show_scrollbar = false;
   int reserved_width = 0;
   if (ScrollbarMode != SCROLL_WHEEL_ONLY) {
      should_show_scrollbar = need_scroll && (ScrollbarMode == SCROLL_DYNAMIC_ALWAYS || (ScrollbarMode == SCROLL_DYNAMIC_HOVER && mouse_in_display));
      if (should_show_scrollbar) {
         reserved_width = 16;
      }
   }
   string allLines[];
   string lineRoles[];
   int lineHeights[];
   int totalHeight, totalLines;
   if (reserved_width > 0) {
      ComputeLinesAndHeight(font, fontSize, timestampFontSize, adjustedLineHeight, adjustedTimestampHeight,
                            messageMargin, fullMaxWidth - reserved_width, msgRoles, msgContents, msgTimestamps, numMessages,
                            totalHeight, totalLines, allLines, lineRoles, lineHeights);
   } else {
      totalHeight = tentativeTotalHeight;
      totalLines = tentativeTotalLines;
      ArrayCopy(allLines, tentativeAllLines);
      ArrayCopy(lineRoles, tentativeLineRoles);
      ArrayCopy(lineHeights, tentativeLineHeights);
   }
   g_total_height = totalHeight;
   bool prev_scroll_visible = scroll_visible;
   scroll_visible = should_show_scrollbar;
   if (scroll_visible != prev_scroll_visible) {
      if (scroll_visible) {
         CreateScrollbar();
      } else {
         DeleteScrollbar();
      }
   }
   int max_scroll = MathMax(0, totalHeight - visibleHeight);
   if (scroll_pos > max_scroll) scroll_pos = max_scroll;
   if (scroll_pos < 0) scroll_pos = 0;
   if (totalHeight > visibleHeight && scroll_pos == prev_scroll_pos && prev_scroll_pos == -1) {
      scroll_pos = max_scroll;
   }
   if (scroll_visible) {
      slider_height = CalculateSliderHeight();
      ObjectSetInteger(0, SCROLL_SLIDER, OBJPROP_YSIZE, slider_height);
      UpdateSliderPosition();
      UpdateButtonColors();
   }
   int currentY = textY - scroll_pos;
   int endY = textY + visibleHeight;
   int startLineIndex = 0;
   int currentHeight = 0;
   for (int line = 0; line < totalLines; line++) {
      if (currentHeight >= scroll_pos) {
         startLineIndex = line;
         currentY = textY + (currentHeight - scroll_pos);
         break;
      }
      currentHeight += lineHeights[line];
      if (line < totalLines - 1 && StringFind(lineRoles[line], "_timestamp") >= 0 && StringFind(lineRoles[line + 1], "_timestamp") < 0) {
         currentHeight += messageMargin;
      }
   }
   int numVisibleLines = 0;
   int visibleHeightUsed = 0;
   for (int line = startLineIndex; line < totalLines; line++) {
      int lineHeight = lineHeights[line];
      if (visibleHeightUsed + lineHeight > visibleHeight) break;
      visibleHeightUsed += lineHeight;
      numVisibleLines++;
      if (line < totalLines - 1 && StringFind(lineRoles[line], "_timestamp") >= 0 && StringFind(lineRoles[line + 1], "_timestamp") < 0) {
         if (visibleHeightUsed + messageMargin > visibleHeight) break;
         visibleHeightUsed += messageMargin;
      }
   }
   int leftX = g_mainContentX + g_sidePadding + g_textPadding;
   int rightX = g_mainContentX + g_mainWidth - g_sidePadding - g_textPadding - reserved_width;
   color userColor = clrGray;
   color aiColor = clrBlue;
   color timestampColor = clrDarkGray;
   for (int li = 0; li < numVisibleLines; li++) {
      int lineIndex = startLineIndex + li;
      if (lineIndex >= totalLines) break;
      string line = allLines[lineIndex];
      string role = lineRoles[lineIndex];
      bool isTimestamp = StringFind(role, "_timestamp") >= 0;
      int currFontSize = isTimestamp ? timestampFontSize : fontSize;
      color textCol = isTimestamp ? timestampColor : (StringFind(role, "User") >= 0 ? userColor : aiColor);
      string display_line = line;
      if (line == " ") {
         display_line = " ";
         textCol = clrWhite;
      }
      int textX_pos = (StringFind(role, "User") >= 0) ? rightX : leftX;
      ENUM_ANCHOR_POINT textAnchor = (StringFind(role, "User") >= 0) ? ANCHOR_RIGHT_UPPER : ANCHOR_LEFT_UPPER;
      string lineName = "ChatGPT_MessageText_" + IntegerToString(lineIndex);
      if (currentY >= textY && currentY < endY) {
         createLabel(lineName, textX_pos, currentY, display_line, textCol, currFontSize, font, CORNER_LEFT_UPPER, textAnchor);
      }
      currentY += lineHeights[lineIndex];
      if (lineIndex < totalLines - 1 && StringFind(lineRoles[lineIndex], "_timestamp") >= 0 && StringFind(lineRoles[lineIndex + 1], "_timestamp") < 0) {
         currentY += messageMargin;
      }
   }
   ChartRedraw();
}


void UpdatePromptDisplay() {
   if (showing_small_history_popup || showing_big_history_popup) return;
   int total = ObjectsTotal(0, 0, -1);
   for (int j = total - 1; j >= 0; j--) {
      string name = ObjectName(0, j, 0, -1);
      if (StringFind(name, "ChatGPT_PromptLine_") == 0) {
         ObjectDelete(0, name);
      }
   }
   int promptX = g_mainContentX + g_sidePadding;
   int footerY = g_mainY + g_headerHeight + g_padding + g_displayHeight + g_padding;
   int promptY = footerY + g_margin;
   int textX = promptX + g_textPadding;
   int textY = promptY + g_textPadding;
   int editY = promptY + g_promptHeight - g_editHeight - 5;
   int fullMaxWidth = g_mainWidth - 2 * g_sidePadding - 2 * g_textPadding;
   int visibleHeight = editY - textY - g_textPadding - g_margin;
   if (currentPrompt == "") {
      p_total_height = 0;
      p_visible_height = visibleHeight;
      if (p_scroll_visible) {
         DeletePromptScrollbar();
         p_scroll_visible = false;
      }
      ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XSIZE, g_editW);
      ChartRedraw();
      return;
   }
   string font = "Arial";
   int fontSize = 10;
   int lineHeight = TextGetHeight("A", font, fontSize);
   int adjustedLineHeight = lineHeight + g_lineSpacing;
   p_visible_height = visibleHeight;
   string wrappedLines[];
   WrapText(currentPrompt, font, fontSize, fullMaxWidth, wrappedLines);
   int totalLines = ArraySize(wrappedLines);
   int totalHeight = totalLines * adjustedLineHeight;
   bool need_scroll = totalHeight > visibleHeight;
   bool should_show_scrollbar = false;
   int reserved_width = 0;
   if (ScrollbarMode != SCROLL_WHEEL_ONLY) {
      should_show_scrollbar = need_scroll && (ScrollbarMode == SCROLL_DYNAMIC_ALWAYS || (ScrollbarMode == SCROLL_DYNAMIC_HOVER && mouse_in_prompt));
      if (should_show_scrollbar) {
         reserved_width = 16;
      }
   }
   if (reserved_width > 0) {
      WrapText(currentPrompt, font, fontSize, fullMaxWidth - reserved_width, wrappedLines);
      totalLines = ArraySize(wrappedLines);
      totalHeight = totalLines * adjustedLineHeight;
   }
   p_total_height = totalHeight;
   bool prev_p_scroll_visible = p_scroll_visible;
   p_scroll_visible = should_show_scrollbar;
   if (p_scroll_visible != prev_p_scroll_visible) {
      if (p_scroll_visible) {
         CreatePromptScrollbar();
      } else {
         DeletePromptScrollbar();
      }
   }
   ObjectSetInteger(0, "ChatGPT_PromptEdit", OBJPROP_XSIZE, g_editW - reserved_width);
   int max_scroll = MathMax(0, totalHeight - visibleHeight);
   if (p_scroll_pos > max_scroll) p_scroll_pos = max_scroll;
   if (p_scroll_pos < 0) p_scroll_pos = 0;
   if (p_scroll_visible) {
      p_slider_height = CalculatePromptSliderHeight();
      ObjectSetInteger(0, P_SCROLL_SLIDER, OBJPROP_YSIZE, p_slider_height);
      UpdatePromptSliderPosition();
      UpdatePromptButtonColors();
   }
   int currentY = textY - p_scroll_pos;
   int endY = textY + visibleHeight;
   int startLineIndex = 0;
   int currentHeight = 0;
   for (int line = 0; line < totalLines; line++) {
      if (currentHeight >= p_scroll_pos) {
         startLineIndex = line;
         currentY = textY + (currentHeight - p_scroll_pos);
         break;
      }
      currentHeight += adjustedLineHeight;
   }
   int numVisibleLines = 0;
   int visibleHeightUsed = 0;
   for (int line = startLineIndex; line < totalLines; line++) {
      if (visibleHeightUsed + adjustedLineHeight > visibleHeight) break;
      visibleHeightUsed += adjustedLineHeight;
      numVisibleLines++;
   }
   int textX_pos = textX;
   color textCol = clrBlack;
   for (int li = 0; li < numVisibleLines; li++) {
      int lineIndex = startLineIndex + li;
      if (lineIndex >= totalLines) break;
      string line = wrappedLines[lineIndex];
      string display_line = line;
      if (line == " ") {
         display_line = " ";
         textCol = clrWhite;
      }
      string lineName = "ChatGPT_PromptLine_" + IntegerToString(lineIndex);
      if (currentY >= textY && currentY < endY) {
         createLabel(lineName, textX_pos, currentY, display_line, textCol, fontSize, font, CORNER_LEFT_UPPER, ANCHOR_LEFT_UPPER);
      }
      currentY += adjustedLineHeight;
   }
   ChartRedraw();
}

string JsonEscape(string value) {
   StringReplace(value, "\\", "\\\\");
   StringReplace(value, "\"", "\\\"");
   StringReplace(value, "\n", "\\n");
   StringReplace(value, "\r", "\\r");
   StringReplace(value, "\t", "\\t");
   StringReplace(value, "\f", "\\f");
   for (int i = 0; i < StringLen(value); i++) {
      ushort charCode = StringGetCharacter(value, i);
      if (charCode < 32 || charCode == 127) {
         string hex = StringFormat("\\u%04x", charCode);
         string before = StringSubstr(value, 0, i);
         string after = StringSubstr(value, i + 1);
         value = before + hex + after;
         i += 5;
      }
   }
   return value;
}

string LogCharArray(char &data[]) {
   string result = "";
   for (int i = 0; i < ArraySize(data); i++) {
      result += StringFormat("%02X ", data[i]);
   }
   return result;
}

string GetChatGPTResponse(string prompt) {
   string messages = BuildMessagesFromHistory(prompt);
   Print("Messages JSON: " + messages);
   FileWrite(logFileHandle, "Messages JSON: " + messages);
   string requestData = "{\"model\":\"" + OpenAI_Model + "\",\"messages\":" + messages + ",\"max_tokens\":" + IntegerToString(MaxResponseLength) + "}";
   Print("Request data: " + requestData);
   FileWrite(logFileHandle, "Request data: " + requestData);
   char postData[];
   int dataLen = StringToCharArray(requestData, postData, 0, WHOLE_ARRAY, CP_UTF8);
   ArrayResize(postData, dataLen - 1);
   string headers = "Authorization: Bearer " + OpenAI_API_Key + "\r\n" +
                    "Content-Type: application/json; charset=UTF-8\r\n" +
                    "Content-Length: " + IntegerToString(dataLen - 1) + "\r\n\r\n";
   char result[];
   string resultHeaders;
   int res = WebRequest("POST", OpenAI_Endpoint, headers, 10000, postData, result, resultHeaders);
   if (res != 200) {
      string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
      string errMsg = "API request failed: HTTP Code " + IntegerToString(res) + ", Error: " + IntegerToString(GetLastError()) + ", Response: " + response;
      Print(errMsg);
      return errMsg;
   }
   string response = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
   Print("API response: " + response);
   FileWrite(logFileHandle, "API response: " + response);
   JsonValue jsonObject;
   int index = 0;
   char charArray[];
   int arrayLength = StringToCharArray(response, charArray, 0, WHOLE_ARRAY, CP_UTF8);
   if (!jsonObject.DeserializeFromArray(charArray, arrayLength, index)) {
      string errMsg = "Error: Failed to parse API response JSON: " + response;
      Print(errMsg);
      return errMsg;
   }
   JsonValue *error = jsonObject.FindChildByKey("error");
   if (error != NULL) {
      string errMsg = "API Error: " + error["message"].ToString();
      Print(errMsg);
      return errMsg;
   }
   string content = jsonObject["choices"][0]["message"]["content"].ToString();
   if (StringLen(content) > 0) {
      StringReplace(content, "\\n", "\n");
      StringTrimLeft(content);
      StringTrimRight(content);
      return content;
   }
   string errMsg = "Error: No content in API response: " + response;
   Print(errMsg);
   return errMsg;
}

string BuildMessagesFromHistory(string newPrompt) {
   string lines[];
   int numLines = StringSplit(conversationHistory, '\n', lines);
   string messages = "[";
   string currentRole = "";
   string currentContent = "";
   for (int i = 0; i < numLines; i++) {
      string line = lines[i];
      string trimmed = line;
      StringTrimLeft(trimmed);
      StringTrimRight(trimmed);
      if (StringLen(trimmed) == 0 || IsTimestamp(trimmed)) continue;
      if (StringFind(trimmed, "You: ") == 0) {
         if (currentRole != "") {
            string roleJson = (currentRole == "User") ? "user" : "assistant";
            messages += "{\"role\":\"" + roleJson + "\",\"content\":\"" + JsonEscape(currentContent) + "\"},";
         }
         currentRole = "User";
         currentContent = StringSubstr(line, StringFind(line, "You: ") + 5);
      } else if (StringFind(trimmed, "AI: ") == 0) {
         if (currentRole != "") {
            string roleJson = (currentRole == "User") ? "user" : "assistant";
            messages += "{\"role\":\"" + roleJson + "\",\"content\":\"" + JsonEscape(currentContent) + "\"},";
         }
         currentRole = "AI";
         currentContent = StringSubstr(line, StringFind(line, "AI: ") + 4);
      } else if (currentRole != "") {
         currentContent += "\n" + line;
      }
   }
   if (currentRole != "") {
      string roleJson = (currentRole == "User") ? "user" : "assistant";
      messages += "{\"role\":\"" + roleJson + "\",\"content\":\"" + JsonEscape(currentContent) + "\"},";
   }
   messages += "{\"role\":\"user\",\"content\":\"" + JsonEscape(newPrompt) + "\"}]";
   return messages;
}

string PeriodToString(ENUM_TIMEFRAMES period) {
   switch(period) {
      case PERIOD_M1: return "M1";
      case PERIOD_M5: return "M5";
      case PERIOD_M15: return "M15";
      case PERIOD_M30: return "M30";
      case PERIOD_H1: return "H1";
      case PERIOD_H4: return "H4";
      case PERIOD_D1: return "D1";
      case PERIOD_W1: return "W1";
      case PERIOD_MN1: return "MN1";
      default: return IntegerToString(period);
   }
}

void GetAndAppendChartData() {
   string symbol = Symbol();
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)_Period;
   string timeframe = PeriodToString(tf);
   long visibleBarsLong = ChartGetInteger(0, CHART_VISIBLE_BARS);
   int visibleBars = (int)visibleBarsLong;
   MqlRates rates[];
   int copied = CopyRates(symbol, tf, 0, MaxChartBars, rates);
   if (copied != MaxChartBars) {
      Print("Failed to copy rates: ", GetLastError());
      return;
   }
   ArraySetAsSeries(rates, true);
   string data = "Chart Details: Symbol=" + symbol + ", Timeframe=" + timeframe + ", Visible Bars=" + IntegerToString(visibleBars) + "\n";
   data += "Recent Bars Data (Bar 1 is latest):\n";
   for (int i = 0; i < copied; i++) {
      data += "Bar " + IntegerToString(i + 1) + ": Date=" + TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES) + ", Open=" + DoubleToString(rates[i].open, _Digits) + ", High=" + DoubleToString(rates[i].high, _Digits) + ", Low=" + DoubleToString(rates[i].low, _Digits) + ", Close=" + DoubleToString(rates[i].close, _Digits) + ", Volume=" + IntegerToString((int)rates[i].tick_volume) + "\n";
   }
   Print("Chart data appended to prompt: \n" + data);
   FileWrite(logFileHandle, "Chart data appended to prompt: \n" + data);
   string fileName = "candlesticksdata.txt";
   int handle = FileOpen(fileName, FILE_WRITE | FILE_TXT | FILE_ANSI);
   if (handle == INVALID_HANDLE) {
      Print("Failed to open file for writing: ", GetLastError());
      return;
   }
   FileWriteString(handle, data);
   FileClose(handle);
   handle = FileOpen(fileName, FILE_READ | FILE_TXT | FILE_ANSI);
   if (handle == INVALID_HANDLE) {
      Print("Failed to open file for reading: ", GetLastError());
      return;
   }
   string fileContent = "";
   while (!FileIsEnding(handle)) {
      fileContent += FileReadString(handle) + "\n";
   }
   FileClose(handle);
   if (StringLen(currentPrompt) > 0) {
      currentPrompt += "\n";
   }
   currentPrompt += fileContent;
   DeletePlaceholder();
   UpdatePromptDisplay();
   p_scroll_pos = MathMax(0, p_total_height - p_visible_height);
   if (p_scroll_visible) {
      UpdatePromptSliderPosition();
      UpdatePromptButtonColors();
   }
   ChartRedraw();
}