/*
 *  Copyright (C) 2008 Kristian Kristola
 *
 *  This program is distributed under the terms of the
 *  GNU General Public License.
 *
 *  This file is part of Dave the Ordinary Spaceman.
 *
 *  Dave the Ordinary Spaceman 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.
 *
 *  Dave the Ordinary Spaceman 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 Dave the Ordinary Spaceman. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#ifndef _PAL_SECAM_NTSC_
#define _PAL_SECAM_NTSC_


#include "opengl.h"
#include "image.h"

class Effect
{
private:
  static GLFramebuffer *framebuffer;
  static GLColorTexture2D *ping, *pong;
  static GLColorTexture2D *current_tex;
  static GLColorTexture2D *not_current_tex();
  static void flip();                    // Vaihtaa currentin toiseen
  static void init();                    // Alustaa framebufferin ja tekstuurit

protected:
  static void fbo_chain_begin();                   // Pingpongaa tekstuurin ja bindaa framebufferin
  static void fbo_chain_end();                     // Unbindaa framebufferin
  static void fbo_accum_begin();                   // Akkumuloi samaan tekstuuriin mihin edellinen tehtiin ja bindaa fb:n
  static void fbo_accum_end();                     // Unbindaa framebufferin
  static void fbo_gen_mipmaps();                   // Generoi nykyiselle framelle mipmapit
  static void fbo_reset();                         // Tekee uudet tekstuurit että päästään mipmapeista eroon
  static void bind_fbo_texture();                  // Bindaa tekstuurin
  static void bind_other_fbo_texture(int unit=0);  // Bindaa edellisen tekstuurin jotta sillä voi piirtää seuraavan efektin
  static bool head_or_tails();                     // Palauttaa truen joka toisella ketjun nodella

public:
  static void draw_fbo(const bool flip = false);   // Piirtää sen ruudulle mitä tässä vaiheessa tuli tekstuuriin

  Effect();
  virtual ~Effect();

  virtual void render();
};

class Power : public Effect
{
public:
  enum Mode {
    POWERON,
    POWEROFF
  };

private:
  float init;
  bool launched;
  float screen[80][2];
  Mode mode;
  bool doing;

public:
  Power();
  ~Power();

  void render();
  void launch();
  void unlaunch();
  void set_mode(Mode m);
};

class JupetJaLoota : public Effect
{
private:
  Image *ralli;

public:
  JupetJaLoota();
  void render();
};

class Pixels2Cubes : public Effect
{
public:
  Pixels2Cubes();
  ~Pixels2Cubes();
  void render();
  void set_phase(const float p);

private:
  float phase;
  GLuint vertex_buf;
  GLuint index_buf;
  GLuint texcoord_buf;
  GLuint color_buf;
  GLfloat *vertices;
  GLuint *indices;
  GLfloat *texcoords;
  GLfloat *colors;
  unsigned int vertex_count;
  unsigned int index_count;
};

class MotionBlur : public Effect
{
public:
  MotionBlur();
  ~MotionBlur();
  void render();
  float chroma;
  float amount;

private:
  GLProgram *prog;
  GLFramebuffer *blurred_fbo;
  GLColorTexture2D *blurred_texture;
};

class GaussianBlur : public Effect
{
public:
  GaussianBlur();
  ~GaussianBlur();
  void render();
  float amount;
  float mosaic;

private:
  GLProgram *prog;
};

class Model;
class Scene;

class TV : public Effect
{
public:
  TV();
  ~TV();

  void render();
  void zoom_in();
  void zoom_out();

private:
  Scene *room;
  Model *tv;
  class TextureProxy : public Image
  {
  public:
    void bind() const;
  } proxy;
  float zoom;
  float started_zooming_at;
  bool zooming_in;
};

class PalNoise : public Effect
{
public:
  PalNoise();
  ~PalNoise();

  void render();
  void set_bottom(int x);
  void set_top(int x);
  void set_opacity(float x);

private:
  Image *noise;
  GLProgram *prog;
};

class TapeDistortion : public Effect
{
  // Tämä luokka osaa:
  // * Roiskia valkoisia vaakasuoria pieniä lumisateen palasia välillä
  //   satunnaisiin kohtiin
  // * Lisätä välillä tummennettuja kapeita nauhoja
  // * Taivuttaa välillä kapeaa vaakasuoraa kaistaletta vasemmalle tai
  //   oikealle
  // * Laittaa kuvan pois synkistä jolloin se siirtyilee hoopoihin
  //   kohtiin sekä pysty- että vaakasuunnassa. Kun kuva palaa taas
  //   synkkiin niin se ei palaa heti vaan jousiefektin saattelemana
  // * Repiä kuvasta enemmän tai vähemmän paksuja kaistaleita ja
  //   liimata niitä hassuihin kohtiin sekä animoida niiden liikkeet
  //   realistisesti
public:
  TapeDistortion();
  ~TapeDistortion();

  void render();
  void set_bend(float x) {bend = x; started_normalizing_at = 1.0e30f;}
  float get_bend(float x) const {return bend;}
  void set_offset(float x) {offset = x;}
  float get_offset(float x) const {return offset;}
  void normalize();
  bool is_normalizing() const {return started_normalizing_at != 1.0e30f;}

private:
  GLProgram *prog;
  float bend;
  float offset;
  float started_normalizing_at;
};

class ComponentSynchronizationProblem : public Effect
{
};

#endif
