#include <vector>
#include <random>

#ifndef ISING_HPP
#define ISING_HPP


const double kB = 1.0; // Boltzmann constant

class IsingModel {
public:
    // Construction & Initialization
    IsingModel(int size, double temperature, bool randomInit = true, int seed = 42);
    void initializeLattice(bool randomInit = true);

    // Simulation Control
    void monteCarloCycle();
    void run(int cycles);

    // Measurements & Observables
    double currentEnergy(bool normalized = true) const;
    double currentMagnetization(bool normalized = true) const;

    double calculateEnergy(bool normalized = true) const;
    double calculateMagnetization(bool normalized = true) const;

    double calculateEnergyVariance(bool normalized = true) const;
    double calculateMagnetizationVariance(bool normalized = true) const;

    double calculateHeatCapacity(bool normalized = true) const;
    double calculateSusceptibility(bool normalized = true) const;

    // Data Output
    void saveResults(const std::string& filename) const;
    void saveEvolution(const std::string& filename) const;

private:
    // Internal State
    int latticeSize;
    double temp; // Units here are [T] = J/kB
    std::mt19937 rng;
    std::vector<std::vector<int>> lattice;
    std::vector<std::vector<double>> evolutionData;

    std::vector<double> boltzmannFactors;

    int n_steps = 0;
    double sumEnergy = 0.0;
    double sumSquaredEnergy = 0.0;
    double sumMagnetization = 0.0;
    double sumSquaredMagnetization = 0.0;

    // Internal Helpers
    int getNeighborIndex(int i, int j, int dir, int offset = 1) const;
    int getNeighborSum(int i, int j) const;
    double getBoltzmannFactor(int neighborSum, int spin) const;
    void monteCarloStep();
    void updateAverages();
    void recordEvolution();
};
#endif

