// Unicode support by Jim Park -- 08/20/2007

#include "makensisw.h"
#include "update.h"
#include <wininet.h>
#include "utils.h" // OpenUrlInDefaultBrowser

#define mbtitle "NSIS Update"

static LPSTR InetGetErrorStringAllocA(DWORD ec)
{
  LPSTR buf;
  DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS;
  if (FormatMessageA(flags|FORMAT_MESSAGE_FROM_SYSTEM, 0, ec, 0, (LPSTR) &buf, 0, 0))
    return buf;
  HMODULE hMod = GetModuleHandle(TEXT("WinInet"));
  if (hMod && FormatMessageA(flags|FORMAT_MESSAGE_FROM_HMODULE, hMod, ec, 0, (LPSTR) &buf, 0, 0))
    return buf;
#if 0 // We are not using WinHttp* functions
  hMod = GetModuleHandle(TEXT("WinHTTP"));
  if (hMod && FormatMessageA(flags|FORMAT_MESSAGE_FROM_HMODULE, hMod, ec, 0, (LPSTR) &buf, 0, 0))
    return buf;
#endif
  return NULL;
}

static void InetWorkOnline(HINTERNET hInet)
{
  //msdn.microsoft.com/en-us/library/Aa768170#Supporting Offline Browsing in Applications and Components
  DWORD cbio = sizeof(DWORD), op32;
  if (InternetQueryOption(hInet, INTERNET_OPTION_CONNECTED_STATE, &op32, &cbio))
  {
    if (INTERNET_STATE_DISCONNECTED_BY_USER & op32)
    {
      INTERNET_CONNECTED_INFO ci = { INTERNET_STATE_CONNECTED, 0 };
      InternetSetOption(hInet, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
    }
  }
}

static DWORD InetSynchronousReadFile(HINTERNET hConn, void*Buffer, DWORD cbBuffer, DWORD*pcbReceived)
{
  *pcbReceived = 0;
  for (DWORD cbio;;)
  {
    if (!cbBuffer)
      break;
    if (!InternetReadFile(hConn, Buffer, cbBuffer, &cbio))
      return GetLastError();
    if (!cbio)
      break; // EOF, we are done.
    Buffer = ((char*)Buffer) + cbio, cbBuffer -= cbio;
    (*pcbReceived) += cbio;
  }
  return ERROR_SUCCESS;
}

static DWORD CALLBACK UpdateCheckWebrequestThread(LPVOID ThreadParam)
{
  char buf[300];
  wsprintfA(buf, "%s%s", NSIS_UC_URL, g_sdata.brandingv);

  InternetAttemptConnect(0);
  UINT mbicon = MB_ICONINFORMATION;
  LPCSTR msg = NULL;
  HINTERNET hInet = InternetOpenA("MakeNSISw (WinInet)", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
  DWORD gle = GetLastError();
  if (hInet)
  {
    InetWorkOnline(hInet);

    // Note: InternetOpenUrlW does not handle the http headers in the same way, be careful if you stop forcing the A version
    static const CHAR httpheaders[] = "Accept:*/*\r\n";
    DWORD connflags = INTERNET_FLAG_NO_UI|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS;
    connflags |= INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_NO_COOKIES;
    connflags |= INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_RELOAD;
    //if ((32|buf[4]) == 's') connflags |= INTERNET_FLAG_SECURE;
    HINTERNET hConn = InternetOpenUrlA(hInet, buf, httpheaders, -1, connflags, 0);
    gle = GetLastError();
    if (hConn)
    {
      char response[30+1];
      DWORD cbRecv;
      gle = InetSynchronousReadFile(hConn, response, sizeof(response)-1, &cbRecv);
      if (!gle)
      {
        response[cbRecv] = '\0';
        switch(response[0])
        {
        case '0':
          msg = "There is no update available for NSIS at this time.";
          break;
        case '1':
        case '2':
          if (cbRecv > 2 && '|' == response[1])
          {
            const char *reltypestr = '1' == response[0] ? "it" : "this preview release";
            wsprintfA(buf, "NSIS %.50s is now available. Would you like to download %s now?", response+2, reltypestr);
            mbicon = MB_ICONQUESTION, msg = buf;
            break;
          }
          // fall through
        default:
          gle = ERROR_INVALID_DATA;
        }
      }
      InternetCloseHandle(hConn);
    }
    InternetCloseHandle(hInet);
  }

  HWND hwndOwner = (HWND) ThreadParam;
  // Hopefully we don't end up with a race where our main window has been closed 
  // and the HWND has been reused just as this thread completes its work
  if (IsWindow(hwndOwner))
  {
    if (!msg)
    {
      LPSTR dynbuf = InetGetErrorStringAllocA(gle);
      wsprintfA(buf, "There was a problem checking for updates, please try again later.\n\nError: %u %.200s", gle, dynbuf ? dynbuf : "");
      mbicon = MB_ICONSTOP, msg = buf;
      LocalFree(dynbuf);
    }

    UINT mbtype = MB_ICONQUESTION == mbicon ? MB_YESNO : MB_OK;
    int mbret = MessageBoxA(hwndOwner, msg, mbtitle, mbtype|mbicon);
    if (mbret == IDYES && mbtype == MB_YESNO)
    {
      OpenUrlInDefaultBrowser(hwndOwner, NSIS_DL_URL);
    }

    EnableMenuItem(g_sdata.menu, IDM_NSISUPDATE, MF_ENABLED);
  }
  return 0;
}

void CheckForUpdate()
{
  if (strstr(g_sdata.brandingv, "cvs"))
  {
    MessageBoxA(g_sdata.hwnd, "Cannot check for new version of nightly builds. To update, download a new nightly build.", mbtitle, MB_OK|MB_ICONSTOP);
    return;
  }

  EnableMenuItem(g_sdata.menu, IDM_NSISUPDATE, MF_GRAYED);
  DWORD tid;
  CloseHandle(CreateThread(NULL, 0, UpdateCheckWebrequestThread, (LPVOID) g_sdata.hwnd, 0, &tid));
}
