/**********************************************************************
 *
 * startbrowser.cpp
 * Copyright (C) 1999  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "startbrowser.h"
#include <windows.h>
#include <DDE.H>
#include <DDEML.H>
#include <string.h>
#include <direct.h>
#include "locate.h"

#define SB_COMLEN 1024
#define SB_DDETIMEOUT 60000UL  // One minute in milliseconds

int numbrowserstarted = 0;

static int inited = 0;

static DWORD idInst = 0;
static HINSTANCE hinst = NULL;
static HSZ hszNetscapeName = NULL;
static HSZ hszIExploreName = NULL;
static HSZ hszOpenURLTopic = NULL;
static HSZ hszActivateTopic = NULL;

// budget callback
HDDEDATA CALLBACK DdeCallback(UINT uType,     /* transaction type                 */
			      UINT uFmt,      /* clipboard data format            */
			      HCONV hconv,    /* handle of conversation           */
			      HSZ hsz1,       /* handle of string                 */
			      HSZ hsz2,       /* handle of string                 */
			      HDDEDATA hdata, /* handle of global memory object   */
			      DWORD dwData1,  /* transaction-specific data        */
			      DWORD dwData2)  /* transaction-specific data        */ 
{
  return (HDDEDATA) NULL; 
}

// returns 0 on failure
static int talkdde (HSZ hszServName, HSZ hszSysTopic, char *requeststr) {
  HSZ hszRequest;
  HCONV hConv;
  HDDEDATA result;
  
  // try to connect to the newly started browser
  hConv = DdeConnect (idInst, hszServName, hszSysTopic, (PCONVCONTEXT) NULL);
  if (hConv == NULL) return 0;
  
  hszRequest = DdeCreateStringHandle (idInst, requeststr, CP_WINANSI);
  result = DdeClientTransaction (
				 NULL,          // pointer to data to pass to server
				 0,             // length of data
				 hConv,         // handle to conversation
				 hszRequest,    // handle to item name string
				 CF_TEXT,       // clipboard data format
				 XTYP_REQUEST,  // transaction type
				 SB_DDETIMEOUT, // time-out duration
				 NULL           // pointer to transaction result
				 );

  if (result != NULL) DdeFreeDataHandle (result);
  DdeDisconnect(hConv);
  DdeFreeStringHandle (idInst, hszRequest);

  return 1;
}

// returns 0 on failure
static int openurl(HSZ hszServName, HSZ hszActivateTopic, HSZ hszOpenURLTopic, char *url) {
  char requeststr[65600];
  
  // bring the browser to the front, don't worry if this fails
  talkdde (hszServName, hszActivateTopic, "0xFFFFFFFF,0x0");
  
  // tell the browser to open a url
  strcpy (requeststr, "\"");
  strcat (requeststr, url);
  strcat (requeststr, "\",,0xFFFFFFFF,0x0,,,");
  
  return talkdde (hszServName, hszOpenURLTopic, requeststr);
}

// returns 0 on failure
static int tryconnect(HSZ hszServName, HSZ hszSysTopic) {
  // try to connect to a running browser
  HCONV hConv = DdeConnect (idInst, hszServName, hszSysTopic, (PCONVCONTEXT) NULL);
  if (hConv == NULL) return 0;
  DdeDisconnect(hConv);
  return 1;
}

static void freestrings () {
  // free up any currently allocated strings
  if (hszNetscapeName != NULL) DdeFreeStringHandle (idInst, hszNetscapeName);
  hszNetscapeName = NULL;
  if (hszIExploreName != NULL) DdeFreeStringHandle (idInst, hszIExploreName);
  hszIExploreName = NULL;
  if (hszOpenURLTopic != NULL) DdeFreeStringHandle (idInst, hszOpenURLTopic);
  hszOpenURLTopic = NULL;
  if (hszActivateTopic != NULL) DdeFreeStringHandle (idInst, hszActivateTopic);
  hszActivateTopic = NULL;
}

static void getbrowserinfo() {
  freestrings();
  
  hszNetscapeName = DdeCreateStringHandle(idInst,"NETSCAPE",CP_WINANSI);
  hszIExploreName = DdeCreateStringHandle(idInst,"IExplore",CP_WINANSI);
  
  hszOpenURLTopic = DdeCreateStringHandle(idInst,"WWW_OpenURL",CP_WINANSI);
  hszActivateTopic = DdeCreateStringHandle(idInst,"WWW_Activate",CP_WINANSI);
}

// returns SB_NOERROR on success, 
// SB_ALREADYINIT or SB_DDE_FAILINIT on failure
int initstartbrowser () {
  if (inited) return SB_ALREADYINIT;
  
  // from nstest if(DdeInitialize(&CDDEObject::m_dwidInst, NstestDdeCallBack, APPCLASS_STANDARD, 0ul))
  if (DdeInitialize (&idInst, (PFNCALLBACK) DdeCallback, 
		     APPCLASS_STANDARD, 0)
      != DMLERR_NO_ERROR) {
    freestrings();
    inited = 0;
    return SB_DDE_FAILINIT;
  }

  getbrowserinfo();
  
  inited = 1;
  return SB_NOERROR;
}


// returns SB_NOERROR on success,
// SB_DDE_FAILDEINIT of failure
int deinitstartbrowser () {
  // make sure this has been inited
  // not being inited here, however, is not
  // important
  if (!inited) return SB_NOERROR;
  inited = 0;
  
  freestrings();
  
  if (DdeUninitialize(idInst)) return SB_NOERROR;
  
  return SB_DDE_FAILDEINIT;
}


// startbrowser will try to start a browser (or connect to a running browser).
//
// Returns SB_NOERROR on success,
// SB_NOTINITED, SB_FAIL_BADFORMAT, SB_FAIL_NOTFOUND, SB_FAIL_NORESOURCES, or
// SB_FAILCONNECTBROWSER on failure
int startbrowser (char *url, char *browser_exe, char *startdir) {
  char newcommand[SB_COMLEN];
  int usingnetscape = 0;
  int usingiexplore = 0;
  
  if (!inited) return SB_NOTINITED; // has to be inited first
  
  // find out which browser we are dealing with
  strcpy (newcommand, browser_exe);
  _strlwr (newcommand);
  if (strstr (newcommand, "netscape.exe") != NULL) { 
    // netscape
    usingnetscape = 1;
  } else if (strstr (newcommand, "iexplore.exe") != NULL) { 
    // internet explorer
    usingiexplore = 1;
  }
  
  // only try to communicate with a running browser if a startdir
  // is not specified
  if (startdir == NULL) {
    if (usingnetscape && hszNetscapeName != NULL && 
	openurl(hszNetscapeName, hszActivateTopic, hszOpenURLTopic, url)) {
      return SB_NOERROR;
    }
    if (usingiexplore && hszIExploreName != NULL && 
	openurl(hszIExploreName, hszActivateTopic, hszOpenURLTopic, url)) {
      return SB_NOERROR;
    }
  }
  
  strcpy (newcommand, browser_exe);
  
  // don't put the url on the command line for internet explorer
  if (usingiexplore) {
    strcat (newcommand, " -nohome");
    
  } else {
    // will have to put the url on the command line
    strcat (newcommand, " \"");
    strcat (newcommand, url);
    strcat (newcommand, "\"");
  }
  
  // change the working directory (if needed)
  char cwd[1024];
  cwd[0] = '\0';
  if (startdir != NULL) {
    _getcwd(cwd, 1024);
    _chdir(startdir);
  }
  
  // attempt to start the browser
  int res = WinExec(newcommand, SW_SHOW);
  
  // change the working directory back (if needed)
  if (startdir != NULL) _chdir(cwd);
  
  if (res == ERROR_BAD_FORMAT) return SB_FAIL_BADFORMAT;
  if ((res == ERROR_FILE_NOT_FOUND) || (res == ERROR_PATH_NOT_FOUND)) 
    return SB_FAIL_NOTFOUND;
  if (res <= 31) return SB_FAIL_NORESOURCES;
  
  ++numbrowserstarted;
  
  // if we aren't using iexplore then the url was
  // put on the command line and there is nothing left to do
  if (!usingiexplore) return SB_NOERROR;
  
  // connect to the browser and get it to open a page,
  // time out after 1 minute
  DWORD lastcheck = GetTickCount ();
  while (DiffTickCounts (lastcheck, GetTickCount()) <= 60000) {
    if (usingnetscape && hszNetscapeName != NULL && 
	openurl(hszNetscapeName, hszActivateTopic, hszOpenURLTopic, url)) {
      return SB_NOERROR;
    }
    if (usingiexplore && hszIExploreName != NULL && 
	openurl(hszIExploreName, hszActivateTopic, hszOpenURLTopic, url)) {
      return SB_NOERROR;
    }
  }
  
  // timed out trying to connect to the browser
  return SB_FAILCONNECTBROWSER;
}

// returns SB_NOERROR on success,
// SB_NOTINITED, SB_FAILCONNECTBROWSER on failure
int browserrunning (char *browser_exe) {
  char newcommand[SB_COMLEN];
  if (!inited) return SB_NOTINITED; // has to be inited first
  
  strcpy (newcommand, browser_exe);
  _strlwr (newcommand);
  
  if ((strstr (newcommand, "netscape.exe") != NULL) && (hszNetscapeName != NULL) && 
      tryconnect(hszNetscapeName, hszOpenURLTopic)) {
    // succeeded connecting to netscape
    return SB_NOERROR;
    
  } else if ((strstr (newcommand, "iexplore.exe") != NULL) && (hszIExploreName != NULL) && 
	     tryconnect(hszIExploreName, hszOpenURLTopic)) {
    // succeeded connecting to internet explorer
    return SB_NOERROR;
  }
  
  return SB_FAILCONNECTBROWSER;
}
