{$IFDEF Unix}
uses
  baseunix;
{$ENDIF}

const
{$IFDEF MSWINDOWS}
  libprojectM = 'libprojectM.dll';
{$ELSE}
  libprojectM = 'libprojectM.so';
{$ENDIF}

{**************** INTERNAL SECTION ****************}


type
  PPSingle = ^PSingle;

type
  _TContextType = Integer;
const
  AGL_CONTEXT   = 0;
  CGL_CONTEXT   = 1;
  NSGL_CONTEXT  = 2;
  GLX_CONTEXT   = 3;
  WGL_CONTEXT   = 4;

type
  _PRenderTarget = ^_TRenderTarget;
  _TRenderTarget = record
    { Texture size }
    texsize: Integer;

    { Application context }
    origContextType: _TContextType;

    usePbuffers: Integer;

    {$ifdef LINUX}
      lock_func: procedure(); cdecl;
      unlock_func: procedure(); cdecl;
    {$endif}

    { Opaque pbuffer context and pbuffer }
    {$ifdef DARWIN}
      origContext: Pointer;
      pbufferContext: Pointer;
      pbuffer: Pointer;
    {$endif}

  { Render target texture ID for non-pbuffer systems }
    textureID: array[0..2] of GLuint;
  end;

  _PProjectM = ^_TProjectM;
  _TProjectM = record
    presetURL: PChar;
    presetName: PChar;
    fontURL: PChar;

    hasInit: Integer;

    noSwitch: Integer;
    pcmframes: Integer;
    freqframes: Integer;
    totalframes: Integer;

    showfps: Integer;
    showtitle: Integer;
    showpreset: Integer;
    showhelp: Integer;
    showstats: Integer;

    studio: Integer;

    fbuffer: PGLubyte;

    {$IFNDEF MSWINDOWS}
    { The first ticks value of the application }
    startTime: timeval;
    {$ELSE}
    startTime: Longint;
    {$ENDIF}
    Time: Single;

    { Render target texture ID }
    renderTarget: _PRenderTarget;

    disp: array[0..79] of Char;

    wave_o: Single;

    //int texsize=1024;   //size of texture to do actual graphics
    fvw: Integer;   //fullscreen dimensions
    fvh: Integer;
    wvw: Integer;   //windowed dimensions
    wvh: Integer;
    vw: Integer;    //runtime dimensions
    vh: Integer;
    fullscreen: Integer;

    maxsamples: Integer;  //size of PCM buffer
    numsamples: Integer;  //size of new PCM info
    pcmdataL: PSingle;     //holder for most recent pcm data
    pcmdataR: PSingle;     //holder for most recent pcm data

    avgtime: Integer;     //# frames per preset
    
    title: PChar;
    drawtitle: Integer;

    correction: Integer;

    vol: Single;

    //per pixel equation variables
    gridx: PPSingle;     //grid containing interpolated mesh
    gridy: PPSingle;
    origtheta: PPSingle; //grid containing interpolated mesh reference values
    origrad: PPSingle;
    origx: PPSingle;     //original mesh
    origy: PPSingle;
    origx2: PPSingle;    //original mesh
    origy2: PPSingle;

    { Timing information }
    mspf: Integer;
    timed: Integer;
    timestart: Integer;
    nohard: Integer;
    count: Integer;
    realfps,
      fpsstart: Single;

    { PCM data }
    vdataL: array[0..511] of Single;  //holders for FFT data (spectrum)
    vdataR: array[0..511] of Single;

    { Various toggles }
    doPerPixelEffects: Integer;
    doIterative: Integer;

    { ENGINE VARIABLES }
    { From engine_vars.h }
    preset_name: array[0..255] of Char;

    { PER FRAME CONSTANTS BEGIN }
    zoom: Single;
    zoomexp: Single;
    rot: Single;
    warp: Single;

    sx: Single;
    sy: Single;
    dx: Single;
    dy: Single;
    cx: Single;
    cy: Single;

    gy: Integer;
    gx: Integer;

    decay: Single;

    wave_r: Single;
    wave_g: Single;
    wave_b: Single;
    wave_x: Single;
    wave_y: Single;
    wave_mystery: Single;

    ob_size: Single;
    ob_r: Single;
    ob_g: Single;
    ob_b: Single;
    ob_a: Single;

    ib_size: Single;
    ib_r: Single;
    ib_g: Single;
    ib_b: Single;
    ib_a: Single;

    meshx: Integer;
    meshy: Integer;

    mv_a: Single;
    mv_r: Single;
    mv_g: Single;
    mv_b: Single;
    mv_l: Single;
    mv_x: Single;
    mv_y: Single;
    mv_dy: Single;
    mv_dx: Single;

    treb: Single;
    mid: Single;
    bass: Single;
    bass_old: Single;
    beat_sensitivity: Single;
    treb_att: Single;
    mid_att: Single;
    bass_att: Single;
    progress: Single;
    frame: Integer;

    { PER_FRAME CONSTANTS END }

    { PER_PIXEL CONSTANTS BEGIN }

    x_per_pixel: Single;
    y_per_pixel: Single;
    rad_per_pixel: Single;
    ang_per_pixel: Single;

    { PER_PIXEL CONSTANT END }


    fRating: Single;
    fGammaAdj: Single;
    fVideoEchoZoom: Single;
    fVideoEchoAlpha: Single;

    nVideoEchoOrientation: Integer;
    nWaveMode: Integer;
    bAdditiveWaves: Integer;
    bWaveDots: Integer;
    bWaveThick: Integer;
    bModWaveAlphaByVolume: Integer;
    bMaximizeWaveColor: Integer;
    bTexWrap: Integer;
    bDarkenCenter: Integer;
    bRedBlueStereo: Integer;
    bBrighten: Integer;
    bDarken: Integer;
    bSolarize: Integer;
    bInvert: Integer;
    bMotionVectorsOn: Integer;
    fps: Integer;

    fWaveAlpha: Single;
    fWaveScale: Single;
    fWaveSmoothing: Single;
    fWaveParam: Single;
    fModWaveAlphaStart: Single;
    fModWaveAlphaEnd: Single;
    fWarpAnimSpeed: Single;
    fWarpScale: Single;
    fShader: Single;

    
    { Q VARIABLES START }

    q1: Single;
    q2: Single;
    q3: Single;
    q4: Single;
    q5: Single;
    q6: Single;
    q7: Single;
    q8: Single;


    { Q VARIABLES END }

    zoom_mesh: PPSingle;
    zoomexp_mesh: PPSingle;
    rot_mesh: PPSingle;

    sx_mesh: PPSingle;
    sy_mesh: PPSingle;
    dx_mesh: PPSingle;
    dy_mesh: PPSingle;
    cx_mesh: PPSingle;
    cy_mesh: PPSingle;

    x_mesh: PPSingle;
    y_mesh: PPSingle;
    rad_mesh: PPSingle;
    theta_mesh: PPSingle;
  end;

  PProjectMState = ^TProjectMState;
  TProjectMState = record
    fontURLStr: string;
    presetURLStr: string;
    titleStr: string;
    pm: _TProjectM;
  end;

{ projectM.h declarations }
procedure _projectM_init(pm: _PProjectM); cdecl; external libprojectM name 'projectM_init';
procedure _projectM_reset(pm: _PProjectM); cdecl; external libprojectM name 'projectM_reset';
procedure _projectM_resetGL(pm: _PProjectM; width: Integer; height: Integer); cdecl; external libprojectM name 'projectM_resetGL';
procedure _projectM_setTitle(pm: _PProjectM; title: PChar); cdecl; external libprojectM name 'projectM_setTitle';
procedure _renderFrame(pm: _PProjectM); cdecl; external libprojectM name 'renderFrame';

{ PCM.h declarations }
procedure _addPCMfloat(pcm_data: PSingle; samples: integer); cdecl; external libprojectM name 'addPCMfloat';
procedure _addPCM16(pcm_data: PPCM16); cdecl; external libprojectM name 'addPCM16';
procedure _addPCM16Data(pcm_data: PSmallint; samples: Smallint); cdecl; external libprojectM name 'addPCM16Data';
procedure _addPCM8_512(pcm_data: PPCM8_512); cdecl; external libprojectM name 'addPCM8';

{ console_interface.h declarations }
procedure _key_handler(pm: _PProjectM;
                       event:    TProjectMEvent;
                       keycode:  TProjectMKeycode;
                       modifier: TProjectMModifier); cdecl; external libprojectM name 'key_handler';




{**************** EXTERNAL SECTION ****************}


constructor TProjectM.Create(gx, gy: integer; fps: integer;
  texsize: integer; width, height: integer;
  const presetsDir, fontsDir: string;
  const titleFont, menuFont: string);
var
  state: PProjectMState;
begin
  inherited Create();

  New(state);
  data := state;

  with state^ do
  begin
    // copy strings (Note: do not use e.g. PChar(presetsDir) directly, it might
    // be a pointer to local stack data that is invalid after the calling function returns)
    fontURLStr   := fontsDir;
    presetURLStr := presetsDir;
    
    _projectM_reset(@pm);

    pm.fullscreen := 0;
    pm.renderTarget^.texsize := texsize;
    pm.gx := gx;
    pm.gy := gy;
    pm.fps := fps;
    pm.renderTarget^.usePbuffers := 0;
    pm.fontURL   := PChar(fontURLStr);
    pm.presetURL := PChar(presetURLStr);

  	_projectM_init(@pm);
  end;
end;

procedure TProjectM.ResetGL(width, height: Integer);
begin
  _projectM_resetGL(@PProjectMState(data).pm, width, height);
end;

procedure TProjectM.SetTitle(const title: string);
var
  state: PProjectMState;
begin
  state := PProjectMState(data);
  with state^ do
  begin
    titleStr := title;
    pm.title := PChar(titleStr);
    pm.showtitle := 1;
  end;
end;

procedure TProjectM.RenderFrame();
begin
  _renderFrame(@PProjectMState(data).pm);
end;

procedure TProjectM.AddPCMfloat(pcmData: PSingle; samples: integer);
begin
  _addPCMfloat(pcmData, samples);
end;

procedure TProjectM.AddPCM16(pcmData: PPCM16);
begin
  _addPCM16(pcmData);
end;

procedure TProjectM.AddPCM16Data(pcmData: PSmallint; samples: Smallint);
begin
  _addPCM16Data(pcmData, samples);
end;

procedure TProjectM.AddPCM8_512(pcmData: PPCM8_512);
begin
  _addPCM8_512(pcmData);
end;

procedure TProjectM.KeyHandler(event:    TProjectMEvent;
                               keycode:  TProjectMKeycode;
                               modifier: TProjectMModifier);
begin
  _key_handler(@PProjectMState(data).pm, event, keycode, modifier);
end;

procedure TProjectM.RandomPreset();
begin
  KeyHandler(PROJECTM_KEYDOWN, PROJECTM_K_r_LOWERCASE, PROJECTM_KMOD_LSHIFT);
end;

procedure TProjectM.PreviousPreset();
begin
  KeyHandler(PROJECTM_KEYDOWN, PROJECTM_K_p_LOWERCASE, PROJECTM_KMOD_LSHIFT);
end;

procedure TProjectM.NextPreset();
begin
  KeyHandler(PROJECTM_KEYDOWN, PROJECTM_K_n_LOWERCASE, PROJECTM_KMOD_LSHIFT);
end;

procedure TProjectM.ToggleShowPresetNames();
begin
  KeyHandler(PROJECTM_KEYDOWN, PROJECTM_K_F3, PROJECTM_KMOD_LSHIFT);
end;

destructor TProjectM.Destroy();
begin
  Dispose(PProjectMState(data));
  data := nil;
  inherited;
end;