#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>

#define CONSTANT 1.009f

SDL_Surface *d_screen;

int white, black;
unsigned char *sample;
int pos, lpos;
int pos2;
float beat_pos, speed;
int bend_up, bend_down;
int mods[17];
char bit_codes[] = {
  "qwertyuiasdfghjk"
};

void q_input(void)
{
  int i;
  SDL_Event event;
  while (SDL_PollEvent(&event)) {
    switch (event.type) {
      case SDL_KEYDOWN:
        if (event.key.keysym.sym == 'z') {
          bend_down = 1;
          bend_up = 0;
          speed = 1.0f;
        }
        if (event.key.keysym.sym == 'x') {
          bend_up = 1;
          bend_down = 0;
          speed = 1.0f;
        }
        for (i = 0; i < 16; i++)
          if (event.key.keysym.sym == bit_codes[i])
            mods[i + 1] = 1;
        if (event.key.keysym.sym == ' ')
          mods[17] = 1;
        break;
      case SDL_KEYUP:
        if (event.key.keysym.sym == 'z') {
          bend_down = 0;
          speed = 1.0f;
        }
        if (event.key.keysym.sym == 'x') {
          bend_up = 0;
          speed = 1.0f;
        }
        for (i = 0; i < 16; i++)
          if (event.key.keysym.sym == bit_codes[i])
            mods[i + 1] = 0;
        if (event.key.keysym.sym == ' ')
          mods[17] = 0;
        break;
    }
  }
}

#define SET_BIT(x,y) (x|(1<<y))
#define TEST_BIT(x,y) ((x>>y)&1)

int mod_pos(int in)
{
  int out, pool, i;
  pool = 0;
  out = in;
  for (i = 0; i < 16; i++)
    if (mods[i + 1] == 1)
      if (TEST_BIT(out, i) == 1)
        pool = 1;
  if (mods[17] == 1 && pool == 1)
    return 0;
  for (i = 0; i < 16; i++)
    if (mods[i + 1] == 1 && pool == 1)
      out = SET_BIT(out, i);
  return out % 0xffff;
}

void draw_vline(int x, signed char h, int color)
{
  int i, j;
  unsigned char *pix, *src;
  pix =
    (unsigned char *) ((int) d_screen->pixels + ((h + 128) * d_screen->pitch) + (d_screen->format->BytesPerPixel * x));
  src = (unsigned char *) &color;
  for (i = 0; i < abs(h); i++) {
    for (j = 0; j < d_screen->format->BytesPerPixel; j++)
      pix[j] = src[j];
    if (h < 0)
      pix += d_screen->pitch;
    else
      pix -= d_screen->pitch;
  }
}

void audio(void *userdata, Uint8 * stream, int len)
{
  int i, j;
  int qq;
  SDL_FillRect(d_screen, NULL, white);
  if (bend_up == 1)
    speed *= CONSTANT;
  if (bend_down == 1)
    speed /= CONSTANT;
  SDL_LockSurface(d_screen);
  for (i = 0; i < len; i++) {
    lpos = pos2;
    pos2 = mod_pos(pos);
    if (((pos2 - 1) % 0xffff) != lpos) {
      beat_pos = (float) pos2;
    }
    qq = (int) beat_pos;
    qq &= 0xffff;
    if (abs(qq - pos2) > 0x7ff)
      stream[i] = 0x7f;
    else
      stream[i] = sample[qq];
    beat_pos += speed;
    pos++;
    pos %= 0xffff;
    draw_vline(i, (signed char) stream[i], black);
  }
  SDL_UnlockSurface(d_screen);
  SDL_UpdateRect(d_screen, 0, 0, 0, 0);
}

SDL_AudioSpec audio_spec;

int main(int argc, char **argv)
{
  int i;
  FILE *fp;
  signed char j;
  SDL_AudioSpec spec;
  if (argc < 2)
    exit(-1);
  if (!(fp = fopen(argv[1], "rb"))) {
    perror(argv[1]);
    exit(-1);
  }
  sample = (unsigned char *) malloc(0xffff);
  fread(sample, 1, 0xffff, fp);
  fclose(fp);
  SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER);
  spec.freq = 22050;
  spec.format = AUDIO_S8;
  spec.channels = 1;
  spec.samples = 512;
  spec.callback = audio;
  if (SDL_OpenAudio(&spec, &audio_spec) < 0) {
    printf(SDL_GetError());
    exit(-1);
  }
  if ((d_screen = SDL_SetVideoMode(512, 256, 16, 0)) < 0) {
    printf(SDL_GetError());
    exit(-1);
  }
  white = SDL_MapRGB(d_screen->format, 255, 255, 255);
  black = SDL_MapRGB(d_screen->format, 0, 0, 0);
  pos = 0;
  lpos = 43;
  speed = 1.0f;
  bend_up = 0;
  bend_down = 0;
  bzero(mods, 256);
  SDL_PauseAudio(0);
  for (;;)
    q_input();
}