// Download.cpp: implementation of the Download class.
//
//////////////////////////////////////////////////////////////////////

/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2011 Melin Software HB
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Stigbergsvägen 7, SE-75242 UPPSALA, Sweden
    
************************************************************************/

#include "stdafx.h"
#include "Download.h"
#include <Wininet.h>
#include "Localizer.h"
#include "meos_util.h"

#include <sys/stat.h>
#include <io.h>
#include <fcntl.h>

#include <process.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Download::Download()
{
	hThread = 0;
	doExit = false;
//	hProgress = NULL;

  fileno = 0;
	hInternet = NULL;
	hURL = NULL;

	bytesLoaded = 0;
	bytesToLoad = 1024;

	success = false;
}

Download::~Download()
{
	shutDown();
	endDownload();

	if(hInternet)
		InternetCloseHandle(hInternet);
}

void __cdecl SUThread(void *ptr)
{
	Download *dwl=(Download *)ptr;
	dwl->initThread();	
}

bool Download::createDownloadThread() {	
	doExit=false;
	hThread=_beginthread(SUThread, 0, this);

	if(hThread==-1)	{
		hThread=0;
		return false;
	}

	return true;
}

void Download::shutDown()
{
	if(hThread)	{
		doExit=true;

		int m=0;
		while(m<100 && hThread) {
			Sleep(0);
			Sleep(10);
			m++;
		}
		//If unsuccessful ending thread, do it violently
		if(hThread) {
			OutputDebugString("Terminate thread...\n");
			TerminateThread(HANDLE(hThread), 0);
			CloseHandle(HANDLE(hThread));					   			
		}
		hThread=0;
	}

}

void Download::initThread()
{
	int status = true;
	while(!doExit && status)	{
	//	if(hProgress)
	//		PostMessage(hProgress, PBM_SETPOS, ((BytesLoaded*100)/BytesToLoad)%100, 0);
		status = doDownload();
	}

//	PostMessage(hProgress, PBM_SETPOS, 100, 0);
	hThread=0;
}
/*
void Download::setProgress(HWND hWnd)
{
	hProgress = hWnd;
}
*/
void Download::initInternet() {
	hInternet = InternetOpen("MeOS", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);

	if(hInternet==NULL)	{
		DWORD ec = GetLastError();
    string error = lang.tl("Error: X#" + getErrorMessage(ec));
    throw std::exception(error.c_str());
	}
}

void Download::downloadFile(const string &url, const string &file, const vector< pair<string, string> > &headers)
{		 
	if(hURL || !hInternet) 
    throw std::exception("Not inititialized");

	success = false;

  string hdr;
  for (size_t k = 0; k<headers.size(); k++)
    hdr += headers[k].first + ": " + headers[k].second; 

  string url2 = url;
  hURL = InternetOpenUrl(hInternet, url2.c_str(), hdr.empty() ? 0 : hdr.c_str(), hdr.length(), INTERNET_FLAG_DONT_CACHE, 0);

	if (!hURL) {		
    int err = GetLastError();
    string msg2 = getErrorMessage(err);
		DWORD em = 0, blen = 256;
    char bf2[256];
		InternetGetLastResponseInfo(&em, bf2, &blen);
    string msg = "Failed to connect to: " + url;
    msg += " " + msg2;
    if (bf2[0] != 0)
      msg += " (" + string(bf2) + ")";
    throw std::exception(msg.c_str());
	}


	DWORD dwContentLen = 0;
  DWORD dwBufLen = sizeof(dwContentLen);
  BOOL success = HttpQueryInfo(hURL, 
                          HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, 
                          (LPVOID)&dwContentLen, &dwBufLen, 0);

  if (success)
    setBytesToDownload(dwContentLen);
  else
    setBytesToDownload(0);

  DWORD dwStatus = 0;
  dwBufLen = sizeof(dwStatus);
  success = HttpQueryInfo(hURL, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER,
                          (LPVOID)&dwStatus, &dwBufLen, 0);

  if (success) {
    if (dwStatus >= 400) {
      char bf[256];
      switch (dwStatus) {
        case HTTP_STATUS_BAD_REQUEST:
          sprintf_s(bf, "HTTP Error 400: The request could not be processed by the server due to invalid syntax.");
          break;
        case HTTP_STATUS_DENIED:
          sprintf_s(bf, "HTTP Error 401: The requested resource requires user authentication.");
          break;
        case HTTP_STATUS_FORBIDDEN:
          sprintf_s(bf, "HTTP Error 403: Åtkomst nekad (access is denied).");
          break;
        case HTTP_STATUS_NOT_FOUND:
          sprintf_s(bf, "HTTP Error 404: Resursen kunde ej hittas (not found).");
          break;
        case HTTP_STATUS_NOT_SUPPORTED:
          sprintf_s(bf, "HTTP Error 501: Förfrågan stöds ej (not supported).");
          break;
        case HTTP_STATUS_SERVER_ERROR:
          sprintf_s(bf, "HTTP Error 500: Internt serverfel (server error).");
          break;
        default: 
          sprintf_s(bf, "HTTP Status Error %d", dwStatus);
      }      
      throw dwException(bf, dwStatus);
    }
  }

  fileno=open(file.c_str(), O_BINARY|O_CREAT|O_WRONLY|O_TRUNC, S_IREAD|S_IWRITE);

	if(fileno==-1)	{
		fileno=0;
		endDownload();
		char bf[256];
    sprintf(bf, "Error opening '%s' for writing", file);
    throw std::exception(bf);
	}

	bytesLoaded = 0;
	return;
}

void Download::endDownload()
{
	if(hURL && hInternet)	{
		InternetCloseHandle(hURL);
		hURL=NULL;
	}

	if (fileno)	{
		close(fileno);
		fileno=0;
	}
}

bool Download::doDownload()
{
	if(hURL && hInternet)	{
		char buffer[512];

		DWORD bRead;
		if(InternetReadFile(hURL, buffer, 512, &bRead)) {
			//Success!
			if(bRead==0) {					
				//EOF
				success=true;
				endDownload();
			}
			else{
				if(write(fileno, buffer, bRead) != int(bRead)) {
					endDownload();
          return false;
				}
				bytesLoaded+=bRead;
				return true;
			}	
		}	
	}	
	return false;
}

void Download::setBytesToDownload(DWORD btd)
{
	bytesToLoad = btd;
}

bool Download::isWorking()
{
	return hThread!=0;
}

bool Download::successful()
{
	return success;
}

/*
int Download::processMessages()
{
	MSG msg;
	while(isWorking())
	{					 					
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{						
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else Sleep(0);
	}

	return isWorking();
}
*/