// Copyright (c) 2022 Giorgos Vougioukas
//
// The license can be found in the LICENSE file.

#if defined(_WIN32) && defined(_DEBUG)
#include <vld.h>
#endif

//#define ENABLE_THA_WAVPACK_CONSOLE

#include "thalia/thalia_plugin_public.h"
#include "thalia_wavpack/wavpack_input.h"
#include "thalia_wavpack/wavpack_apic.h"
#include "thalia_wavpack/wavpack_tag.h"

#include "WDL/wdlcstring.h"

double (*THA_GetAudioDeviceSamplerate)();
void (*THA_GetDiskReadMode)(int *rmode, int *rbufsize, int *rnbufs);
void (*THA_GetResampleMode)(bool *interp, int *filtercnt,
  bool *sinc, int *sinc_size, int *sinc_interpsize);

static HINSTANCE wv_instance;
static HWND wv_main_hwnd;

int32_t tha_read_bytes(void *id, void *data, int32_t bcount)
{
  WDL_FileRead *fr = (WDL_FileRead *)id;
  return fr->Read(data, bcount);
}

int32_t tha_write_bytes(void *id, void *data, int32_t bcount)
{
  WDL_FileWrite *fw = (WDL_FileWrite *)id;
  return fw->Write(data, bcount);
}

int64_t tha_get_pos(void *id)
{
  WDL_FileRead *fr = (WDL_FileRead *)id;
  return (int64_t)fr->GetPosition();
}

int tha_set_pos_abs(void *id, int64_t pos)
{
  WDL_FileRead *fr = (WDL_FileRead *)id;
  return fr->SetPosition((WDL_INT64)pos);
}

int tha_set_pos_rel(void *id, int64_t delta, int mode)
{
  WDL_FileRead *fr = (WDL_FileRead *)id;

  switch (mode)
  {
  case SEEK_SET:
    {
      return fr->SetPosition((WDL_INT64)delta);
    } break;
  case SEEK_CUR:
    {
      return fr->SetPosition(fr->GetPosition() + (WDL_INT64)delta);
    } break;
  case SEEK_END:
    {
      return fr->SetPosition(fr->GetSize() + (WDL_INT64)delta);
    } break;
  default: break;
  }

  return 1;
}

int tha_push_back_byte(void *id, int c)
{
  return 0;
}

int64_t tha_get_length(void *id)
{
  WDL_FileRead *fr = (WDL_FileRead *)id;
  return (int64_t)fr->GetSize();
}

int tha_can_seek(void *id)
{
  return 1;
}

int tha_truncate_here(void *id)
{
  return 0;
}

int tha_close(void *id)
{
  return 0;
}

THA_IFileInput *CreateFromType(const char *type)
{
  if (!strcmp(type, "WV"))
  {
    return new THA_WavPackInput;
  }

  return NULL;
}

THA_IFileInput *CreateFromFile(const char *filename)
{
  if (!wdl_filename_cmp(WDL_get_fileext(filename), ".wv"))
  {
    THA_WavPackInput *p = new THA_WavPackInput;

    if (p->Open(filename))
    {
      return p;
    }

    delete p;
  }

  return NULL;
}

struct THA_FileInputRegister wv_input_reg =
{
  CreateFromType,
  CreateFromFile,
};

THA_IFileTag *CreateWavPackTag(const char *filename)
{
  if (!wdl_filename_cmp(WDL_get_fileext(filename), ".wv"))
  {
    THA_WavPackTag *p = new THA_WavPackTag;

    if (p && p->Open(filename))
    {
      return p;
    }
    else
    {
      delete p;
    }
  }

  return NULL;
}

THA_FileTagRegister wv_tag_reg =
{
  &CreateWavPackTag
};

THA_IFilePic *CreateWavPackAPIC(const char *filename)
{
  if (!wdl_filename_cmp(WDL_get_fileext(filename), ".wv"))
  {
    THA_WavPackAPIC *p = new THA_WavPackAPIC;

    if (p->Open(filename))
    {
      return p;
    }
    else
    {
      delete p;
    }
  }

  return NULL;
}

THA_FilePicRegister wv_apic_reg =
{
  &CreateWavPackAPIC
};

extern "C"
{
  THA_PLUGIN_EXPORT int THA_PLUGIN_ENTRYPOINT(
    THA_PLUGIN_HINSTANCE instance, THA_PluginInfo *rec)
  {
#if defined(_WIN32) && defined(_DEBUG) && defined(ENABLE_THA_WAVPACK_CONSOLE)
    // The first will use the console only if
    // the application is started by the command 
    // line. The second one will always enable
    // the console.
    //if (AttachConsole(ATTACH_PARENT_PROCESS))
    if (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())
    {
        freopen("CONOUT$", "w", stdout);
        freopen("CONOUT$", "w", stderr);
    }
#endif

    wv_instance = instance;

    if (rec)
    {
      if (rec->caller_version != THA_PLUGIN_VERSION || !rec->GetFunc)
      {
        return 0;
      }

      wv_main_hwnd = rec->hwnd_main;

      *((void **)&THA_GetAudioDeviceSamplerate) = rec->GetFunc("THA_GetAudioDeviceSamplerate");
      *((void **)&THA_GetDiskReadMode) = rec->GetFunc("THA_GetDiskReadMode");
      *((void **)&THA_GetResampleMode) = rec->GetFunc("THA_GetResampleMode");

      if (!rec->Register || !THA_GetAudioDeviceSamplerate || !THA_GetDiskReadMode ||
        !THA_GetResampleMode)
      {
        return 0;
      }

      rec->Register("input", &wv_input_reg);
      rec->Register("tag", &wv_tag_reg);
      //rec->Register("apic", &wv_apic_reg);

      return 1;
    }
    else
    {
      return 0;
    }
  }
}
