#ifndef __PATTERN2_H__
#define __PATTERN2_H__



#include <windows.h>
#include <math.h>
#include "Pattern.h"



//---- PATTERN STRUCTURES AND DEFINES ----------------------------------------//

#define _PATTERN_CLASS_STD                                                                      \
  void *input[16];                                                                              \
  void *output[16];                                                                             \
  void init() { for(int i=0; i<16; i++) input[i] = output[i] = NULL; }                          \
  void setinput(int i, void* p) { input[i] = p; }                                               \
  void setoutput(int i, void* p) { output[i] = p; }                                             \
  void addinput(void* p) { for(int i=0; i<16; i++) if(!input[i]) { input[i] = p; return; } }    \
  void addoutput(void* p) { for(int i=0; i<16; i++) if(!output[i]) { output[i] = p; return; } }

class Pattern_Fn { public: _PATTERN_CLASS_STD; Pattern_Fn() { init(); } void process() { } };

#define pattern_type(type, data) class type { public: data; };

#define pattern_def(name) class name : Pattern_Fn { public: _PATTERN_CLASS_STD; name() { init(); } void process(); }; void name::process()
#define pattern_class(name) class name : Pattern_Fn { public: _PATTERN_CLASS_STD;

#define pattern_fn(type, name) static type *name = NULL; if(!name) { name = new type(); }

#define pattern_var(type, name) static type *name = NULL; if(!name) { name = new type(); }

#define pattern_const(type, name, initfn) static type *name = NULL; if(!name) { name = new type(); \
initfn *__Pattern_Temp_Fn = new initfn(); }

 //__Pattern_Temp_Fn->addoutput(name); __Pattern_Temp_Fn->process();

//pattern_addoutput(__Pattern_Temp_Fn, name); pattern_process(__Pattern_Temp_Fn); }

#define pattern_input(type, name, index) type *name = (type *)input[index]
#define pattern_output(type, name, index) type *name = (type *)output[index]

#define pattern_setinput(var, index, value) var->setinput(index, value)
#define pattern_setoutput(var, index, value) var->setoutput(index, value)

#define pattern_addinput(var, value) var->addinput(value)
#define pattern_addoutput(var, value) var->addoutput(value)

#define pattern_process(name) name->process()



//---- DATA TYPES ------------------------------------------------------------//

pattern_type(PATTERN_BOOL, bool value);

pattern_type(PATTERN_REAL, double value);

#define PATTERN_RGB_SETPIXEL(name, x, y, r, g, b) { name->buf[y][x][0] = b; name->buf[y][x][1] = g; name->buf[y][x][2] = r; }

pattern_type(PATTERN_RGBA, unsigned char buf[256][256][4]);

pattern_type(PATTERN_RGBAZ, unsigned char buf[256][256][4]; unsigned int zbuf[256][256]);



//---- TYPES -----------------------------------------------------------------//

#include "type_simple3d.h"



//---- FUNCTIONS -------------------------------------------------------------//

#include "functions_real.h"
#include "functions_alpha.h"
#include "functions_rgb.h"
#include "functions_drums.h"
#include "functions_vis.h"

#include "functions_simple3d.h"



//---- FUNCTIONS -------------------------------------------------------------//



//---- Pattern_Fn_ValueToggle (REAL time) -> (REAL out)

pattern_def( Pattern_Fn_ValueToggle )
{
  pattern_input(PATTERN_REAL, time, 0);
  pattern_output(PATTERN_REAL, out, 0);

  out->value = 1 + (((int)(time->value)/1000)&1);
}



//---- Pattern_Fn_Timer (REAL time, BOOL pulse) -> (REAL out)

pattern_class( Pattern_Fn_Timer )

  PATTERN_REAL starttime;
  PATTERN_BOOL trigger;

  Pattern_Fn_Timer()
  {
    init();

    starttime.value = 0;
    trigger.value = false;
  }

  void process()
  {
    pattern_input(PATTERN_REAL, time,  0);
    pattern_input(PATTERN_BOOL, pulse, 1);

    pattern_output(PATTERN_REAL, out, 0);
    
    if(starttime.value == 0) starttime.value = time->value;

    if(trigger.value == false && pulse->value == true)
    {
      starttime.value = time->value;
    }

    trigger.value = pulse->value;

    out->value = time->value - starttime.value;
  }
};



//---- Pattern_Fn_Memory (REAL time, BOOL pulse) -> (REAL out)

pattern_class( Pattern_Fn_Memory )

  PATTERN_REAL lasttime;
  PATTERN_BOOL trigger;

  Pattern_Fn_Memory()
  {
    init();

    lasttime.value = 0;
    trigger.value = false;
  }

  void process()
  {
    pattern_input(PATTERN_REAL, time,  0);
    pattern_input(PATTERN_BOOL, pulse, 1);

    pattern_output(PATTERN_REAL, out, 0);
    
    if(lasttime.value == 0) lasttime.value = time->value;

    //if(trigger.value == false && pulse->value == true)
    {
      lasttime.value = time->value;
    }

    trigger.value = pulse->value;

    out->value = lasttime.value;
  }
};



//---- Pattern_Fn_Speedo (REAL time, REAL speed) -> (REAL out)

pattern_class( Pattern_Fn_Speedo )

  PATTERN_REAL oldtime;
  PATTERN_REAL speedotime;

  Pattern_Fn_Speedo()
  {
    init();

    speedotime.value = 0;
    oldtime.value = 0;
  }

  void process()
  {
    PATTERN_REAL * time  = (PATTERN_REAL*) input[0];
    PATTERN_REAL * speed = (PATTERN_REAL*) input[1];

    PATTERN_REAL * out  = (PATTERN_REAL*) output[0];
    
    if(oldtime.value == 0) oldtime.value = time->value;

    speedotime.value += (time->value - oldtime.value)*speed->value;
    oldtime.value = time->value;

    out->value = speedotime.value;
  }
};



//---- Pattern_Fn_Hit1 (REAL time) -> (REAL value)

pattern_def( Pattern_Fn_Hit1 )
{
  pattern_input(PATTERN_REAL, time, 0);
  pattern_output(PATTERN_REAL, value, 0);

  value->value = 0.3 - 0.8 * time->value;
  if(value->value < 0) value->value = 0;
}



//---- Pattern_Fn_HitTest1 (REAL time) -> (RGB result)

pattern_def( Pattern_Fn_HitTest1 )
{
  //  Inputs and outputs

  pattern_input(PATTERN_REAL, time, 0);
  pattern_output(PATTERN_RGB, result, 0);

  //  Constants

//  pattern_const(PATTERN_A, horiz, Pattern_Fn_XGradient);
//  pattern_const(PATTERN_A, vert , Pattern_Fn_YGradient);

//  pattern_const(PATTERN_RGB, image, Pattern_Fn_Checkers);

  pattern_var(PATTERN_A, horiz);
  pattern_fn(Pattern_Fn_XGradient, xx);
  pattern_addoutput(xx, horiz);
  pattern_process(xx);

  pattern_var(PATTERN_A, vert);
  pattern_fn(Pattern_Fn_YGradient, yy);
  pattern_addoutput(yy, vert);
  pattern_process(yy);

  pattern_var(PATTERN_RGB, image);
  pattern_fn(Pattern_Fn_Checkers, zz);
  pattern_addoutput(zz, image);
  pattern_process(zz);

  //  Internal variables
  
  pattern_var(PATTERN_BOOL, drum1);

  pattern_var(PATTERN_REAL, timer);
  pattern_var(PATTERN_REAL, memory);

  pattern_var(PATTERN_REAL, hit1);

  pattern_var(PATTERN_A, sinwave1);
  pattern_var(PATTERN_A, sinwave2);

  pattern_var(PATTERN_A, fade1);
  pattern_var(PATTERN_A, fade2);

  pattern_var(PATTERN_REAL, radius);

  //  Internal functions

  pattern_fn(Pattern_Fn_Drum1, Drum1);
  pattern_fn(Pattern_Fn_Timer, Timer);
  pattern_fn(Pattern_Fn_Memory, Memory);

  pattern_fn(Pattern_Fn_Hit1, Hit1);

  pattern_fn(Pattern_Fn_SinWave1, SinWave1);
  pattern_fn(Pattern_Fn_SinWave2, SinWave2);

  pattern_fn(Pattern_Fn_AlphaFade, Fade1);
  pattern_fn(Pattern_Fn_AlphaFade, Fade2);

  pattern_fn(Pattern_Fn_Lookup, Lookup);

  pattern_fn(Pattern_Fn_BoxBlur, BoxBlur);

  //  Make tree

  pattern_addoutput(Drum1, drum1);

  pattern_addinput(Timer, time);
  pattern_addinput(Timer, drum1);
  pattern_addoutput(Timer, timer);

  pattern_addinput(Memory, time);
  pattern_addinput(Memory, drum1);
  pattern_addoutput(Memory, memory);

  pattern_addinput(Hit1, timer);
  pattern_addoutput(Hit1, hit1);

  pattern_addinput(SinWave1, memory);
  pattern_addoutput(SinWave1, sinwave1);

  pattern_addinput(SinWave2, memory);
  pattern_addoutput(SinWave2, sinwave2);

  pattern_addinput(Fade1, horiz);
  pattern_addinput(Fade1, sinwave1);
  pattern_addinput(Fade1, hit1);
  pattern_addoutput(Fade1, fade1);

  pattern_addinput(Fade2, vert);
  pattern_addinput(Fade2, sinwave2);
  pattern_addinput(Fade2, hit1);
  pattern_addoutput(Fade2, fade2);

  pattern_addinput(Lookup, image);
  pattern_addinput(Lookup, fade1);
  pattern_addinput(Lookup, fade2);
  pattern_addoutput(Lookup, result);

  pattern_addinput(BoxBlur, result);
  pattern_addinput(BoxBlur, radius);
  pattern_addoutput(BoxBlur, result);

  //  Process

  pattern_process(Drum1);

  pattern_process(Timer);
  pattern_process(Memory);

  pattern_process(Hit1);

  pattern_process(SinWave1);
  pattern_process(SinWave2);

  pattern_process(Fade1);
  pattern_process(Fade2);

  pattern_process(Lookup);

  //radius->value = hit1->value * 100;

  //pattern_process(BoxBlur);
}



//---- Pattern_Fn_Main (REAL time) -> (RGB result)

pattern_def( Pattern_Fn_Main )
{
  //  Inputs and outputs

  pattern_input(PATTERN_REAL, time, 0);
  pattern_output(PATTERN_RGB, result, 0);

  //  Internal functions

  pattern_fn(Pattern_Fn_SwirlyThing1, swirly);

  //  Make tree

  pattern_addinput(swirly, time);
  pattern_addoutput(swirly, result);

  //  Process

  pattern_process(swirly);
}




//---- END OF FILE -----------------------------------------------------------//

#endif
