#include "inspch.h"
#include "inseng.h"
#include "download.h"
#include "advpub.h"
#include "site.h"
#include "sitemgr.h"
#include "util2.h"
#include "util.h"

#define SITEFILENAME  "sites.dat"
#define SITEARRAY_GROWTHFACTOR 100

#define NUM_RETRIES 2

#define SITEQUERYSIZE_V1    8
#define SITEQUERYSIZE_V2   12

CDownloadSiteMgr::CDownloadSiteMgr(IUnknown **punk)
{
   m_cRef = 0;
   m_pszUrl = 0;
 
   m_pquery = NULL;
   m_ppdls = (DOWNLOADSITE **) malloc(SITEARRAY_GROWTHFACTOR * sizeof(DOWNLOADSITE *));
   m_numsites = 0;
   m_arraysize = SITEARRAY_GROWTHFACTOR;
   
   AddRef();
   *punk = (IDownloadSiteMgr *) this;
}

CDownloadSiteMgr::~CDownloadSiteMgr()
{
   if(m_ppdls)
   {
      for(UINT i=0; i < m_numsites; i++)
         DeleteDownloadSite(m_ppdls[i]);

      free(m_ppdls);
   }

   // Delete the query structure
   if(m_pquery)
   {
      if(m_pquery->pszLang)
         delete m_pquery->pszLang;

      delete m_pquery;
   }

   if(m_pszUrl)
      delete m_pszUrl;

   DllRelease();
}   

//************ IUnknown implementation ***************

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

STDMETHODIMP_(ULONG) CDownloadSiteMgr::AddRef()                      
{
   return(m_cRef++);
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

STDMETHODIMP_(ULONG) CDownloadSiteMgr::Release()
{
   ULONG temp = --m_cRef;

   if(temp == 0)
      delete this;
   return temp;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

STDMETHODIMP CDownloadSiteMgr::QueryInterface(REFIID riid, void **ppv)
{
   *ppv = NULL;

   if((riid == IID_IUnknown) || (riid == IID_IDownloadSiteMgr))
      *ppv = (IDownloadSiteMgr *)this;
   
   if(*ppv == NULL)
      return E_NOINTERFACE;
   
   AddRef();
   return NOERROR;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

STDMETHODIMP CDownloadSiteMgr::Initialize(LPCSTR pszUrl, SITEQUERYPARAMS *psqp)
{
   HRESULT hr = S_OK;
   char szPath[MAX_PATH];
      
   if(!pszUrl)
      return E_INVALIDARG; 

   if(!m_ppdls)
      return E_OUTOFMEMORY;

   m_pszUrl = CopyAnsiStr(pszUrl);
   if(!m_pszUrl)
      return E_OUTOFMEMORY;
    
   if(psqp != NULL)
   {
      m_pquery = new SITEQUERYPARAMS;
      if(!m_pquery)
         return E_OUTOFMEMORY;

      ZeroMemory(m_pquery, sizeof(SITEQUERYPARAMS));
      
      if(psqp->pszLang)
      {   
         m_pquery->pszLang = CopyAnsiStr(psqp->pszLang);
         if(!m_pquery->pszLang)
            return E_OUTOFMEMORY;
      }
      if((psqp->cbSize >= SITEQUERYSIZE_V2) && psqp->pszRegion)
      {
         m_pquery->pszRegion = CopyAnsiStr(psqp->pszRegion);
         if(!m_pquery->pszRegion)
            return E_OUTOFMEMORY;
      }
   }

   CDownloader *pDownloader = new CDownloader();
   if(pDownloader)
   {
      hr = E_FAIL;
      for(int i = 0; (i < NUM_RETRIES) && FAILED(hr) ; i++)
      {
         hr = pDownloader->SetupDownload(m_pszUrl, (IMyDownloadCallback *) this, DOWNLOADFLAGS_USEWRITECACHE, SITEFILENAME);
         if(FAILED(hr))
            break;
         hr = pDownloader->DoDownload(szPath, sizeof(szPath));
      }
      pDownloader->Release();
     
   }
      
   // Parse the file
   if(SUCCEEDED(hr))
   {
      hr = ParseSiteFile(szPath);
   }

   // delete the dir we downloaded to

   if(GetParentDir(szPath))
      DelNode(szPath, 0);
   
   return hr;
}


STDMETHODIMP CDownloadSiteMgr::EnumSites(DWORD dwIndex, IDownloadSite **pds)
{
   HRESULT hr = NOERROR;
   
   if(!pds)
      return E_POINTER;

   *pds = NULL;
   
   if(dwIndex < m_numsites)
   {
      *pds = CopyDownloadSite(m_ppdls[dwIndex]);
      if(! (*pds) )
         hr = E_OUTOFMEMORY;
   }
   else
      hr = E_FAIL;
   
   return hr;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

HRESULT CDownloadSiteMgr::OnProgress(ULONG progress, LPCSTR pszStatus)
{
   // Not interesting
   return NOERROR;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

// This probably changes after beta1

HRESULT CDownloadSiteMgr::ParseSiteFile(LPCSTR pszPath)
{
   HANDLE hfile;
   DWORD dwSize;
   DOWNLOADSITE *p;
   LPSTR pBuf, pCurrent, pEnd;

   m_onegoodsite = FALSE;

   hfile = CreateFile(pszPath, GENERIC_READ, 0, NULL, 
                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  

   if(hfile == INVALID_HANDLE_VALUE)
      return E_FAIL;

   
   dwSize = GetFileSize(hfile, NULL);
   if(dwSize == DWORD(-1)) {
      CloseHandle(hfile);
      return E_UNEXPECTED;
   }

   pBuf = new char[dwSize + 1];

   if(!pBuf)
   {
      CloseHandle(hfile);
      return E_OUTOFMEMORY;
   }
   // Copy contents of file to our buffer
   
   if(!ReadFile(hfile, pBuf, dwSize, &dwSize, NULL)) {
      delete pBuf;
      CloseHandle(hfile);
      return E_UNEXPECTED;
   }
   
   pCurrent = pBuf;
   pEnd = pBuf + dwSize;
   *pEnd = 0;

   // One pass thru replacing \n or \r with \0
   while(pCurrent <= pEnd)
   {
      if(*pCurrent == '\r' || *pCurrent == '\n')
         *pCurrent = 0;
      pCurrent++;
   }

   pCurrent = pBuf;
   while(1)
   {
      while(pCurrent <= pEnd && *pCurrent == 0)
         pCurrent++;

      // we are now pointing at begginning of line or pCurrent > pBuf
      if(pCurrent > pEnd)
         break;

      p = ParseAndAllocateDownloadSite(pCurrent);
      if(p)
         AddSite(p);
      
      pCurrent += lstrlen(pCurrent);
   }

   delete pBuf;
   CloseHandle(hfile);

   if(!m_onegoodsite)
      return E_UNEXPECTED;
   else
      return NOERROR;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

// BUGBUG: Stack is getting large here - consider writing with
//   only one buffer that is reused. Have to break up the nice Allocate
//   call
DOWNLOADSITE *CDownloadSiteMgr::ParseAndAllocateDownloadSite(LPSTR psz)
{
   char szUrl[1024];
   char szName[256];
   char szlang[256];
   char szregion[256];
   BOOL bQueryTrue = TRUE;
   DOWNLOADSITE *p = NULL;

   GetStringField(psz, 0, szUrl, sizeof(szUrl)); 
   GetStringField(psz,1, szName, sizeof(szName));
   GetStringField(psz, 2, szlang, sizeof(szlang));
   GetStringField(psz, 3, szregion, sizeof(szregion));

   if(szUrl[0] == 0 || szName[0] == 0 || szlang[0] == 0 || szregion[0] == 0)
      return NULL;

   m_onegoodsite = TRUE;
   
   // Hack - Check language against what is in our query params
   //    a) this query should have already been performed on the server
   //    b) or it should be more generic/its own function

   if(m_pquery)
   {
      if(m_pquery->pszLang && (lstrcmpi(m_pquery->pszLang, szlang) != 0))
         bQueryTrue = FALSE;

      // query for region
      if(bQueryTrue && m_pquery->pszRegion && (lstrcmpi(m_pquery->pszRegion, szregion) != 0))
         bQueryTrue = FALSE;
   }

   if(bQueryTrue)
      p = AllocateDownloadSite(szUrl, szName, szlang, szregion);

   return p;
}

//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

DWORD CDownloadSiteMgr::TranslateLanguage(LPSTR psz)
{
   return ( (DWORD) AtoL(psz) );
}


//=--------------------------------------------------------------------------=
// Function name here
//=--------------------------------------------------------------------------=
// Function description
//
// Parameters:
//   
// Returns:
//
// Notes:
//

HRESULT CDownloadSiteMgr::AddSite(DOWNLOADSITE *pdls)
{
   if((m_numsites % m_arraysize == 0) && m_numsites != 0)
   {
      m_arraysize += SITEARRAY_GROWTHFACTOR;
      DOWNLOADSITE **pp = (DOWNLOADSITE **) realloc( m_ppdls, m_arraysize * sizeof(DOWNLOADSITE *));
      if(pp)
         m_ppdls = pp;
      else
      {
         m_arraysize -= SITEARRAY_GROWTHFACTOR;
         return E_OUTOFMEMORY;
      }
   }

   if(!m_ppdls)
   {
       return E_OUTOFMEMORY;
   }
   
   m_ppdls[m_numsites++] = pdls;
   return NOERROR;
}