using System;
using System.Collections.Generic;
using System.Threading;
using TradingPlatform.BusinessLayer;
using TradingPlatform.BusinessLayer.Integration;

namespace SyntheticVendorNamespace
{
    public class SyntheticVendor : Vendor
    {
        private readonly List<MessageExchange> exchanges;
        private readonly List<MessageAsset> assets;
        private readonly List<MessageSymbol> symbols;

        public SyntheticVendor()
        {
            exchanges = new List<MessageExchange>
            {

                //Spike,
                //Impulse,
                //Triangle,
                //Sawtooth
                //Sine
                //Chirp
                //White
                //Gauss
                //B
                //HF
                //Impulse+HF,
                //Sawtooth+HF
                //Sine+G
                //Chirp+G
                //Complex
                //Market

                new MessageExchange { Id = "PU", ExchangeName = "1 Pulse" },
                new MessageExchange { Id = "WA", ExchangeName = "2 Wave" },
                new MessageExchange { Id = "MD", ExchangeName = "3 Modulation" },
                new MessageExchange { Id = "NO", ExchangeName = "4 Noise" },
                new MessageExchange { Id = "BR", ExchangeName = "5 Brownian" },
                new MessageExchange { Id = "QT", ExchangeName = "6 QuanTAlib" }
            };

            assets = new List<MessageAsset>
            {
                new MessageAsset { Id = "USD", Name = "USD" },

            };

            symbols = new List<MessageSymbol>
            {
                CreateMessageSymbol(id: "W1",  name: "1 Digital spike", exchangeId: "QT", assetId: "USD", type: SymbolType.Crypto,
                    description: "Sudden sharp spike in the signal"),
                CreateMessageSymbol("W2",  "2 Dirac delta spike", "QT", "USD", SymbolType.Crypto),
                CreateMessageSymbol("W8",  "4 Sinc pulse", "QT", "USD",  SymbolType.Crypto),

                CreateMessageSymbol("W3",  "1 Square Wave", "QT", "USD", SymbolType.ETF),
                CreateMessageSymbol("W4",  "2 Sawtooth Wave", "QT", "USD", SymbolType.ETF),
                CreateMessageSymbol("W5",  "3 Inverse sawtooth Wave", "QT", "USD", SymbolType.ETF),
                CreateMessageSymbol("W6",  "4 Triangle Wave", "QT", "USD", SymbolType.ETF),
                CreateMessageSymbol("W7",  "5 Sine Wave", "QT", "USD", SymbolType.ETF),

                CreateMessageSymbol("W11", "1 Amplitude modulation", "QT", "USD",  SymbolType.Forex),
                CreateMessageSymbol("W10", "2 Frequency sweep", "QT", "USD",  SymbolType.Forex),
                CreateMessageSymbol("W12", "3 Frequency modulation", "QT", "USD",  SymbolType.Forex),

                CreateMessageSymbol("W13", "1 White noise", "QT", "USD",  SymbolType.Indexes),
                CreateMessageSymbol("W14", "2 Pink noise", "QT", "USD",  SymbolType.Indexes),
                CreateMessageSymbol("W15", "3 Brown noise", "QT", "USD",  SymbolType.Indexes),

                CreateMessageSymbol("W16", "1 Fractional Brownian motion", "QT", "USD",  SymbolType.Synthetic),
                CreateMessageSymbol("W17", "2 Geometric Brownian motion", "QT", "USD",  SymbolType.Synthetic)
            };

/*
    Bond,
    CFD,
        Crypto,
    Debentures,
    Equities,
        ETF,
    FixedIncome,
        Forex,
    Forward,
    Futures,
        Indexes,
    Options,
    Spot,
        Synthetic,
    Swap,
    Warrants,

*/



        }

        private MessageSymbol CreateMessageSymbol(
            string id,
            string name,
            string exchangeId,
            string assetId,
            SymbolType type,
            string description)
        {
            var messageSymbol = new MessageSymbol(id)
            {
                Name = name,
                Description = description,
                SymbolType = type,
                ExchangeId = exchangeId,
                ProductAssetId = assetId,

                // Setting some default values
                QuotingCurrencyAssetID = "USD",
                HistoryType = HistoryType.Last,
                DeltaCalculationType = DeltaCalculationType.TickDirection,
                LotSize = 1,
                VariableTickList = new List<VariableTick>
                {
                    new VariableTick(0.01) // Default tick size
                }
            };

            return messageSymbol;
        }

        public static VendorMetaData GetVendorMetaData()
        {
            return new VendorMetaData()
            {
                VendorName = "Synthetic Vendor",
                VendorDescription = "A synthetic vendor for testing and demonstration purposes",
                GetDefaultConnections = () =>
                {
                    var defaultConnection = Vendor.CreateDefaultConnectionInfo(
                        "Synthetic Connection",
                        "Synthetic Vendor",
                        "", // Replace with actual path if you have a logo
                        allowCreateCustomConnections: true
                    );
                    return new List<ConnectionInfo> { defaultConnection };
                }
            };
        }


        private MessageSymbol CreateMessageSymbol(string id, string name, string exchangeId, string assetId, SymbolType type)
        {
            return new MessageSymbol(id)
            {
                Name = name,
                ExchangeId = exchangeId,
                ProductAssetId = assetId,
                QuotingCurrencyAssetID = "USD",
                QuotingType = SymbolQuotingType.LotSize,
                LotSize = 1,
                NettingType = NettingType.OnePosition,
                VolumeType = SymbolVolumeType.Volume,
                AllowCalculateRealtimeTicks = true,
                AllowCalculateRealtimeTrades = false,
                AllowCalculateRealtimeVolume = true,
                AllowCalculateRealtimeChange = true,
                AllowAbbreviatePriceByTickSize = false,
                NotionalValueStep = 0.01,
                DeltaCalculationType = DeltaCalculationType.AggressorFlag, // Changed from None to AggressorFlag
                MinVolumeAnalysisTickSize = 0.01,
                MaturityDate = DateTime.MaxValue, // Set to max value for non-expiring symbols
                HistoryType = HistoryType.Last,
                MinLot = 0.01,
                LotStep = 0.01,
                MaxLot = 1000000,
                SymbolType = type
/*
    SymbolType.Unknown,
    [EnumMember] Forex,
    [EnumMember] Equities,
    [EnumMember] CFD,
    [EnumMember] Indexes,
    [EnumMember] Futures,
    [EnumMember] Options,
    [EnumMember] ETF,
    [EnumMember] Crypto,
    [EnumMember] Synthetic,
    [EnumMember] Spot,
    [EnumMember] Forward,
    [EnumMember] FixedIncome,
    [EnumMember] Warrants,

    [EnumMember] Debentures,
    [EnumMember] Bond,
    [EnumMember] Swap,
*/
            };
        }

        public override ConnectionResult Connect(ConnectRequestParameters connectRequestParameters)
        {
            // Simulating connection process
            Thread.Sleep(100); // Simulate some connection delay

            return ConnectionResult.CreateSuccess("Successfully connected to Synthetic Vendor");
        }

        public override void Disconnect()
        {
            // Simulating disconnection process
            Thread.Sleep(500); // Simulate some disconnection delay

        }

        public override PingResult Ping()
        {
        return new PingResult()
            {
                State = PingEnum.Connected,
                PingTime = TimeSpan.FromMilliseconds(2),
                RoundTripTime = TimeSpan.FromMilliseconds(2)
            };
        }




        public override void OnConnected(CancellationToken token)
        {
            // This method is called after a successful connection
            // You can initialize resources or start any necessary processes here
            base.OnConnected(token);

            // For example, you might want to push some initial messages or data
           // PushMessage(new MessageVendorEvent("SyntheticVendor connected successfully"));
        }



        public override IList<MessageExchange> GetExchanges(CancellationToken token)
        {
            return exchanges;
        }

        public override IList<MessageAsset> GetAssets(CancellationToken token)
        {
            return assets;
        }

        public override IList<MessageSymbol> GetSymbols(CancellationToken token)
        {
            return symbols;
        }

        public override void SubscribeSymbol(SubscribeQuotesParameters parameters)
        {
            // Empty method for data subscription to be filled later
        }

        public override void UnSubscribeSymbol(SubscribeQuotesParameters parameters)
        {
            // Empty method for data unsubscription to be filled later
        }


        public override IList<IHistoryItem> LoadHistory(HistoryRequestParameters requestParameters)
        {
            var historyItems = new List<IHistoryItem>();
            var symbolId = requestParameters.SymbolId;

            if (string.IsNullOrEmpty(symbolId)) return historyItems;

            DateTime from = requestParameters.FromTime;
            DateTime to = requestParameters.ToTime;

            TimeSpan periodTimeSpan = requestParameters.Aggregation.GetPeriod.Duration;

            // Define the maximum number of items to generate per request
            const int MAX_ITEMS_PER_REQUEST = 10000;

            Func<DateTime, TimeSpan, HistoryItemBar> waveGenerator = GetWaveGenerator(symbolId);

            DateTime currentTime = from;
            while (currentTime < to)
            {
                DateTime intervalEnd = currentTime.AddTicks(periodTimeSpan.Ticks * MAX_ITEMS_PER_REQUEST);
                if (intervalEnd > to)
                    intervalEnd = to;

                while (currentTime <= intervalEnd)
                {
                    var historyItem = waveGenerator(currentTime, periodTimeSpan); //calling generator fuction
                    historyItems.Add(historyItem);

                    currentTime = currentTime.Add(periodTimeSpan);

                    if (requestParameters.CancellationToken.IsCancellationRequested) return historyItems;
                }

                currentTime = intervalEnd;
            }

            return historyItems;
        }

        private Func<DateTime, TimeSpan, HistoryItemBar> GetWaveGenerator(string symbolId)
        {
            switch (symbolId)
            {
                //case "W0": return GenerateConstant;
                case "W1": return GenerateSpike;
                case "W2": return GenerateDiracDelta;
                case "W3": return GenerateSquareWave;
                case "W4": return GenerateSawtoothWave;
                case "W5": return GenerateInverseSawtoothWave;
                case "W6": return GenerateTriangleWave;
                case "W7": return GenerateSineWave;
                case "W8": return GenerateSincWave;
                case "W9": return GenerateGaussianPulse;
                case "W10": return GenerateFrequencySweep;
                case "W11": return GenerateAMSignal;
                case "W12": return GenerateFMSignal;
                case "W13": return GenerateWhiteNoise;
                case "W14": return GeneratePinkNoise;
                case "W15": return GenerateBrownNoise;
                case "W16": return GenerateFBM;
                case "W17": return GenerateGBM;

                default: return GenerateSineWave;
            }
        }

        public override HistoryMetadata GetHistoryMetadata(CancellationToken cancellationToken)
        {
            return new HistoryMetadata()
            {
                AllowedHistoryTypes = new HistoryType[]
                {
                    HistoryType.Bid,
                    HistoryType.Ask,
                    HistoryType.Midpoint,
                    HistoryType.Last,
                    HistoryType.BidAsk,
                    HistoryType.Mark,
                },
                AllowedPeriods = new Period[]
                {
                Period.TICK1,
                Period.SECOND1, Period.SECOND5, Period.SECOND10, Period.SECOND15, Period.SECOND30,
                Period.MIN1, Period.MIN2, Period.MIN3, Period.MIN4, Period.MIN5,
                Period.MIN10, Period.MIN15, Period.MIN30,
                Period.HOUR1, Period.HOUR2, Period.HOUR3, Period.HOUR4,
                Period.HOUR6, Period.HOUR8, Period.HOUR12,
                Period.DAY1,
                Period.WEEK1,
                Period.MONTH1,
                Period.YEAR1
                },
                UseHistoryLocalCache = false
            };
        }


/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/
/*******************************************************************************************************************************************/

    private HistoryItemBar GenerateSpike(DateTime time, TimeSpan slice)
    {
        // Ensure we're working with UTC time
        DateTime utcTime = time.ToUniversalTime();

        // Calculate the number of hours since the epoch
        double hoursSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalHours;

        // Calculate the position within the 25-hour cycle
        int cyclePosition = (int)Math.Floor(hoursSinceEpoch % 25);

        // Determine if this is a spike hour (hour 24 in the cycle) or the hour after
        bool isSpike = cyclePosition == 24;
        bool isAfterSpike = cyclePosition == 0;

        double openValue, closeValue;
        if (isSpike)
        {
            openValue = 0;
            closeValue = 100;
        }
        else if (isAfterSpike)
        {
            openValue = 100;
            closeValue = 0;
        }
        else
        {
            openValue = closeValue = 0.000001;
        }

        return new HistoryItemBar
        {
            TicksLeft = time.Ticks,
            TicksRight = time.Add(slice).Ticks - 1,
            Open = openValue,
            High = Math.Max(openValue, closeValue),
            Low = Math.Min(openValue, closeValue),
            Close = closeValue,
            Volume = Math.Abs(closeValue - openValue),
            Ticks = time.Add(slice).Ticks - time.Ticks
        };
    }




        private static readonly double[] distributionValues = new double[]
        {
            0.010,  // Extreme left tail
            0.050,  // Left tail
            0.200,  // Left of center
            0.480,  // Center (peak)
            0.200,  // Right of center
            0.050,  // Right tail
            0.010   // Extreme right tail
        };

        private HistoryItemBar GenerateDiracDelta(DateTime time, TimeSpan slice)
        {
            // Ensure we're working with UTC time
            DateTime utcTime = time.ToUniversalTime();

            // Calculate the start of the current day
            DateTime dayStart = utcTime.Date;

            // Determine which bar of the day we're on
            int barOfDay = (int)((utcTime - dayStart).Ticks / slice.Ticks);

            double openValue, closeValue;
            double scaleFactor = 100; // Scale factor to convert to percentage

            // Generate the spike pattern for the first 4 bars of each day
            switch (barOfDay)
            {
                case 0:
                    openValue = 0.000001 * scaleFactor;
                    closeValue = 0.05 * scaleFactor;
                    break;
                case 1:
                    openValue = 0.05 * scaleFactor;
                    closeValue = 0.50 * scaleFactor;
                    break;
                case 2:
                    openValue = 0.50 * scaleFactor;
                    closeValue = 0.05 * scaleFactor;
                    break;
                case 3:
                    openValue = 0.05 * scaleFactor;
                    closeValue = 0.0000001 * scaleFactor;
                    break;
                default:
                    // Outside of the spike period, use baseline value
                    openValue = closeValue = 0.000001;
                    break;
            }

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = openValue,
                High = Math.Max(openValue, closeValue),
                Low = Math.Min(openValue, closeValue),
                Close = closeValue,
                Volume = Math.Abs(closeValue - openValue),
                Ticks = time.Add(slice).Ticks - time.Ticks
            };
        }



        private HistoryItemBar GenerateSineWave(DateTime time, TimeSpan slice)
        {
            // Ensure we're working with UTC time
            DateTime utcTime = time.ToUniversalTime();

            // Calculate the number of hours since the epoch
            double minutesSinceEpoch = (utcTime - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMinutes;

            // Calculate the position within the 25-hour cycle
            double cyclePosition = minutesSinceEpoch % 1500;


            // Calculate the sine wave values
            double frequency = 2 * Math.PI / 1500; // Complete cycle over 25 hours
            double value = 50 + 50 * Math.Sin(cyclePosition * frequency); // Oscillate between 0 and 100
            double nextValue = 50 + 50 * Math.Sin((cyclePosition + slice.TotalMinutes) * frequency);

            double factor = 0.6 * Math.Abs (nextValue - value);

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,

                High = Math.Max(value, nextValue)+factor,
                Low =  Math.Min(value, nextValue)-factor,

                Close = nextValue,
                Volume = Math.Abs(nextValue - value) * 100, // Volume proportional to price change
                Ticks = time.Add(slice).Ticks - time.Ticks
            };
        }


        private HistoryItemBar GenerateSquareWave(DateTime time, TimeSpan slice)
        {
            // Ensure we're working with UTC time
            DateTime utcTime = time.ToUniversalTime();

            // Calculate the time within the day (in hours)
            double hoursInDay = utcTime.TimeOfDay.TotalHours;

            double openValue, closeValue;

            if (hoursInDay < 12)
            {
                // First half of the day
                openValue = 99;
                closeValue = 100;
            }
            else
            {
                // Second half of the day
                openValue = 1;
                closeValue = 0.0001;
            }

            // Handle transition bars
            if (Math.Abs(hoursInDay - 12) < slice.TotalHours / 2)
            {
                // Transition from 100 to 0 at noon
                openValue = 100;
                closeValue = 0.0001;
            }
            else if (hoursInDay < slice.TotalHours / 2 || hoursInDay > 24 - slice.TotalHours / 2)
            {
                // Transition from 0 to 100 at midnight
                openValue = 0.0001;
                closeValue = 100;
            }
            else
            {
                // No action
            }

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = openValue,
                High = Math.Max(openValue, closeValue),
                Low = Math.Min(openValue, closeValue),
                Close = closeValue,
                Volume = Math.Abs(closeValue - openValue),
                Ticks = time.Add(slice).Ticks - time.Ticks
            };
        }

        private HistoryItemBar GenerateSawtoothWave(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double period = 24; // 24-hour period
            double position = hours % period;
            double value = 200 * (position / period) - 100;
            double nextValue = 200 * ((position + slice.TotalHours) % period / period) - 100;

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue,
                Volume = 100,
                Ticks = 100
            };
        }

        private HistoryItemBar GenerateInverseSawtoothWave(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double period = 24; // 24-hour period
            double position = hours % period;
            double value = 100 - (200 * (position / period));
            double nextValue = 100 - (200 * ((position + slice.TotalHours) % period / period));

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue,
                Volume = 100,
                Ticks = 100
            };
        }

        private HistoryItemBar GeneratePulseWave(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double period = 24; // 24-hour period
            double position = hours % period;
            double value = position < period / 5 ? 100 : -100; // 20% duty cycle

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = 100,
                Low = -100,
                Close = value,
                Volume = 100,
                Ticks = 100
            };
        }

        private HistoryItemBar GenerateTriangleWave(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double period = 24;
            double position = hours % period;
            double value = 200 * (Math.Abs(position / period - 0.5) - 0.25) * 100;
            double nextValue = 200 * (Math.Abs(((position + slice.TotalHours) % period) / period - 0.5) - 0.25) * 100;

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue,
                Volume = 100,
                Ticks = 100
            };
        }

        private HistoryItemBar GenerateSincWave(DateTime time, TimeSpan slice)
        {
            double minutes = (time - DateTime.UnixEpoch).TotalMinutes;
            double period = 1500.0; // 24-hour period
            double frequency = 2 * Math.PI / period; // Full cycle over 24 hours

            // Adjust time to center the main peak at 12 hours
            double t = minutes % period - period / 2;

            // Scale factor
            double scaleFactor = 7.0;

            // Calculate Sinc value
            double x = scaleFactor * frequency * t;
            double sincValue = x != 0 ? 100 * Math.Sin(x) / x : 100;

            // Calculate next value
            double nextT = ((minutes + slice.TotalMinutes) % period) - period / 2;
            double nextX = scaleFactor * frequency * nextT;
            double nextSincValue = nextX != 0 ? 100 * Math.Sin(nextX) / nextX : 100;

            // Ensure minimum value
            double minValue = 0.00001;
            sincValue = Math.Sign(sincValue) * Math.Max(Math.Abs(sincValue), minValue);
            nextSincValue = Math.Sign(nextSincValue) * Math.Max(Math.Abs(nextSincValue), minValue);

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = sincValue,
                High = Math.Max(sincValue, nextSincValue),
                Low = Math.Min(sincValue, nextSincValue),
                Close = nextSincValue,
                Volume = Math.Abs(nextSincValue - sincValue), // Volume as the change in value
                Ticks = slice.Ticks
            };
        }

        private HistoryItemBar GenerateGaussianPulse(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double totalPeriod = 24.0; // 24-hour total cycle
            double pulsePeriod = 12.0; // 12-hour pulse duration
            double position = hours % totalPeriod;

            // Parameters for the Gaussian pulse
            double amplitude = 100.0; // Maximum amplitude
            double center = pulsePeriod / 2.0; // Center of the pulse (at 6 hours within the pulse period)
            double width = pulsePeriod / 6.0; // Width of the pulse (adjusts the spread)

            double baselineValue = 0.00001; // Value outside the pulse period

            // Calculate the Gaussian pulse value
            double value;
            if (position < pulsePeriod)
            {
                value = amplitude * Math.Exp(-Math.Pow(position - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue;
            }
            else
            {
                value = baselineValue;
            }

            // Calculate the next value for the slice
            double nextPosition = (hours + slice.TotalHours) % totalPeriod;
            double nextValue;
            if (nextPosition < pulsePeriod)
            {
                nextValue = amplitude * Math.Exp(-Math.Pow(nextPosition - center, 2) / (2 * Math.Pow(width, 2))) + baselineValue;
            }
            else
            {
                nextValue = baselineValue;
            }

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue,
                Volume = Math.Abs(nextValue - value), // Volume as the change in value
                Ticks = slice.Ticks
            };
        }

        private HistoryItemBar GenerateFrequencySweep(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double sweepPeriod = 48.0; // 48-hour period

            // Starting frequency (very low)
            double minFreq = Math.PI / 48.0;

            // Calculate the ending frequency to ensure continuity
            double maxFreq = Math.PI * 1.0 * Math.Exp(2 * Math.PI / sweepPeriod);

            // Calculate the exponential factor for frequency sweep
            double expFactor = Math.Log(maxFreq / minFreq) / sweepPeriod;

            // Calculate the overall phase up to the current time
            double totalPhase = (minFreq / expFactor) * (Math.Exp(expFactor * (hours % sweepPeriod)) - 1);

            // Shift the phase to start the cycle at 100 (cosine-like behavior)
            totalPhase += Math.PI / 2;

            // Calculate the value of the signal at the current time
            double value = 100.0 * Math.Sin(totalPhase);

            // Calculate the value of the signal at the end of the slice
            double nextPhase = (minFreq / expFactor) * (Math.Exp(expFactor * ((hours + slice.TotalHours) % sweepPeriod)) - 1);
            nextPhase += Math.PI / 2; // Apply the same phase shift
            double nextValue = 100.0 * Math.Sin(nextPhase);

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue,
                Volume = Math.Abs(nextValue - value), // Volume as the change in value
                Ticks = slice.Ticks
            };
        }

#pragma warning disable S2245
        // NOSONAR
        readonly Random random = new Random();
#pragma warning restore S2245
        private double currentAmplitude = 100;
        private HistoryItemBar GenerateAMSignal(DateTime time, TimeSpan slice)
        {
            double hours = (time - DateTime.UnixEpoch).TotalHours;
            double period = 12.0;
            double frequency = 2 * Math.PI / period; // Frequency for a 5-hour period

            // Determine the start of the current 5-hour cycle
            double cycleStartTime = Math.Floor(hours / period) * period;

            // Calculate the phase of the signal within the current 5-hour cycle
            double phase = frequency * (hours % period);

            // If we're at the start of a new 5-hour cycle, generate a new amplitude
            if (hours % period == 0)
            {
                currentAmplitude = random.NextDouble() * 100;
            }

            // Calculate the value of the signal at the current time
            double value = currentAmplitude * Math.Sin(phase);

            // Calculate the value of the signal at the end of the slice
            double nextPhase = frequency * ((hours + slice.TotalHours) % period);
            double nextValue = currentAmplitude * Math.Sin(nextPhase);

            // Create the HistoryItemBar
            var historyItem = new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = value,
                High = Math.Max(value, nextValue),
                Low = Math.Min(value, nextValue),
                Close = nextValue, // Set Close to the newly calculated value
                Volume = Math.Abs(nextValue), // Volume as the change in value
                Ticks = slice.Ticks
            };

            return historyItem;
        }


        private double currentFrequency = Math.PI / 220.0; // Initial frequency
        private double accumulatedPhase = 0;
        private double lastCloseValue = 0; // To store the last close value

        private HistoryItemBar GenerateFMSignal(DateTime time, TimeSpan slice)
        {
            double amplitude = 100.0; // Maximum amplitude
            double minFreq = Math.PI / 256.0;
            double maxFreq = Math.PI / 32.0;

            // Randomly adjust the frequency
            double frequencyStep = (maxFreq - minFreq) * 0.2; // 20% of the frequency range
            currentFrequency += (random.NextDouble() - 0.5) * 2 * frequencyStep;
            currentFrequency = Math.Max(minFreq, Math.Min(maxFreq, currentFrequency)); // Clamp frequency

            // Calculate phase increment for this slice
            double phaseIncrement = currentFrequency * slice.TotalHours;

            // Calculate the open value (which is the last close value)
            double openValue = lastCloseValue;

            // Calculate the close value
            accumulatedPhase += phaseIncrement;
            double closeValue = amplitude * Math.Sin(2 * Math.PI * accumulatedPhase);

            // Determine high and low values
            double midPhase = accumulatedPhase - (phaseIncrement / 2);
            double midValue = amplitude * Math.Sin(2 * Math.PI * midPhase);
            double highValue = Math.Max(Math.Max(openValue, closeValue), midValue);
            double lowValue = Math.Min(Math.Min(openValue, closeValue), midValue);

            // Store the close value for the next iteration
            lastCloseValue = closeValue;

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = openValue,
                High = highValue,
                Low = lowValue,
                Close = closeValue,
                Volume = Math.Abs(closeValue - openValue), // Volume as the change in value
                Ticks = slice.Ticks
            };
        }


        private HistoryItemBar GenerateWhiteNoise(DateTime time, TimeSpan slice)
        {
            double volatility = 2;
            double meanReversionStrength = 0.1;

            double openNoise = random.NextDouble();
            double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose);
            double closeNoise = random.NextDouble();
            double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open);

            // Determine High and Low
            double high = Math.Max(open, close);
            double low = Math.Min(open, close);

            // Add variation to High and Low
            double highNoise = Math.Abs(random.NextDouble());
            high += volatility * highNoise;

            double lowNoise = Math.Abs(random.NextDouble());
            low -= volatility * lowNoise;

            double volume = Math.Abs(random.NextDouble()) * 1000 + 100;

            previousClose = close;


            // Create the HistoryItemBar
            var historyItem = new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = open,
                High = high,
                Low = low,
                Close = close,
                Volume = volume,
                Ticks = slice.Ticks
            };

            return historyItem;
        }



private double previousClose = 50;
private const double meanPrice = 50;

private HistoryItemBar GeneratePinkNoise(DateTime time, TimeSpan slice)
{
    double volatility = 2;
    double meanReversionStrength = 0.1;

    // Generate open price
    double openNoise = GeneratePinkNoiseValue();
    double open = previousClose + volatility * openNoise + meanReversionStrength * (meanPrice - previousClose);

    // Generate close price
    double closeNoise = GeneratePinkNoiseValue();
    double close = open + volatility * closeNoise + meanReversionStrength * (meanPrice - open);

    // Determine High and Low
    double high = Math.Max(open, close);
    double low = Math.Min(open, close);

    // Add variation to High and Low
    double highNoise = Math.Abs(GeneratePinkNoiseValue());
    high += volatility * highNoise;

    double lowNoise = Math.Abs(GeneratePinkNoiseValue());
    low -= volatility * lowNoise;

    double volume = Math.Abs(GeneratePinkNoiseValue()) * 1000 + 100;

    // Update previous close for the next iteration
    previousClose = close;

    return new HistoryItemBar
    {
        TicksLeft = time.Ticks,
        TicksRight = time.Add(slice).Ticks - 1,
        Open = open,
        High = high,
        Low = low,
        Close = close,
        Volume = volume,
        Ticks = slice.Ticks
    };
}


    private const int NumOctaves = 6;
    private double[] pinkNoiseState = new double[NumOctaves];
    private double GeneratePinkNoiseValue()
    {
        double total = 0;

        for (int i = 0; i < NumOctaves; i++)
        {
            double white = random.NextDouble() * 2 - 1;
            pinkNoiseState[i] = (pinkNoiseState[i] + white) * 0.5;
            total += pinkNoiseState[i] * Math.Pow(2, -i);
        }

        // Normalize
        return total / NumOctaves;
    }



private double lastValue = 0;

private HistoryItemBar GenerateBrownNoise(DateTime time, TimeSpan slice)
{
    double dt = slice.TotalDays / 365.0; // Time step in years
    double sigma = 25.0; // Annual volatility

    double increment = GenerateGaussian(0, sigma * Math.Sqrt(dt));
    double open = lastValue * (1 + GenerateGaussian(0, 0.05));
    double close = open + increment;

            // Simulate intra-period high and low
            double high = Math.Max(open, close);
            high += high * Math.Abs(GenerateGaussian(0, 0.06));
            double low = Math.Min(open, close);
            low -= low * Math.Abs(GenerateGaussian(0, 0.06));

    lastValue = close;

    return new HistoryItemBar
    {
        TicksLeft = time.Ticks,
        TicksRight = time.Add(slice).Ticks - 1,
        Open = open,
        High = high,
        Low = low,
        Close = close,
        Volume = Math.Abs(close - open) * 1000, // Simplified volume calculation
        Ticks = slice.Ticks
    };
}
// Helper method to generate Gaussian distributed random numbers
private double GenerateGaussian(double mean, double stdDev)
{
    double u1 = 1.0 - random.NextDouble(); // Uniform(0,1] random doubles
    double u2 = 1.0 - random.NextDouble();
    double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
    return mean + stdDev * randStdNormal;
}



private double GBMLastClose = 100; // Starting price
private double GBMMu = 0.05; // Annual drift
private double GBMSigma = 0.2; // Annual volatility

private HistoryItemBar GenerateGBM(DateTime time, TimeSpan slice)
{
    // Convert time slice to years
    double dt = slice.TotalDays / 365.0;

    // Generate a random normal variable for the main price movement
    double epsilon = GenerateGaussian(0, 1);

    // Calculate the price movement using GBM equation
    double drift = (GBMMu - 0.5 * GBMSigma * GBMSigma) * dt;
    double diffusion = GBMSigma * Math.Sqrt(dt) * epsilon;
    double returnValue = Math.Exp(drift + diffusion);

    // Add variability between previous close and current open
    double openVariability = GBMLastClose * GBMSigma * Math.Sqrt(dt) * GenerateGaussian(0, 1) * 0.1;
    double open = GBMLastClose + openVariability;

    // Calculate new close price
    double close = open * returnValue;

    // Generate High and Low values
    double highLowRange = Math.Max(Math.Abs(close - open), GBMLastClose * GBMSigma * Math.Sqrt(dt) * Math.Abs(GenerateGaussian(0, 1)));
    double high = Math.Max(open, close) + highLowRange * 0.5;
    double low = Math.Min(open, close) - highLowRange * 0.5;

    // Generate volume (you may want to adjust this based on your needs)
    double volume = Math.Max(100, 1000 * Math.Abs(close - open) + 500 * GenerateGaussian(0, 1));

    // Update last close for next iteration
    GBMLastClose = close;

    return new HistoryItemBar
    {
        TicksLeft = time.Ticks,
        TicksRight = time.Add(slice).Ticks - 1,
        Open = open,
        High = high,
        Low = low,
        Close = close,
        Volume = volume,
        Ticks = slice.Ticks
    };
}

        private double FBMLastClose = 100; // Starting price
        private double FBMHurst = 0.85; // Hurst parameter (0.5 < H < 1 for persistent fBm)
        private double FBMSigma = 0.25; // Volatility parameter
        private double FBMDrift = 0.001; // drift

        private HistoryItemBar GenerateFBM(DateTime time, TimeSpan slice)
        {
            double dt = Math.Pow(slice.TotalDays / 365.0, 0.5);

            double epsilon = GenerateFractionalGaussianNoise(FBMHurst);

            double drift = FBMDrift * dt;
            double diffusion = FBMSigma * Math.Pow(dt, FBMHurst) * epsilon;

            double openVariability = FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * GenerateFractionalGaussianNoise(FBMHurst) * 0.1;
            double open = FBMLastClose + openVariability;

            double close = open * Math.Exp(drift + diffusion);

            double highLowRange = Math.Max(Math.Abs(close - open),
                FBMLastClose * FBMSigma * Math.Pow(dt, FBMHurst) * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst)) * 2);
            double high = Math.Max(open, close) + highLowRange * 0.5;
            double low = Math.Min(open, close) - highLowRange * 0.5;

            double volume = Math.Max(100, 2000 * Math.Abs(close - open) +
                1000 * Math.Abs(GenerateFractionalGaussianNoise(FBMHurst)));

            FBMLastClose = close;

            return new HistoryItemBar
            {
                TicksLeft = time.Ticks,
                TicksRight = time.Add(slice).Ticks - 1,
                Open = open,
                High = high,
                Low = low,
                Close = close,
                Volume = volume,
                Ticks = slice.Ticks
            };
        }

        private double GenerateFractionalGaussianNoise(double hurst)
        {
            double sum = 0;
            int n = 1000; // Number of terms in the approximation

            for (int i = 1; i <= n; i++)
            {
                double ri = GenerateGaussian(0, 1);
                sum += (Math.Pow(i, hurst - 0.5) - Math.Pow(i - 1, hurst - 0.5)) * ri;
            }

            return sum / Math.Sqrt(n);
        }



        // Add other necessary overrides and implementations as needed
    }
}