// The top of every source code file must include this line
#include "sierrachart.h"
#include <cstdlib>

#include <string>
// For reference, refer to this page:
// https://www.sierrachart.com/index.php?page=doc/AdvancedCustomStudyInterfaceAndLanguage.php

// This line is required. Change the text within the quote
// marks to what you want to name your group of custom studies.
SCDLLName("Custom Study DLL")

	/* Example log text
		SCString  LogText;
		LogText.Format("start: %f. end: %f.", startValue, endValue);

		sc.AddMessageToLog( LogText , 0);
	*/

	SCSFExport scsf_AdamManciniLevels(SCStudyInterfaceRef sc)
{
	SCInputRef Input_SupportLevels = sc.Input[0];
	SCInputRef Input_ResistanceLevels = sc.Input[1];
	SCInputRef Input_ShowMajorLevelsOnly = sc.Input[2];
	SCInputRef Input_SupportColor = sc.Input[3];
	SCInputRef Input_ResistanceColor = sc.Input[4];
	SCInputRef Input_FontSize = sc.Input[5];
	SCInputRef Input_MajorText = sc.Input[6];
	SCInputRef Input_LineWidth = sc.Input[7];
	// Set configuration variables
	if (sc.SetDefaults)
	{
		// Set the configuration and defaults

		sc.GraphName = "Support and Resistance Levels";

		sc.StudyDescription = "Displays support and resistance levels from a comma-delimited list.";

		// Set the region to draw the graph in.  Region zero is the main
		// price graph region.
		sc.GraphRegion = 0;

		// Add new input for support and resistance levels
		Input_SupportLevels.Name = "Support Levels";
		Input_SupportLevels.SetString("");
		Input_SupportLevels.SetDescription("Comma-delimited list of support. Use '(major)' to indicate major levels.");

		Input_ResistanceLevels.Name = "Resistance Levels";
		Input_ResistanceLevels.SetString("");
		Input_ResistanceLevels.SetDescription("Comma-delimited list of resistance. Use '(major)' to indicate major levels.");

		Input_ShowMajorLevelsOnly.Name = "Show Major Levels Only";
		Input_ShowMajorLevelsOnly.SetYesNo(0);
		Input_ShowMajorLevelsOnly.SetDescription("If enabled, only major levels will be shown.");

		Input_SupportColor.Name = "Support Color";
		Input_SupportColor.SetColor(RGB(185, 0, 185));
		Input_SupportColor.SetDescription("Color of the support levels.");

		Input_ResistanceColor.Name = "Resistance Color";
		Input_ResistanceColor.SetColor(RGB(0, 185, 185));
		Input_ResistanceColor.SetDescription("Color of the resistance levels.");

		Input_FontSize.Name = "Font Size";
		Input_FontSize.SetInt(10);
		Input_FontSize.SetDescription("Font size for the levels.");

		Input_MajorText.Name = "Major Text";
		Input_MajorText.SetString("Maj");
		Input_MajorText.SetDescription("Text to display for major levels.");

		Input_LineWidth.Name = "Line Width";
		Input_LineWidth.SetInt(2);
		Input_LineWidth.SetDescription("Line width for the levels.");

		sc.AutoLoop = 0;

		sc.AlertOnlyOncePerBar = true;

		// Must return before doing any data processing if sc.SetDefaults is set
		return;
	}

	// New function to lighten a color
	auto lightenColor = [](COLORREF color, int amount)
	{
		int red = GetRValue(color);
		int green = GetGValue(color);
		int blue = GetBValue(color);
		return RGB(min(red + amount, 255), min(green + amount, 255), min(blue + amount, 255));
	};

	// New function to create and configure a tool
	auto createLevelTool = [&sc, &lightenColor, &Input_FontSize, &Input_MajorText, &Input_LineWidth](int lineNumber, float value, bool isMajor, COLORREF color)
	{
		if (isMajor)
		{
			color = lightenColor(color, 70);
		}
		s_UseTool Tool;
		Tool.LineNumber = lineNumber;
		Tool.ChartNumber = sc.ChartNumber;
		Tool.DrawingType = DRAWING_HORIZONTALLINE;
		Tool.DisplayHorizontalLineValue = 1;
		Tool.ShowPrice = true;
		Tool.Color = color;
		Tool.LineWidth = Input_LineWidth.GetInt();
		Tool.AddMethod = UTAM_ADD_OR_ADJUST;
		Tool.BeginValue = value;
		Tool.Text = isMajor ? Input_MajorText.GetString() : "";
		Tool.FontSize = Input_FontSize.GetInt();
		Tool.TextAlignment = DT_VCENTER | DT_RIGHT;
		return Tool;
	};

	// New function to create and configure a rectangle tool
	auto createRectangleTool = [&sc, &lightenColor, &Input_FontSize, &Input_MajorText, &Input_LineWidth](int lineNumber, float startValue, float endValue, bool isMajor, COLORREF color)
	{
		if (isMajor)
		{
			color = lightenColor(color, 70);
		}
		s_UseTool Tool;
		Tool.LineNumber = lineNumber;
		Tool.DrawingType = DRAWING_RECTANGLE_EXT_HIGHLIGHT;
		int BarIndex = 0;
		// Update BarIndex to 30 bars from the end
		BarIndex = max(sc.ArraySize - 100, 0);
		Tool.BeginDateTime = sc.BaseDateTimeIn[BarIndex];
		BarIndex = max(sc.ArraySize - 15, 0);
		Tool.EndDateTime = sc.BaseDateTimeIn[BarIndex];
		float minValue = min(startValue, endValue);
		float maxValue = max(startValue, endValue);
		Tool.BeginValue = minValue;
		Tool.EndValue = maxValue;
		Tool.Color = color;
		Tool.ExtendLeft = 1;
		Tool.ExtendRight = 1;
		Tool.ShowPrice = true;
		Tool.CenterPriceLabels = 1;
		Tool.Text = isMajor ? Input_MajorText.GetString() : "";
		Tool.FontSize = Input_FontSize.GetInt();
		Tool.TextAlignment = DT_RIGHT;
		Tool.LineWidth = Input_LineWidth.GetInt(); // To see the outline this must be 1 or greater.
		Tool.SecondaryColor = color;
		Tool.TransparencyLevel = 85;
		Tool.AddMethod = UTAM_ADD_OR_ADJUST;

		return Tool;
	};

	// Function to process levels (can be used for both support and resistance)
	auto processLevels = [&sc, &createLevelTool, &createRectangleTool, &Input_ShowMajorLevelsOnly](SCString levelsString, COLORREF color, int startLineNumber)
	{
		std::vector<char *> levels;
		const int NumTokens = levelsString.Tokenize(",", levels);

		for (int i = 0; i < NumTokens; i++)
		{
			SCString levelString = levels[i];
			// Trim whitespace
			while (levelString.GetLength() > 0 && isspace(levelString[0]))
				levelString = levelString.Right(levelString.GetLength() - 1);
			while (levelString.GetLength() > 0 && isspace(levelString[levelString.GetLength() - 1]))
				levelString = levelString.Left(levelString.GetLength() - 1);

			bool isMajor = levelString.Right(7).CompareNoCase("(major)") == 0;
			if (isMajor)
				levelString = levelString.Left(levelString.GetLength() - 7);
			if (Input_ShowMajorLevelsOnly.GetYesNo() && !isMajor)
			{
				continue;
			}
			std::vector<char *> levelParts;
			levelString.Tokenize(" ", levelParts);
			if (!levelParts.empty())
			{
				s_UseTool Tool;
				SCString levelPartString = levelParts[0];
				std::string stdLevelString = levelString.GetChars();
				bool isRange = stdLevelString.find("-") != std::string::npos;

				if (isRange)
				{
					// Process as a rectangle
					std::vector<char *> rangeParts;
					levelPartString.Tokenize("-", rangeParts);
					if (rangeParts.size() == 2)
					{
						float startValue = static_cast<float>(atof(rangeParts[0]));
						float endValue;
						size_t startLen = strlen(rangeParts[0]);
						if (strlen(rangeParts[1]) == 2 && startLen > 2)
						{
							char combinedValue[20] = {0}; // Increased buffer size and zero-initialized
							strncpy(combinedValue, rangeParts[0], startLen - 2);
							strcat(combinedValue, rangeParts[1]);
							endValue = static_cast<float>(atof(combinedValue));
						}
						else
						{
							endValue = static_cast<float>(atof(rangeParts[1]));
						}
						Tool = createRectangleTool(startLineNumber + i, startValue, endValue, isMajor, color);
					}
					else
					{
						sc.AddMessageToLog(SCString("Invalid range format: ") + levelString, 1);
					}
				}
				else
				{
					// Process as a horizontal line
					float value = static_cast<float>(atof(levelParts[0]));
					Tool = createLevelTool(startLineNumber + i, value, isMajor, color);
				}

				try
				{
					sc.UseTool(Tool);
				}
				catch (const std::invalid_argument &e)
				{
					sc.AddMessageToLog(SCString("Invalid level: ") + levelString, 1);
				}
			}
			else
			{
				sc.AddMessageToLog(SCString("Invalid level format: ") + levelString, 1);
			}
		}
	};

	// Process support levels
	processLevels(Input_SupportLevels.GetString(), Input_SupportColor.GetColor(), 1000);

	// Process resistance levels
	processLevels(Input_ResistanceLevels.GetString(), Input_ResistanceColor.GetColor(), 2000);
}