using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using FluidSim.Components;
using FluidSim.Interfaces;

namespace FluidSim.Core
{
    public class Solver
    {
        private readonly List<IComponent> _components = new();
        private PipeSystem _pipeSystem;
        private BoundarySystem _boundarySystem;
        private double _dt;

        public int SubStepCount { get; set; } = 4;
        public bool EnableProfiling { get; set; } = false;

        private long _stepCount;
        private long _ticksOrifice, _ticksOpenEnd, _ticksPipe, _ticksUpdate;

        public void SetTimeStep(double dt) => _dt = dt;
        public void AddComponent(IComponent component) => _components.Add(component);

        public void SetPipeSystem(PipeSystem pipeSystem)
        {
            _pipeSystem = pipeSystem;
        }
        public void SetBoundarySystem(BoundarySystem boundarySystem)
        {
            _boundarySystem = boundarySystem;
        }

        public void Step()
        {
            if (_pipeSystem == null || _boundarySystem == null) return;

            int nSub = _pipeSystem.GetRequiredSubSteps((float)_dt, 0.8f);
            nSub = Math.Max(nSub, SubStepCount);   // never go below fixed minimum
            float dtSub = (float)(_dt / nSub);

            for (int sub = 0; sub < nSub; sub++)
            {
                long t0;

                t0 = Stopwatch.GetTimestamp();
                _boundarySystem.ResolveOrifices(dtSub);
                _ticksOrifice += Stopwatch.GetTimestamp() - t0;

                t0 = Stopwatch.GetTimestamp();
                _boundarySystem.ResolveOpenEnds(dtSub);
                _ticksOpenEnd += Stopwatch.GetTimestamp() - t0;

                t0 = Stopwatch.GetTimestamp();
                _pipeSystem.SimulateStep(dtSub);
                _ticksPipe += Stopwatch.GetTimestamp() - t0;
            }

            long tUS = Stopwatch.GetTimestamp();
            foreach (var comp in _components)
                comp.UpdateState((float)_dt);
            _ticksUpdate += Stopwatch.GetTimestamp() - tUS;

            _stepCount++;
            if (_stepCount % 5000 == 0 && EnableProfiling)
            {
                double freq = Stopwatch.Frequency;
                double total = _ticksOrifice + _ticksOpenEnd + _ticksPipe + _ticksUpdate;
                double avgStepUs = (total / freq) * 1e6 / 5000.0;

                int orificeCalls = 5000 * nSub;
                int updateCalls = 5000;

                double orificeMs = _ticksOrifice * 1000.0 / freq;
                double openEndMs = _ticksOpenEnd * 1000.0 / freq;
                double pipeMs = _ticksPipe * 1000.0 / freq;
                double updateMs = _ticksUpdate * 1000.0 / freq;

                double orificeAvgUs = orificeMs * 1000.0 / orificeCalls;
                double openEndAvgUs = openEndMs * 1000.0 / orificeCalls;
                double pipeAvgUs = pipeMs * 1000.0 / orificeCalls;
                double updateAvgUs = updateMs * 1000.0 / updateCalls;

                Console.WriteLine($"--- Solver ({5000} steps, nSub={nSub}) ---");
                Console.WriteLine($"  Average step: {avgStepUs:F2} µs");
                Console.WriteLine($"  Orifice:  {orificeMs:F2} ms ({(double)_ticksOrifice / total * 100:F1}%), avg {orificeAvgUs:F2} µs/call");
                Console.WriteLine($"  OpenEnd:  {openEndMs:F2} ms ({(double)_ticksOpenEnd / total * 100:F1}%), avg {openEndAvgUs:F2} µs/call");
                Console.WriteLine($"  Pipe:     {pipeMs:F2} ms ({(double)_ticksPipe / total * 100:F1}%), avg {pipeAvgUs:F2} µs/call");
                Console.WriteLine($"  Update:   {updateMs:F2} ms ({(double)_ticksUpdate / total * 100:F1}%), avg {updateAvgUs:F2} µs/call");

                // Pipe internal breakdown (with per-phase averages)
                if (_pipeSystem.EnableProfiling)
                {
                    Console.WriteLine(_pipeSystem.GetProfileReport());
                }

                _ticksOrifice = _ticksOpenEnd = _ticksPipe = _ticksUpdate = 0;
            }
        }
    }
}