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

//#define ENABLE_EUTERPE_CONSOLE

#include <shlobj.h>

#include "euterpe/definitions.h"
#include "euterpe/main_wnd.h"
#include "euterpe/app_info.h"
#include "euterpe/plugin.h"

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

#include "WDL/win32_curses/curses.h"
#include "WDL/wingui/virtwnd.h"
#include "WDL/dirscan.h"
#include "WDL/lineparse.h"
#include "WDL/ptrlist.h"
#include "WDL/wdlstring.h"

//int WINAPI WinMain(_In_ HINSTANCE instance,
//  _In_opt_ HINSTANCE previous_instance,
//  _In_ LPSTR cmd_param, _In_ int show_cmd)
int WINAPI wWinMain(_In_ HINSTANCE instance,
  _In_opt_ HINSTANCE previous_instance,
  _In_ PWSTR cmd_wparam, _In_ int show_cmd)
{
  char cmd_param[32767];
  WDL_WideToMBStr(cmd_param, cmd_wparam, sizeof(cmd_param));

  g_inst = instance;

  CoInitialize(NULL);
  //CoInitializeEx(NULL, COINIT_MULTITHREADED); // COINIT_APARTMENTTHREADED, COINIT_MULTITHREADED

  InitCommonControls();
  //LoadLibrary("riched20.dll");

  g_scroll_message = RegisterWindowMessage("MSWHEEL_ROLLMSG");

  // 1: add
  // 2: addnew
  // 3: pause
  // 4: previous
  // 5: next
  // 6: play
  // 7: stop
  // 8: exit
  // 9: show
  // 10: hide
  WDL_TypedBuf<int> datatype;
  WDL_PtrList_DeleteOnDestroy<WDL_FastString> fadd, faddnew;
  {
    LineParser lp;
    lp.parse(cmd_param);

    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--sleep"))
      {
        Sleep(888);
      }
    }

    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--add"))
      {
        datatype.Add(1);
        while (++i < lp.getnumtokens() &&
          strncmp(lp.gettoken_str(i), "--", 2))
        {
          fadd.Add(new WDL_FastString(lp.gettoken_str(i)));
        }
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--addnew"))
      {
        datatype.Add(2);
        while (++i < lp.getnumtokens() &&
          strncmp(lp.gettoken_str(i), "--", 2))
        {
          faddnew.Add(new WDL_FastString(lp.gettoken_str(i)));
        }
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--pause"))
      {
        datatype.Add(3);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--previous"))
      {
        datatype.Add(4);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--next"))
      {
        datatype.Add(5);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--play"))
      {
        datatype.Add(6);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--stop"))
      {
        datatype.Add(7);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--exit"))
      {
        datatype.Add(8);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--show"))
      {
        datatype.Add(9);
      }
    }
    for (int i = 0; i < lp.getnumtokens(); i++)
    {
      if (!strcmp(lp.gettoken_str(i), "--hide"))
      {
        datatype.Add(10);
      }
    }
  }

  HWND other_alive = FindWindowEx(NULL, NULL, "euterpe_superhost", NULL);

  if (other_alive)
  {
    SetForegroundWindow(other_alive);

    COPYDATASTRUCT cds;
    for (int i = 0; i < datatype.GetSize(); i++)
    {
      if (datatype.Get()[i] >= 3 && datatype.Get()[i] <= 10)
      {
        cds.dwData = datatype.Get()[i];
        cds.cbData = 0;
        cds.lpData = 0;
        SendMessage(other_alive, WM_COPYDATA, (WPARAM)other_alive, (LPARAM)(LPVOID)&cds);
      }
      else if (datatype.Get()[i] == 2)
      {
        for (int j = 0; j < faddnew.GetSize(); j++)
        {
          if (datatype.Get()[i] == 2)
          {
            cds.dwData = datatype.Get()[i];
            cds.cbData = 0;
            cds.lpData = 0;
            SendMessage(other_alive, WM_COPYDATA, (WPARAM)other_alive, (LPARAM)(LPVOID)&cds);
            datatype.Get()[i] = 1;
          }
          cds.dwData = datatype.Get()[i];
          cds.cbData = faddnew.Get(j)->GetLength() + 1;
          cds.lpData = (char *)faddnew.Get(j)->Get();
          SendMessage(other_alive, WM_COPYDATA, (WPARAM)other_alive, (LPARAM)(LPVOID)&cds);
        }
        faddnew.Empty(true);
      }
      else if (datatype.Get()[i] == 1)
      {
        for (int j = 0; j < fadd.GetSize(); j++)
        {
          cds.dwData = datatype.Get()[i];
          cds.cbData = fadd.Get(j)->GetLength() + 1;
          cds.lpData = (char *)fadd.Get(j)->Get();
          SendMessage(other_alive, WM_COPYDATA, (WPARAM)other_alive, (LPARAM)(LPVOID)&cds);
        }
        fadd.Empty(true);
      }
    }
    datatype.Resize(0);

    return 0;
  }

#if defined(_WIN32) && defined(_DEBUG) && defined(ENABLE_EUTERPE_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

  HICON icon = LoadIcon(g_inst, MAKEINTRESOURCE(IDI_ICON1));

  {
    WNDCLASS wc = { 0 };
    GetClassInfo(NULL, "#32770", &wc);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = icon;
    wc.hInstance = g_inst;
    RegisterClass(&wc);

    //wc.style = CS_HREDRAW | CS_VREDRAW;
    //wc.style = CS_GLOBALCLASS;
    wc.style = CS_DBLCLKS;
    wc.lpszClassName = "euterpe_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "transport_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "preferences_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "device_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "playlist_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "about_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "license_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "changelog_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "artwork_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "preview_artwork_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "file_tasks_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "find_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "exact_volume_superhost";
    RegisterClass(&wc);

    wc.lpszClassName = "properties_superhost";
    RegisterClass(&wc);

    curses_registerChildClass(g_inst);

    //wc.lpfnWndProc = (WNDPROC)custom_playlist_control_proc;
    //wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    //wc.lpszClassName = "playlist_control_superhost";
    //RegisterClass(&wc);
  }

  char module_filename[2048];
  GetModuleFileName(g_inst, module_filename, sizeof(module_filename));

  g_module_path.Set(module_filename);
  g_module_path.remove_filepart(true);

  bool portable_settings = false;

  // In order to have portable settings
  // create an empty directory with the
  // name |portable| in the root directory
  // of Euterpe.

  if (!portable_settings)
  {
    WDL_DirScan dir;
    WDL_FastString fn;

    if (!dir.First(g_module_path.Get()))
    {
      do
      {
        if (dir.GetCurrentIsDirectory())
        {
          dir.GetCurrentFullFN(&fn);

          if (strcmp(fn.get_filepart(), "portable") == 0)
          {
            portable_settings = true;
            break;
          }
        }
      }
      while (!dir.Next());
    }
  }

  if (portable_settings)
  {
    g_settings_path.Set(g_module_path.Get());
    g_settings_path.Append("portable");
    g_settings_path.Append(WDL_DIRCHAR_STR);
  }
  else
  {
    char user_path[2048];

    // CSIDL_APPDATA to get the equivalent of %APPDATA%
    // CSIDL_PROFILE to get the equivalent of %USERPROFILE%
    if (SHGetSpecialFolderPathUTF8(NULL, user_path, sizeof(user_path), CSIDL_APPDATA, 0))
    {
      g_settings_path.Set(user_path);
      g_settings_path.Append("\\" EUTERPE_NAME_MARKETING "\\");
      CreateDirectory(g_settings_path.Get(), NULL);
    }
    else
    {
      MessageBox(NULL, "Euterpe cannot retrieve user's roaming directory. Press OK to exit Euterpe.", "Euterpe error", MB_OK);
      exit(1);
    }
  }

  g_ini_path.Set(g_settings_path.Get());
  g_ini_path.Append(EUTERPE_NAME);
  g_ini_path.Append(".ini");

  // Start sub-systems
  g_ini_file = new RSE_IniFile(g_ini_path.Get());

  LoadPlugins();

  g_main_wnd = new RSE_MainWnd;

  CreateDialogParam(g_inst, MAKEINTRESOURCE(IDD_MAIN), GetDesktopWindow(),
    RSE_MainWnd::ST_MainWndProc, (LPARAM)g_main_wnd);

  {
    SetForegroundWindow(g_main_wnd->Handle());

    COPYDATASTRUCT cds;
    for (int i = 0; i < datatype.GetSize(); i++)
    {
      if (datatype.Get()[i] >= 3 && datatype.Get()[i] <= 10)
      {
        cds.dwData = datatype.Get()[i];
        cds.cbData = 0;
        cds.lpData = 0;
        SendMessage(g_main_wnd->Handle(), WM_COPYDATA,
          (WPARAM)g_main_wnd->Handle(), (LPARAM)(LPVOID)&cds);
      }
      else if (datatype.Get()[i] == 2)
      {
        for (int j = 0; j < faddnew.GetSize(); j++)
        {
          if (datatype.Get()[i] == 2)
          {
            cds.dwData = datatype.Get()[i];
            cds.cbData = 0;
            cds.lpData = 0;
            SendMessage(g_main_wnd->Handle(), WM_COPYDATA,
              (WPARAM)g_main_wnd->Handle(), (LPARAM)(LPVOID)&cds);
            datatype.Get()[i] = 1;
          }
          cds.dwData = datatype.Get()[i];
          cds.cbData = faddnew.Get(j)->GetLength() + 1;
          cds.lpData = (char *)faddnew.Get(j)->Get();
          SendMessage(g_main_wnd->Handle(), WM_COPYDATA,
            (WPARAM)g_main_wnd->Handle(), (LPARAM)(LPVOID)&cds);
        }
        faddnew.Empty(true);
      }
      else if (datatype.Get()[i] == 1)
      {
        for (int j = 0; j < fadd.GetSize(); j++)
        {
          cds.dwData = datatype.Get()[i];
          cds.cbData = fadd.Get(j)->GetLength() + 1;
          cds.lpData = (char *)fadd.Get(j)->Get();
          SendMessage(g_main_wnd->Handle(), WM_COPYDATA,
            (WPARAM)g_main_wnd->Handle(), (LPARAM)(LPVOID)&cds);
        }
        fadd.Empty(true);
      }
    }
    datatype.Resize(0);
  }

  const int exitcode = g_main_wnd->RunMessageLoop();

  delete g_main_wnd;

  delete g_ini_file;

  ReleasePlugins();

  CoUninitialize();

  return exitcode;
}
