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

#include "thalia/metadata_wnd.h"
#include "thalia/preferences.h"
#include "thalia/app_info.h"
#include "thalia/metadata_queue.h"
#include "thalia/main_wnd.h"
#include "thalia/plugin.h"

#define THA_METADATA_KEY 300200
#define THA_METADATA_VAL 300201

#include "WDL/assocarray.h"
#include "WDL/fileread.h"

static void delete_str(WDL_FastString *p) { delete p; }

THA_MetadataWnd::THA_MetadataWnd()
  : m_hwnd(NULL)
  , m_list(NULL)
  , m_edit(NULL)
{}

THA_MetadataWnd::~THA_MetadataWnd()
{
  DestroyWindow(m_hwnd);
}

void THA_MetadataWnd::Edit()
{
  int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED | LVIS_FOCUSED);
  if (pos >= 0)
  {
    RECT r;
    char key[1024];
    char text[2048];
    ListView_GetItemRect(m_list, pos, &r, LVIR_SELECTBOUNDS); //LVIR_LABEL
    r.left += ListView_GetColumnWidth(m_list, 0);
    ListView_GetItemText(m_list, pos, 0, key, sizeof(key));
    ListView_GetItemText(m_list, pos, 1, text, sizeof(text));
    ListView_SetItemState(m_list, pos, ~LVIS_SELECTED, LVIS_SELECTED)
    if (!strcmp(key, "Track") || !strcmp(key, "Year"))
    {
      m_edit = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
        "Edit", "", WS_CHILDWINDOW | WS_TABSTOP |
        ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN | ES_NUMBER,
        r.left, r.top, r.right - r.left, r.bottom - r.top, m_list, NULL, g_inst, 0);
    }
    else
    {
      m_edit = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
        "Edit", "", WS_CHILDWINDOW | WS_TABSTOP |
        ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN,
        r.left, r.top, r.right - r.left, r.bottom - r.top, m_list, NULL, g_inst, 0);
    }
    SendMessage(m_edit, WM_SETFONT, SendMessage(m_hwnd, WM_GETFONT, 0, 0), 0);
    SetWindowText(m_edit, text);
    SendMessage(m_edit, EM_SETSEL, 0, -1);
    ShowWindow(m_edit, SW_SHOW);
    SetFocus(m_edit);
  }
}

HWND THA_MetadataWnd::GetEdit() const
{
  return m_edit;
}

void THA_MetadataWnd::EditConfirm()
{
  char key[1024];
  char name[4096];
  char newname[4096];
  int len = GetWindowText(m_edit, newname, sizeof(newname));
  if (len >= 0)
  {
    int pos = ListView_GetNextItem(m_list, -1, LVIS_FOCUSED);
    if (pos >= 0)
    {
      ListView_GetItemText(m_list, pos, 0, key, sizeof(key));
      ListView_GetItemText(m_list, pos, 1, name, sizeof(name));
      ListView_SetItemText(m_list, pos, 1, newname);
      if (g_metadataqueue->GetFilePathCount() == 1)
      {
        g_metadataqueue->UpdateMetadata(key, newname);
      }
      SetFocus(m_list);
      DestroyWindow(m_edit);
      m_edit = NULL;
      ListView_SetItemState(m_list, pos, LVIS_SELECTED, LVIS_SELECTED);
      ListView_SetItemState(m_list, pos, LVIS_FOCUSED, LVIS_FOCUSED);
      InvalidateRect(m_hwnd, NULL, FALSE);
    }
  }
}

void THA_MetadataWnd::EditAbort()
{
  SetFocus(m_list);
  DestroyWindow(m_edit);
  InvalidateRect(m_hwnd, NULL, FALSE);
  m_edit = NULL;

  int pos = ListView_GetNextItem(m_list, -1, LVIS_FOCUSED);
  if (pos >= 0)
  {
    ListView_SetItemState(m_list, pos, LVIS_SELECTED, LVIS_SELECTED);
    ListView_SetItemState(m_list, pos, LVIS_FOCUSED, LVIS_FOCUSED);
    InvalidateRect(m_list, NULL, FALSE);
  }
}

void THA_MetadataWnd::OnInitDialog(WPARAM wparam, LPARAM lparam)
{
  m_resize.init(m_hwnd);
  m_resize.init_item(IDC_LIST1, 0.0f, 0.0f, 1.0f, 1.0f);
  m_resize.init_item(IDC_BUTTON1, 1.0f, 1.0f, 1.0f, 1.0f);
  m_resize.init_item(IDC_BUTTON2, 1.0f, 1.0f, 1.0f, 1.0f);

  int x = GetPrivateProfileInt(THA_NAME, "metadata_wnd_x", 0, g_inipath.Get());
  int y = GetPrivateProfileInt(THA_NAME, "metadata_wnd_y", 50, g_inipath.Get());
  int w = GetPrivateProfileInt(THA_NAME, "metadata_wnd_w", 320, g_inipath.Get());
  int h = GetPrivateProfileInt(THA_NAME, "metadata_wnd_h", 300, g_inipath.Get());
  SetWindowPos(m_hwnd, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);

  m_list = GetDlgItem(m_hwnd, IDC_LIST1);
  WDL_UTF8_HookListView(m_list);

  if (g_metadataqueue->GetFilePathCount() > 1)
  {
    ListView_SetExtendedListViewStyleEx(m_list,
      LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_CHECKBOXES,
      LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_CHECKBOXES);
  }
  else
  {
    ListView_SetExtendedListViewStyleEx(m_list,
      LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER,
      LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
  }

  //m_playlist_menu = (HMENU)GetSubMenu(LoadMenu(g_inst, MAKEINTRESOURCE(IDR_LIBRARY_CONTEXT)), 4);

  ColumnHeader default_column_header[] =
  {
    { THA_METADATA_KEY, "Name", 140, LVCFMT_LEFT, 1, 0 },
    { THA_METADATA_VAL, "Value", 150, LVCFMT_LEFT, 1, 0 }
  };

  m_column_header.Resize(sizeof(default_column_header) /
    sizeof(default_column_header[0]));

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    ColumnHeader *ch = m_column_header.Get();
    ch[i] = default_column_header[i];
  }

  LVCOLUMN lvc = { 0, };
  lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;

  w = 0;
  WDL_FastString s;

  ColumnHeader *ch = m_column_header.Get();

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    s.SetFormatted(128, "metadata_column_width_%02d", i);
    w = GetPrivateProfileInt(THA_NAME, s.Get(), ch[i].width, g_inipath.Get());
    lvc.cx = w;
    lvc.fmt = ch[i].fmt;
    lvc.pszText = (char *)ch[i].text;
    ListView_InsertColumn(m_list, i, &lvc);
  }

  WDL_TypedBuf<int> order_array;
  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "metadata_column_order_%02d", i);
    oa[i] = GetPrivateProfileInt(THA_NAME, s.Get(), i, g_inipath.Get());
  }

  ListView_SetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  ListView_DeleteAllItems(m_list);

  WDL_StringKeyedArray<WDL_FastString *> *metadata;
  g_metadataqueue->GetMetadata(&metadata);

  WDL_PtrList_DeleteOnDestroy<WDL_FastString> keynames;
  keynames.Add(new WDL_FastString("Title"));
  keynames.Add(new WDL_FastString("Artist"));
  keynames.Add(new WDL_FastString("Album"));
  keynames.Add(new WDL_FastString("Genre"));
  keynames.Add(new WDL_FastString("Track"));
  keynames.Add(new WDL_FastString("Year"));
  keynames.Add(new WDL_FastString("Comment"));

  for (int i = 0; i < keynames.GetSize(); i++)
  {
    WDL_FastString *key = keynames.Get(i);
    WDL_FastString *val = metadata->Get(key->Get(), NULL);

    if (val)
    {
      m_strbuf.Set(val->Get());
    }
    else
    {
      m_strbuf.Set("");
    }

    LVITEM lvi = { 0, };
    int si = 0;
    lvi.mask = LVIF_TEXT | LVIF_PARAM;
    lvi.lParam = (LPARAM)i;
    lvi.iItem = i;
    lvi.iSubItem = si++;
    lvi.pszText = (char *)key->Get();
    lvi.cchTextMax = key->GetLength();
    ListView_InsertItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++;
    lvi.pszText = (char *)m_strbuf.Get();
    lvi.cchTextMax = m_strbuf.GetLength();
    ListView_SetItem(m_list, &lvi);
  }

  int bk = g_preferences->GetBackgroundColor();
  int fg = g_preferences->GetTextColor();
  ListView_SetBkColor(m_list, RGB((bk >> 16) & 0xFF,(bk >> 8) & 0xFF, bk & 0xFF));
  ListView_SetTextBkColor(m_list, RGB((bk >> 16) & 0xFF,(bk >> 8) & 0xFF, bk & 0xFF));
  ListView_SetTextColor(m_list, RGB((fg >> 16) & 0xFF,(fg >> 8) & 0xFF, fg & 0xFF));
}

void THA_MetadataWnd::OnTimer(WPARAM wparam, LPARAM lparam)
{}

void THA_MetadataWnd::OnDestroy(WPARAM wparam, LPARAM lparam)
{
  RECT r;
  GetWindowRect(m_hwnd, &r);

  int x = r.left;
  int y = r.top;
  int w = r.right - r.left;
  int h = r.bottom - r.top;

  WDL_FastString xstr, ystr, wstr, hstr;
  xstr.SetFormatted(32, "%d", x);
  ystr.SetFormatted(32, "%d", y);
  wstr.SetFormatted(32, "%d", w);
  hstr.SetFormatted(32, "%d", h);

  WritePrivateProfileString(THA_NAME, "metadata_wnd_x", xstr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "metadata_wnd_y", ystr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "metadata_wnd_w", wstr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "metadata_wnd_h", hstr.Get(), g_inipath.Get());

  WDL_FastString s;
  WDL_TypedBuf<int> order_array;
  WDL_FastString str;

  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  ListView_GetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "metadata_column_order_%02d", i);
    str.SetFormatted(32, "%d", oa[i]);
    WritePrivateProfileString(THA_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  for (int i = 0; i < Header_GetItemCount(ListView_GetHeader(m_list)); i++)
  {
    s.SetFormatted(128, "metadata_column_width_%02d", i);
    str.SetFormatted(32, "%d", ListView_GetColumnWidth(m_list, i));
    WritePrivateProfileString(THA_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  m_hwnd = NULL;
}

void THA_MetadataWnd::OnSize(WPARAM wparam, LPARAM lparam)
{
  if (wparam != SIZE_MINIMIZED)
  {
    int w = LOWORD(lparam);
    ColumnHeader *ch = m_column_header.Get();
    if (ch)
    {
      int c0w = ListView_GetColumnWidth(m_list, 0);
      int c1w = w - 20 - c0w;
      ListView_SetColumnWidth(m_list, 1, c1w);
    }
    m_resize.onResize();
  }
}

void THA_MetadataWnd::OnCommand(WPARAM wparam, LPARAM lparam)
{
  switch(LOWORD(wparam))
  {
  case IDOK:
  case IDC_BUTTON1:
    {
      if (g_metadataqueue->GetFilePathCount() > 1)
      {
        char key[1024];
        char val[2048];
        for (int i = 0; i < ListView_GetItemCount(m_list); i++)
        {
          ListView_GetItemText(m_list, i, 0, key, sizeof(key));
          ListView_GetItemText(m_list, i, 1, val, sizeof(val));
          if (ListView_GetCheckState(m_list, i))
          {
            g_metadataqueue->UpdateMetadata(key, val);
          }
        }
      }
      g_metadataqueue->Save();
      if (g_metadatawnd) { delete g_metadatawnd; g_metadatawnd = NULL; }
    } break;
  case IDCANCEL:
  case IDC_BUTTON2:
    {
      g_metadataqueue->Abort();
      if (g_metadatawnd) { delete g_metadatawnd; g_metadatawnd = NULL; }
    } break;
  }
}

void THA_MetadataWnd::OnSysCommand(WPARAM wparam, LPARAM lparam)
{}

WDL_DLGRET THA_MetadataWnd::MetadataWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  THA_MetadataWnd *self = (THA_MetadataWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

  if (!self && msg == WM_INITDIALOG)
  {
    SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
    self = (THA_MetadataWnd *)lparam;
    self->m_hwnd = hwnd;
  }

  if (WDL_likely(self))
  {
    return self->MetadataWndLoop(msg, wparam, lparam);
  }
  else
  {
    return 0;
  }
}

WDL_DLGRET THA_MetadataWnd::MetadataWndLoop(UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case WM_INITDIALOG: OnInitDialog(wparam, lparam); break;
    case WM_TIMER: OnTimer(wparam, lparam); break;
    case WM_DESTROY: OnDestroy(wparam, lparam); break;
    case WM_SIZE: OnSize(wparam, lparam); break;
    case WM_COMMAND: OnCommand(wparam, lparam); break;
    case WM_SYSCOMMAND: OnSysCommand(wparam, lparam); break;
  }

  return 0;
}



THA_FileInfoWnd::THA_FileInfoWnd()
  : m_hwnd(NULL)
  , m_list(NULL)
  , m_edit(NULL)
{}

THA_FileInfoWnd::~THA_FileInfoWnd()
{
  DestroyWindow(m_hwnd);
}

void THA_FileInfoWnd::Edit()
{
  int pos = ListView_GetNextItem(m_list, -1, LVIS_SELECTED | LVIS_FOCUSED);
  if (pos >= 0)
  {
    RECT r;
    char key[1024];
    char text[2048];
    ListView_GetItemRect(m_list, pos, &r, LVIR_SELECTBOUNDS); //LVIR_LABEL
    r.left += ListView_GetColumnWidth(m_list, 0);
    ListView_GetItemText(m_list, pos, 0, key, sizeof(key));
    ListView_GetItemText(m_list, pos, 1, text, sizeof(text));
    ListView_SetItemState(m_list, pos, ~LVIS_SELECTED, LVIS_SELECTED)
    m_edit = CreateWindowEx(WS_EX_NOPARENTNOTIFY,
      "Edit", "", WS_CHILDWINDOW | WS_TABSTOP |
      ES_LEFT | ES_AUTOHSCROLL | ES_WANTRETURN | ES_READONLY,
      r.left, r.top, r.right - r.left, r.bottom - r.top, m_list, NULL, g_inst, 0);
    SendMessage(m_edit, WM_SETFONT, SendMessage(m_hwnd, WM_GETFONT, 0, 0), 0);
    SetWindowText(m_edit, text);
    SendMessage(m_edit, EM_SETSEL, 0, -1);
    ShowWindow(m_edit, SW_SHOW);
    SetFocus(m_edit);
  }
}

HWND THA_FileInfoWnd::GetEdit() const
{
  return m_edit;
}

void THA_FileInfoWnd::EditConfirm()
{
  char key[1024];
  char name[4096];
  char newname[4096];
  int len = GetWindowText(m_edit, newname, sizeof(newname));
  if (len >= 0)
  {
    int pos = ListView_GetNextItem(m_list, -1, LVIS_FOCUSED);
    if (pos >= 0)
    {
      ListView_GetItemText(m_list, pos, 0, key, sizeof(key));
      ListView_GetItemText(m_list, pos, 1, name, sizeof(name));
      ListView_SetItemText(m_list, pos, 1, newname);
      if (g_metadataqueue->GetFilePathCount() == 1)
      {
        g_metadataqueue->UpdateMetadata(key, newname);
      }
      SetFocus(m_list);
      DestroyWindow(m_edit);
      m_edit = NULL;
      ListView_SetItemState(m_list, pos, LVIS_SELECTED, LVIS_SELECTED);
      ListView_SetItemState(m_list, pos, LVIS_FOCUSED, LVIS_FOCUSED);
      InvalidateRect(m_hwnd, NULL, FALSE);
    }
  }
}

void THA_FileInfoWnd::EditAbort()
{
  SetFocus(m_list);
  DestroyWindow(m_edit);
  InvalidateRect(m_hwnd, NULL, FALSE);
  m_edit = NULL;

  int pos = ListView_GetNextItem(m_list, -1, LVIS_FOCUSED);
  if (pos >= 0)
  {
    ListView_SetItemState(m_list, pos, LVIS_SELECTED, LVIS_SELECTED);
    ListView_SetItemState(m_list, pos, LVIS_FOCUSED, LVIS_FOCUSED);
    InvalidateRect(m_list, NULL, FALSE);
  }
}

void THA_FileInfoWnd::OnInitDialog(WPARAM wparam, LPARAM lparam)
{
  m_resize.init(m_hwnd);
  m_resize.init_item(IDC_LIST1, 0.0f, 0.0f, 1.0f, 1.0f);

  int x = GetPrivateProfileInt(THA_NAME, "fileinfo_wnd_x", 0, g_inipath.Get());
  int y = GetPrivateProfileInt(THA_NAME, "fileinfo_wnd_y", 50, g_inipath.Get());
  int w = GetPrivateProfileInt(THA_NAME, "fileinfo_wnd_w", 320, g_inipath.Get());
  int h = GetPrivateProfileInt(THA_NAME, "fileinfo_wnd_h", 300, g_inipath.Get());
  SetWindowPos(m_hwnd, NULL, x, y, w, h, SWP_NOZORDER | SWP_NOACTIVATE);

  m_list = GetDlgItem(m_hwnd, IDC_LIST1);
  WDL_UTF8_HookListView(m_list);

  ListView_SetExtendedListViewStyleEx(m_list,
    LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER,
    LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);

  //m_playlist_menu = (HMENU)GetSubMenu(LoadMenu(g_inst, MAKEINTRESOURCE(IDR_LIBRARY_CONTEXT)), 4);

  ColumnHeader default_column_header[] =
  {
    { THA_METADATA_KEY, "Name", 140, LVCFMT_LEFT, 1, 0 },
    { THA_METADATA_VAL, "Value", 150, LVCFMT_LEFT, 1, 0 }
  };

  m_column_header.Resize(sizeof(default_column_header) /
    sizeof(default_column_header[0]));

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    ColumnHeader *ch = m_column_header.Get();
    ch[i] = default_column_header[i];
  }

  LVCOLUMN lvc = { 0, };
  lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;

  w = 0;
  WDL_FastString s;

  ColumnHeader *ch = m_column_header.Get();

  for (int i = 0; i < m_column_header.GetSize(); i++)
  {
    s.SetFormatted(128, "fileinfo_column_width_%02d", i);
    w = GetPrivateProfileInt(THA_NAME, s.Get(), ch[i].width, g_inipath.Get());
    lvc.cx = w;
    lvc.fmt = ch[i].fmt;
    lvc.pszText = (char *)ch[i].text;
    ListView_InsertColumn(m_list, i, &lvc);
  }

  WDL_TypedBuf<int> order_array;
  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "fileinfo_column_order_%02d", i);
    oa[i] = GetPrivateProfileInt(THA_NAME, s.Get(), i, g_inipath.Get());
  }

  ListView_SetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  ListView_DeleteAllItems(m_list);

  WDL_StringKeyedArray<WDL_FastString *> fileinfo(false, delete_str);

  HWND mainlist = GetDlgItem(g_mainwnd->Handle(), IDC_LIST1);

  THA_IFileInput *fi = NULL;

  int pos = ListView_GetNextItem(mainlist, -1, LVIS_SELECTED | LVIS_FOCUSED);
  if (pos >= 0)
  {
    char text[2048];
    int subitem = g_mainwnd->GetHeaderSubitem(ID_LIBRARY_HEADER_FILEPATH);
    ListView_GetItemText(mainlist, pos, subitem, text, sizeof(text));

    fi = CreateFileInput(text);
  }

  if (!fi)
  {
    if (g_fileinfownd) { delete g_fileinfownd; g_fileinfownd = NULL; }
    return;
  }

  m_strbuf.Set(fi->GetFileName());
  fileinfo.AddUnsorted("File name", new WDL_FastString(m_strbuf.get_filepart()));

  m_strbuf.Set(fi->GetFileName());
  m_strbuf.remove_filepart(true);
  fileinfo.AddUnsorted("File directory", new WDL_FastString(m_strbuf.Get()));
  fileinfo.AddUnsorted("File path", new WDL_FastString(fi->GetFileName()));
  fileinfo.AddUnsorted("Type", new WDL_FastString(fi->GetType()));

  m_strbuf.SetFormatted(512, "%d Hz", (int)fi->GetSampleRate());
  fileinfo.AddUnsorted("Sample rate", new WDL_FastString(m_strbuf.Get()));

  m_strbuf.SetFormatted(512, "%d-bit", fi->GetBitsPerSample());
  fileinfo.AddUnsorted("Bit depth", new WDL_FastString(m_strbuf.Get()));

  m_strbuf.SetFormatted(512, "%d", fi->GetChannels());
  fileinfo.AddUnsorted("Channels", new WDL_FastString(m_strbuf.Get()));

  m_strbuf.SetFormatted(512, "%f sec", fi->GetLength());
  fileinfo.AddUnsorted("Length", new WDL_FastString(m_strbuf.Get()));

  WDL_FileRead fr(fi->GetFileName());
  m_strbuf.SetFormatted(512, "%" WDL_PRI_INT64 " bytes", fr.GetSize());
  fileinfo.AddUnsorted("File size", new WDL_FastString(m_strbuf.Get()));

  for (int i = 0; i < fileinfo.GetSize(); i++)
  {
    const char *key;
    WDL_FastString *val = fileinfo.Enumerate(i, &key);

    LVITEM lvi = { 0, };
    int si = 0;
    lvi.mask = LVIF_TEXT | LVIF_PARAM;
    lvi.lParam = (LPARAM)i;
    lvi.iItem = i;
    lvi.iSubItem = si++;
    lvi.pszText = (char *)key;
    lvi.cchTextMax = (int)strlen(key);
    ListView_InsertItem(m_list, &lvi);

    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = si++;
    lvi.pszText = (char *)val->Get();
    lvi.cchTextMax = val->GetLength();
    ListView_SetItem(m_list, &lvi);
  }

  fileinfo.DeleteAll();
  if (fi) delete fi;

  int bk = g_preferences->GetBackgroundColor();
  int fg = g_preferences->GetTextColor();
  ListView_SetBkColor(m_list, RGB((bk >> 16) & 0xFF,(bk >> 8) & 0xFF, bk & 0xFF));
  ListView_SetTextBkColor(m_list, RGB((bk >> 16) & 0xFF,(bk >> 8) & 0xFF, bk & 0xFF));
  ListView_SetTextColor(m_list, RGB((fg >> 16) & 0xFF,(fg >> 8) & 0xFF, fg & 0xFF));
}

void THA_FileInfoWnd::OnTimer(WPARAM wparam, LPARAM lparam)
{}

void THA_FileInfoWnd::OnDestroy(WPARAM wparam, LPARAM lparam)
{
  RECT r;
  GetWindowRect(m_hwnd, &r);

  int x = r.left;
  int y = r.top;
  int w = r.right - r.left;
  int h = r.bottom - r.top;

  WDL_FastString xstr, ystr, wstr, hstr;
  xstr.SetFormatted(32, "%d", x);
  ystr.SetFormatted(32, "%d", y);
  wstr.SetFormatted(32, "%d", w);
  hstr.SetFormatted(32, "%d", h);

  WritePrivateProfileString(THA_NAME, "fileinfo_wnd_x", xstr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "fileinfo_wnd_y", ystr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "fileinfo_wnd_w", wstr.Get(), g_inipath.Get());
  WritePrivateProfileString(THA_NAME, "fileinfo_wnd_h", hstr.Get(), g_inipath.Get());

  WDL_FastString s;
  WDL_TypedBuf<int> order_array;
  WDL_FastString str;

  order_array.Resize(Header_GetItemCount(ListView_GetHeader(m_list)));
  ListView_GetColumnOrderArray(m_list, order_array.GetSize(), order_array.Get());

  int *oa = order_array.Get();

  for (int i = 0; i < order_array.GetSize(); i++)
  {
    s.SetFormatted(128, "fileinfo_column_order_%02d", i);
    str.SetFormatted(32, "%d", oa[i]);
    WritePrivateProfileString(THA_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  for (int i = 0; i < Header_GetItemCount(ListView_GetHeader(m_list)); i++)
  {
    s.SetFormatted(128, "fileinfo_column_width_%02d", i);
    str.SetFormatted(32, "%d", ListView_GetColumnWidth(m_list, i));
    WritePrivateProfileString(THA_NAME, s.Get(), str.Get(), g_inipath.Get());
  }

  m_hwnd = NULL;
}

void THA_FileInfoWnd::OnSize(WPARAM wparam, LPARAM lparam)
{
  if (wparam != SIZE_MINIMIZED)
  {
    int w = LOWORD(lparam);
    ColumnHeader *ch = m_column_header.Get();
    if (ch)
    {
      int c0w = ListView_GetColumnWidth(m_list, 0);
      int c1w = w - 20 - c0w;
      ListView_SetColumnWidth(m_list, 1, c1w);
    }
    m_resize.onResize();
  }
}

void THA_FileInfoWnd::OnCommand(WPARAM wparam, LPARAM lparam)
{
  switch(LOWORD(wparam))
  {
  case IDOK:
    {
      if (g_fileinfownd) { delete g_fileinfownd; g_fileinfownd = NULL; }
    } break;
  case IDCANCEL:
    {
      if (g_fileinfownd) { delete g_fileinfownd; g_fileinfownd = NULL; }
    } break;
  }
}

void THA_FileInfoWnd::OnSysCommand(WPARAM wparam, LPARAM lparam)
{}

WDL_DLGRET THA_FileInfoWnd::FileInfoWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  THA_FileInfoWnd *self = (THA_FileInfoWnd *)GetWindowLongPtr(hwnd, GWLP_USERDATA);

  if (!self && msg == WM_INITDIALOG)
  {
    SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam);
    self = (THA_FileInfoWnd *)lparam;
    self->m_hwnd = hwnd;
  }

  if (WDL_likely(self))
  {
    return self->FileInfoWndLoop(msg, wparam, lparam);
  }
  else
  {
    return 0;
  }
}

WDL_DLGRET THA_FileInfoWnd::FileInfoWndLoop(UINT msg, WPARAM wparam, LPARAM lparam)
{
  switch (msg)
  {
    case WM_INITDIALOG: OnInitDialog(wparam, lparam); break;
    case WM_TIMER: OnTimer(wparam, lparam); break;
    case WM_DESTROY: OnDestroy(wparam, lparam); break;
    case WM_SIZE: OnSize(wparam, lparam); break;
    case WM_COMMAND: OnCommand(wparam, lparam); break;
    case WM_SYSCOMMAND: OnSysCommand(wparam, lparam); break;
  }

  return 0;
}
