/* orbital.h -- OpenGL window for atomic orbitals display.  -*- C++ -*-
   Copyright (C) 2025 Gerardo Ballabio

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include <vector>
#include "area.h"
#include "wavefunction.h"

#ifndef __ORBITAL_
#define __ORBITAL_

class gl_orbital
{
private:
  wavefunction wf;

  // data vectors
  std::vector<double> r, stheta, ctheta, sphi, cphi, fr, fa;
  std::vector<GLfloat> vertices;
  unsigned begin, end;
  bool queue_rand;

  int width, height;
  GLfloat matrix[16];
  GLdouble scale, rand_scale;

  // handles for OpenGL objects
  GLuint gl_program, gl_vertex_shader, gl_fragment_shader,
    gl_vertex_array, gl_buffer, gl_texture;
  // uniform variables
  GLuint u_matrix, u_sampler;

  // display mode
  bool draw_electron;
  int npoints;

  void draw_texture(int n, const GLfloat *in, GLfloat scalex, GLfloat scaley);
  void init_rand();
  void set_rand();
  void set_electron();
public:
  gl_orbital();
  void init();
  void set_orbital(int width_, int height_);
  void set_scale(double scale_) { scale = scale_; }
  void set_matrix(const GLfloat *matrix_);
  bool set_z(unsigned z_);
  bool set(int n_, int l_, int m_);
  void set_draw_electron(bool draw_);
  void set_npoints(int npoints_) { npoints = npoints_; }
  void reset();
  void update_rand();

  const unsigned get_z() { return wf.get_z(); }
  const int get_n() { return wf.get_n(); }
  const int get_l() { return wf.get_l(); }
  const int get_m() { return wf.get_m(); }
  const std::string &get_label() { return wf.get_label(); }

  const int get_nmax() { return wf.get_nmax(); }
  const int get_lmax() { return wf.get_lmax(); }

  const bool get_draw_electron() { return draw_electron; }
  const int get_npoints() { return npoints; }

  void tick();
  void draw();

  const double default_scale() { return wf.default_scale(); }
  const double default_vscale() { return wf.default_vscale(); }
  const double max_wave() { return wf.max_wave(); }

  const double wave(double x, double y, double z) { return wf.wave(x, y, z); }
};

class OrbitalArea : public Area
{
private:
  gl_orbital orbital;

  sigc::connection timeout2;
protected:
  virtual void on_realize();
  virtual bool on_timeout2() { orbital.tick(); return true; }
  virtual void on_hide() { Area::on_hide(); timeout2.disconnect(); }
  virtual void on_grid_signal_values(double vmax_);
  virtual void on_grid_signal_scale(double scale_);
  virtual void resize(int width_, int height_);
  virtual void draw();
public:
  OrbitalArea();

  bool set_z(unsigned z_);
  bool set(int n_, int l_, int m_);
  void set_draw_electron(bool draw_);
  void set_npoints(int npoints_) { orbital.set_npoints(npoints_); }

  const unsigned get_z() { return orbital.get_z(); }
  const int get_n() { return orbital.get_n(); }
  const int get_l() { return orbital.get_l(); }
  const int get_m() { return orbital.get_m(); }
  const std::string &get_label() { return orbital.get_label(); }

  const int get_nmax() { return orbital.get_nmax(); }
  const int get_lmax() { return orbital.get_lmax(); }

  const bool get_draw_electron() { return orbital.get_draw_electron(); }
  const int get_npoints() { return orbital.get_npoints(); }

  virtual void reset();
  void update_rand() { orbital.update_rand(); }
  const double default_scale() { return orbital.default_scale(); }
  const double default_vscale() { return orbital.default_vscale(); }
};

#endif /* __ORBITAL_ */
