// MUSHclient.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "MUSHclient.h"
#include "doc.h"
#include "ActivityDoc.h"
#include "TextDocument.h"

#include "mainfrm.h"
#include "childfrm.h"
#include "activitychildfrm.h"
#include "textchildfrm.h"
#include "MUSHview.h"
#include "ActivityView.h"
#include "TextView.h"
#include "PaneView.h"
#include "PaneChildWnd.h"


#include "winplace.h"
#include "StatLink.h"

#include "welcome.h"
#include "welcome1.h"

#include "TipDlg.h"
#include "CreditsDlg.h"
#include "ColourPickerDlg.h"

#include "Splash.h"
#include "direct.h"
#include <Delayimp.h>   // For error handling & advanced features

COLORREF xterm_256_colours [256];

// Statically link __delayLoadHelper/__FUnloadDelayLoadedDLL
#pragma comment( lib, "Delayimp.lib")

#ifdef LUA51
  // Lua 5.1
  #pragma comment( lib, "lua5.1.lib" )
  // however we will allowed delayed load
  #pragma comment(linker, "/DelayLoad:lua5.1.dll")
#else
  // Lua 5.0
  #pragma comment( lib, "..\\lua50\\lib\\dll\\lua50.lib" )
  // however we will allowed delayed load
  #pragma comment(linker, "/DelayLoad:lua50.dll")
#endif // not Lua 5.1 

// library needed for timers
#pragma comment( lib, "winmm.lib")

// Winsock library
//#pragma comment( lib, "wsock32.lib ")
#pragma comment( lib, "ws2_32.lib ")

// zlib (compression) library
#ifdef _AFXDLL
//  #pragma comment( lib, "C:\\Source\\zlib\\dlls\\dll32\\zlib.lib")

  // Tell the linker that the zlib DLL should be delay loaded
//  #pragma comment(linker, "/DelayLoad:zlib.dll")

#else
//  #pragma comment( lib, "C:\\Source\\zlib\\dlls\\static32\\zlibstat.lib")
#endif

#ifdef _DEBUG
//#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

static TCHAR BASED_CODE szCtrlBars[] = _T("CtrlBars");

// working directory at login time

char working_dir [_MAX_PATH];

bool bWinNT;
bool bWin95;
bool bWin98;
bool bWine;

// memory state tracking
#ifdef _DEBUG
    CMemoryState oldMemState, newMemState, diffMemState;
#endif

extern tConfigurationNumericOption OptionsTable [];
extern tConfigurationAlphaOption AlphaOptionsTable [];

void LoadMapDirections (void);

void Generate256colours (void);

/////////////////////////////////////////////////////////////////////////////
// CMUSHclientApp

//IMPLEMENT_DYNCREATE(CMUSHclientApp, CWinApp)

BEGIN_MESSAGE_MAP(CMUSHclientApp, CWinApp)
	ON_COMMAND(CG_IDS_TIPOFTHEDAY, ShowTipOfTheDay)
	//{{AFX_MSG_MAP(CMUSHclientApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_GAME_MINIMISEPROGRAM, OnGameMinimiseprogram)
	ON_COMMAND(ID_CONNECTION_QUICK_CONNECT, OnConnectionQuickConnect)
	ON_COMMAND(ID_FILE_NEW, OnFileNew)
	ON_COMMAND(ID_HELP_GETTINGSTARTED, OnHelpGettingstarted)
	ON_COMMAND(ID_EDIT_COLOURPICKER, OnEditColourpicker)
	//}}AFX_MSG_MAP
	// Standard file based document commands
	ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
	// Standard print setup command
	ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

/*

  See: http://www.codeproject.com/com/mfc_autom.asp

BEGIN_DISPATCH_MAP(CMUSHclientApp, CWinApp)
  DISP_FUNCTION_ID(CMUSHclientApp, "Test", dispidTest, Test, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

static const IID IID_IMUSHclient =
  { 0xE594883F, 0x0CC4, 0x491a, { 0xA2, 0x8C, 0xB5,0x07, 0x1E, 0x53, 0xAE, 0x2C } };


BEGIN_INTERFACE_MAP(CMUSHclientApp, CWinApp)
//  INTERFACE_PART(CMUSHclientApp, IID_IMUSHclient, LocalClass)
  INTERFACE_PART(CMUSHclientApp, IID_IMUSHclient, Dispatch)
END_INTERFACE_MAP()

  */

/*
BEGIN_DISPATCH_MAP(CMUSHclientApp, CWinApp)
   //{{AFX_DISPATCH_MAP(CMUSHclientApp)
	//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

// Note: we add support for IID_IMUSHclient to support typesafe binding
//  from VBA.  This IID must match the GUID that is attached to the 
//  dispinterface in the .ODL file.

// {11DFC5E8-AD6F-11D0-8EAE-00A0247B3BFD}
static const IID IID_IMUSHclient =
{ 0x11dfc5e8, 0xad6f, 0x11d0, { 0x8e, 0xae, 0x0, 0xa0, 0x24, 0x7b, 0x3b, 0xfd } };

BEGIN_INTERFACE_MAP(CMUSHclientApp, CWinApp)
	INTERFACE_PART(CMUSHclientApp, IID_IMUSHclient, Dispatch)
END_INTERFACE_MAP()

*/

/*
BEGIN_INTERFACE_PART(LocalClass, IMUSHclient)
    STDMETHOD(GetTypeInfoCount)(UINT FAR* pctinfo);
    STDMETHOD(GetTypeInfo)(
        UINT itinfo,
        LCID lcid,
        ITypeInfo FAR* FAR* pptinfo);
    STDMETHOD(GetIDsOfNames)(
        REFIID riid,
        OLECHAR FAR* FAR* rgszNames,
        UINT cNames,
        LCID lcid,
        DISPID FAR* rgdispid);
    STDMETHOD(Invoke)(
        DISPID dispidMember,
        REFIID riid,
        LCID lcid,
        WORD wFlags,
        DISPPARAMS FAR* pdispparams,
        VARIANT FAR* pvarResult,
        EXCEPINFO FAR* pexcepinfo,
        UINT FAR* puArgErr);
    STDMETHOD(Test)(THIS);
END_INTERFACE_PART(LocalClass)


IMPLEMENT_OLECREATE(CMUSHclientApp, "MUSHclient.Application", 
                    0x14FE63AB, 0x691A, 0x11DB, 0x99, 0x8B, 0x00, 0x00, 0x8C, 0x01, 0x27, 0x85 )

  */

//#define new DEBUG_NEW 

/////////////////////////////////////////////////////////////////////////////
// CMUSHclientApp construction

CMUSHclientApp::CMUSHclientApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance

EnableAutomation();
   
//::AfxOleLockApp();

}

CMUSHclientApp::~CMUSHclientApp()
{
//::AfxOleUnlockApp();

}

/////////////////////////////////////////////////////////////////////////////
// The one and only CMUSHclientApp object

CMUSHclientApp theApp;

// This identifier was generated to be statistically unique for your app.
// You may change it if you prefer to choose a specific identifier.

// {11DFC5E6-AD6F-11D0-8EAE-00A0247B3BFD}
static const CLSID clsid =
{ 0x11dfc5e6, 0xad6f, 0x11d0, { 0x8e, 0xae, 0x0, 0xa0, 0x24, 0x7b, 0x3b, 0xfd } };

/////////////////////////////////////////////////////////////////////////////
// CMUSHclientApp initialization

BOOL CMUSHclientApp::InitInstance()
{
    char fullfilename [MAX_PATH];

    if (GetModuleFileName (NULL, fullfilename, sizeof (fullfilename)))
      m_strMUSHclientFileName = ExtractDirectory (CString (fullfilename));
    else
      m_strMUSHclientFileName = ".\\MUSHclient.exe";

// as at version 3.13
// int i = sizeof (CLine);     // 56 bytes
// int j = sizeof (CStyle);    // 20 bytes
// int k = sizeof (CAction);   // 28 bytes 

// memory state tracking
#ifdef _DEBUG
    oldMemState.Checkpoint();
#endif

   if (strstr (m_lpCmdLine, "/wine"))
     bWine = true;
   else
     bWine = false;

   LoadMapDirections ();
   Generate256colours ();

// Set the debug-heap flag so that freed blocks are kept on the
   // linked list, to catch any inadvertent use of freed memory
   SET_CRT_DEBUG_FIELD( _CRTDBG_DELAY_FREE_MEM_DF );
   SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF );
//   SET_CRT_DEBUG_FIELD( _CRTDBG_CHECK_ALWAYS_DF );
// speed warning: see:   _ASSERTE( _CrtCheckMemory( ) );
// in ProcessPreviousLine.cpp

  m_SpellChecker_Lua = NULL;

  m_bShowInitialDelay = true;
  m_TypeOfNewDocument = eNormalNewDocument;
  m_bSpellCheckOK = false;
  m_bEnableSpellCheck = false;
  m_bEchoSendToAll = true;
  m_nUniqueNumber = 0;

  // for string.gsub dialog
  m_bEachLine = true;             
  m_bEscapeSequences = false;      
  m_bCallFunction = false;         

	// Initialize OLE libraries
  if (!bWine)
    if (!AfxOleInit())        // not needed?
	  {
		  AfxMessageBox("OLE initialization failed", MB_ICONSTOP);
		  return FALSE;
	  }

  AfxInitRichEdit ();

  LARGE_INTEGER large_int_frequency;
  if (QueryPerformanceFrequency (&large_int_frequency))
    m_iCounterFrequency = large_int_frequency.QuadPart;
  else
    m_iCounterFrequency = 0;    // no performance counter

  SetRegistryKey ("Gammon Software Solutions");

	// CG: The following block was added by the Splash Screen component.

  CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);

	CSplashWnd::EnableSplashScreen(true);

  TRACE ("MUSHclient starting up ...\n");

	if (!AfxSocketInit(&m_wsadata))
	{
		AfxMessageBox(IDP_SOCKETS_INIT_FAILED, MB_ICONSTOP);
		return FALSE;
	}

	// Standard initialization

	Enable3dControls();

	LoadStdProfileSettings(10);  // Load standard INI file options (including MRU)

  if (!bWine)
    AfxEnableControlContainer ();   // not needed?

  m_pActivityDoc = NULL;
  m_pActivityView = NULL;
  m_bUpdateActivity = FALSE;


  // seed the random number generator
  time_t timer;
  time (&timer);
  srand (timer);  

  // Marsenne Twister generator
  init_genrand (timer);

	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views.

  // put this first so unknown extensions default to text (eg. mush)
  // the normal document (*.txt)

	m_pNormalDocTemplate = new CMultiDocTemplate(
		IDR_NORMALTYPE,
		RUNTIME_CLASS(CTextDocument),
		RUNTIME_CLASS(CTextChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CTextView));
	AddDocTemplate(m_pNormalDocTemplate);

  // normal worlds

	m_pWorldDocTemplate = new CMultiDocTemplate(
		IDR_MUSHCLTYPE,
		RUNTIME_CLASS(CMUSHclientDoc),
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CMUSHView));
	AddDocTemplate(m_pWorldDocTemplate);

#ifdef PANE

  // normal worlds - extra pane windows

	m_pPaneTemplate = new CMultiDocTemplate(
		IDR_PANETYPE,
		RUNTIME_CLASS(CMUSHclientDoc),
		RUNTIME_CLASS(CPaneChildWnd), // custom MDI child frame
		RUNTIME_CLASS(CPaneView));
	AddDocTemplate(m_pPaneTemplate);

#endif

  // the activity window

	m_pActivityDocTemplate = new CMultiDocTemplate(
		IDR_ACTIVITYTYPE,
		RUNTIME_CLASS(CActivityDoc),
		RUNTIME_CLASS(CActivityChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CActivityView));
	AddDocTemplate(m_pActivityDocTemplate);

  if (!bWine)
    {
	  // Connect the COleTemplateServer to the document template.
	  //  The COleTemplateServer creates new documents on behalf
	  //  of requesting OLE containers by using information
	  //  specified in the document template.
	  m_server.ConnectTemplate(clsid, m_pWorldDocTemplate, FALSE);

	  // Register all OLE server factories as running.  This enables the
	  //  OLE libraries to create objects from other applications.
	  COleTemplateServer::RegisterAll();
		  // Note: MDI applications register all server objects without regard
		  //  to the /Embedding or /Automation on the command line.
    }

// find the working directory at startup time

  _getdcwd (0, working_dir, sizeof (working_dir) - 1);

// make sure directory name ends in a slash

  working_dir [sizeof (working_dir) - 2] = 0;

  if (working_dir [strlen (working_dir) - 1] != '\\')
    strcat (working_dir, "\\");  

  // read global prefs from the Registry

  LoadGlobalsFromRegistry ();

// check for configuration name collisions

#ifdef _DEBUG
  for (int i = 0; AlphaOptionsTable [i].pName; i++)
    for (int j = 0;  OptionsTable [j].pName; j++)
      if (strcmp (AlphaOptionsTable [i].pName, OptionsTable [j].pName) == 0)
        ::AfxMessageBox (CFormat ("Internal MUSHclient error, config name collision: %s",
                         (LPCTSTR) OptionsTable [j].pName), MB_ICONEXCLAMATION);
#endif

  CWnd * extraWnd = NULL;

  // if *only* tray wanted, hide icon from task bar
  if (m_iIconPlacement == ICON_PLACEMENT_TRAY)
    {
    extraWnd = new CWnd;

    // MUSHclient icon for hidden window, so Alt+Tab will look OK
    HICON hIcon =
        (HICON)LoadImage( AfxGetResourceHandle(),
            MAKEINTRESOURCE(IDR_MUSHCLTYPE),
            IMAGE_ICON,
            GetSystemMetrics(SM_CXICON),
            GetSystemMetrics(SM_CYICON),
            LR_DEFAULTCOLOR);

    // for hiding main window from taskbar
    VERIFY(extraWnd->CreateEx 
        ( 0, AfxRegisterWndClass(CS_CLASSDC | CS_GLOBALCLASS, 0, 0, hIcon), 
        _T("MUSHclient"), WS_OVERLAPPEDWINDOW | WS_EX_TOOLWINDOW,
        0, 0, 0, 0, NULL, NULL ));

    }   // end of tray wanted

	// create main MDI Frame window
	CMainFrame* pMainFrame = new CMainFrame;
	if (!pMainFrame->LoadFrame(IDR_MAINFRAME,
    WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE  , 
      extraWnd)) // owner is hidden window
    {
    AfxMessageBox("Unable to load main frame window", MB_ICONSTOP);
		return FALSE;
    }
	m_pMainWnd = pMainFrame;

  if (m_iIconPlacement == ICON_PLACEMENT_TRAY ||
      m_iIconPlacement == ICON_PLACEMENT_BOTH)
    Frame.AddTrayIcon ();       

  // Enable DDE Execute open
	EnableShellOpen();
	RegisterShellFileTypes();

// initialise COM

  if (!bWine)
    CoInitialize (NULL);

  // see which OS we are using
  OSVERSIONINFO ver;
  memset(&ver, 0, sizeof(ver));
  ver.dwOSVersionInfoSize = sizeof(ver);
  VERIFY(::GetVersionEx(&ver));
  bWinNT = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT);
  bWin95 = (ver.dwPlatformId == 
     VER_PLATFORM_WIN32_WINDOWS) && (ver.dwMinorVersion == 0);
  bWin98 = (ver.dwPlatformId == 
     VER_PLATFORM_WIN32_WINDOWS) && (ver.dwMinorVersion > 0);

  // MXP initialisation

  MXP_LoadElements ();  // elements like <b>
  MXP_LoadEntities ();  // entities like &gt;
  MXP_LoadColours ();   // colours like blue

// show the main window

 	pMainFrame->ShowWindow(m_nCmdShow);

// get main window position from last time

  CWindowPlacement wp;
  pMainFrame->GetWindowPlacement(&wp);   // default if no registry entry

  wp.Restore ("Main window", pMainFrame, true);

  pMainFrame->LoadBarState(szCtrlBars);

  if (m_bAlwaysOnTop)
    pMainFrame->SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE);

// get finger cursor

  CStaticLink::g_hCursorLink = AfxGetApp()->LoadCursor (ID_FINGER_CURSOR);

  bool bAutoOpen = true;

	// simple command line parsing
	if (m_lpCmdLine[0] == '\0')
	{
		// create a new (empty) document
	//	OnFileNew();    // do nothing
	}
	else
	  {
    CString strTemp = m_lpCmdLine;
    strTemp.MakeLower ();

    strTemp.TrimLeft ();

    // look for /noauto command-line option
    if (strTemp == "/noauto")
      bAutoOpen = false;
    else
    if (strTemp == "/wine")
      { }  // do nothing else, checked further up
    else
    if (strstr (strTemp, ".mcl"))
		// open an existing document
		  OpenDocumentFile(m_lpCmdLine);
    else
      {
      // switch to "telnet from netscape" mode
      m_TypeOfNewDocument = eTelnetFromNetscape;
      App.m_pWorldDocTemplate->OpenDocumentFile(NULL);
      // back to normal
      m_TypeOfNewDocument = eNormalNewDocument;
      bAutoOpen = false;    // and cancel auto-open
      } // end of world and port supplied
	}

// open all worlds specified in global preferences if no shift key is down

  if ((GetKeyState (VK_LSHIFT) & 0x8000) == 0 &&
      (GetKeyState (VK_RSHIFT) & 0x8000) == 0 && 
      bAutoOpen)
    {
    vector<string> v;

    StringToVector ((const char *) m_strWorldList, v, "*");

    for (vector<string>::const_iterator i = v.begin (); i != v.end (); i++)
      m_pWorldDocTemplate->OpenDocumentFile (i->c_str ());

    }

	// The main window has been initialized, so update it.
	pMainFrame->UpdateWindow();

	// Enable drag/drop open
	m_pMainWnd->DragAcceptFiles();

	// When a server application is launched stand-alone, it is a good idea
	//  to update the system registry in case it has been damaged.

  if (!bWine)
    {  
    m_server.UpdateRegistry(OAT_DISPATCH_OBJECT);
	  COleObjectFactory::UpdateRegistryAll();
    }

// Show first welcome

  BOOL firsttime = GetProfileInt ("Miscellaneous", "First time", 1);	
  UINT version = 0;

  if (firsttime)
    {
    CWelcomeDlg dlg;

    dlg.m_strMessage.Format ("I notice that this is the first time you have used"
                         " MUSHclient on this PC.");

    dlg.DoModal ();

    WriteProfileInt ("Miscellaneous", "First time", 0);	

    }   // end of first time

  else   // not the first time they have used this program
    
    {

    version = GetProfileInt ("Miscellaneous", "Version", 0);	

    if (version < THISVERSION)  // THISVERSION is defined at start of this module
      {

      CWelcome1Dlg dlg;         // Welcome to this version dialog

      dlg.m_strMessage1.Format ("Welcome to MUSHclient, version %s", MUSHCLIENT_VERSION);
      dlg.m_strMessage2.Format ("Thank you for upgrading MUSHclient to version %s",
                                MUSHCLIENT_VERSION);
  
      dlg.DoModal ();

      } // end of having lower version than THISVERSION
    
    } // end of not first time


// Write out the new version number if necessary

  if (version != THISVERSION)
    WriteProfileInt ("Miscellaneous", "Version", THISVERSION);	

// Find which version of Windows we are using.

OSVERSIONINFO VersionInformation;

  VersionInformation.dwOSVersionInfoSize = sizeof (VersionInformation);

  GetVersionEx(&VersionInformation);

  platform = VersionInformation.dwPlatformId;

// open activity window if wanted

  if (m_bOpenActivityWindow)
    App.m_pActivityDocTemplate->OpenDocumentFile(NULL);

// activate first world, if any (so activity world doesn't have the focus)

  pMainFrame->SendMessage (WM_COMMAND, ID_WORLDS_WORLD1, 0);

  if (firsttime && !bWine)
    App.WinHelp(ID_GETTING_STARTED + HID_BASE_COMMAND);	

  // enable spell checker, if wanted in prefs
  if (m_bEnableSpellCheck)
    InitSpellCheck ();

  App.ShowTipAtStartup();

	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
  protected:

 	// static controls with hyperlinks
	CStaticLink	m_EmailLink;
	CStaticLink	m_WebLink;
	CStaticLink	m_ChangesLink;
	CStaticLink	m_RegcodeLink;

public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	CString	m_strVersion;
	CString	m_strEmail;
	CString	m_strWebAddress;
	CString	m_strChangeHistoryAddress;
	//}}AFX_DATA

// Implementation
protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//{{AFX_MSG(CAboutDlg)
	virtual BOOL OnInitDialog();
	afx_msg void OnCredits();
	afx_msg void OnLicense();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};


CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	m_strVersion = _T("");
	m_strEmail = _T("");
	m_strWebAddress = _T("");
	m_strChangeHistoryAddress = _T("");
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	DDX_Text(pDX, IDC_VERSION_LABEL, m_strVersion);
	DDX_Text(pDX, IDC_EMAIL_ADDRESS, m_strEmail);
	DDX_Text(pDX, IDC_WEB_ADDRESS, m_strWebAddress);
	DDX_Text(pDX, IDC_CHANGES_ADDRESS, m_strChangeHistoryAddress);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	ON_BN_CLICKED(IDC_CREDITS, OnCredits)
	ON_BN_CLICKED(IDC_LICENSE, OnLicense)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CMUSHclientApp::OnAppAbout()
{
	static CAboutDlg aboutDlg;

  aboutDlg.m_strVersion = "Version ";
  aboutDlg.m_strVersion += MUSHCLIENT_VERSION;

  aboutDlg.m_strWebAddress = MY_WEB_PAGE;
  aboutDlg.m_strChangeHistoryAddress = CHANGES_WEB_PAGE;

  aboutDlg.DoModal();
}


BOOL CAboutDlg::OnInitDialog() 
{
	
	// subclass static controls.
	m_WebLink.SubclassDlgItem(IDC_WEB_ADDRESS, this);
	m_ChangesLink.SubclassDlgItem(IDC_CHANGES_ADDRESS,  this);
	m_RegcodeLink.SubclassDlgItem(IDC_REGCODE,  this);
	

	return CDialog::OnInitDialog();

 }

/////////////////////////////////////////////////////////////////////////////
// CMUSHclientApp commands

extern int gdoccount;

BOOL CMUSHclientApp::SaveAllModified() 
{

// warn them, if they have sessions open.
  
  if (gdoccount > 0 && App.m_bConfirmBeforeClosingMushclient)
    if (::AfxMessageBox ("This will end your MUSHclient session.", 
          MB_OKCANCEL | MB_ICONINFORMATION)
          == IDCANCEL)
      return 0;
	
	return CWinApp::SaveAllModified();
}

BOOL CMUSHclientApp::OnIdle(LONG lCount) 
{
	
	if (CWinApp::OnIdle(lCount))
    return 1;

CWnd* wnd = Frame.GetForegroundWindow( );

  if (!wnd)
    return 0;

// update activity window if required

  if (m_bUpdateActivity)
    {
    if (Frame.m_wndMDITabs.InUse ())
      Frame.m_wndMDITabs.Update ();

    if (m_pActivityDoc && 
        m_nActivityWindowRefreshType != eRefreshPeriodically)
          m_pActivityDoc->UpdateAllViews (NULL);
    else
      App.UpdateWorldCounts ();
    }

// See if the front window is our main frame

	if (wnd->IsKindOf(RUNTIME_CLASS(CMainFrame)))
    {
    CMainFrame * frame = (CMainFrame *) wnd;

// find the active view

    CFrameWnd*  pFrame = frame->GetActiveFrame( );

// see if the active view is a MUSHview (output window)

    if (!pFrame)
      return 0;
                
  	if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
    	{

      CChildFrame * pmyFrame = (CChildFrame *) pFrame;

  		CMUSHView * pmyView = pmyFrame->m_topview;

      if (!pmyView)
        return 0;

// if so, see if mouse is still down

  		return pmyView->mouse_still_down ();
  	  }	

    }

  
  return 0;   // no more idle processing

}

void CMUSHclientApp::OnGameMinimiseprogram() 
{
  ASSERT(AfxGetMainWnd () != NULL);
  AfxGetMainWnd ()->ShowWindow(SW_MINIMIZE);		
}

BOOL CMUSHclientApp::PreTranslateMessage(MSG* pMsg)
{
	// CG: The following lines were added by the Splash Screen component.
	if (CSplashWnd::PreTranslateAppMessage(pMsg))
		return TRUE;

	return CWinApp::PreTranslateMessage(pMsg);
}

void CMUSHclientApp::ShowTipAtStartup(void)
{
	// CG: This function added by 'Tip of the Day' component.

	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);
	if (cmdInfo.m_bShowSplash)
	{
		CTipDlg dlg;
		if (dlg.m_bStartup)
			dlg.DoModal();
	}

}

void CMUSHclientApp::ShowTipOfTheDay(void)
{
	// CG: This function added by 'Tip of the Day' component.

	CTipDlg dlg;
	dlg.DoModal();

}


// saves column width and location for a generic list control

void CMUSHclientApp::SaveColumnConfiguration (LPCTSTR strName, 
                                              const int iColCount,
                                              CListCtrl& ctlList,
                                              const int iLastColumn,
                                              const BOOL bReverse)
  {

int * iColOrder;

  iColOrder = new int [iColCount];

  if (!iColOrder)
    return;

// set up default order in case our SendMessage doesn't work

  for (int i = 0; i < iColCount; i++)
    iColOrder [i] = i;

// find column sequence

  ctlList.SendMessage (LVM_GETCOLUMNORDERARRAY, iColCount, (DWORD) iColOrder);

// save column sequence and column width

  CString strTitle;

  for (int nCol = 0; nCol < iColCount; nCol++)
    {
    strTitle.Format ("%s Col %i Width", strName, nCol);
    App.WriteProfileInt ("World Configuration", strTitle, 
                          ctlList.GetColumnWidth (nCol));	

    strTitle.Format ("%s Col %i Order", strName, nCol);
    App.WriteProfileInt ("World Configuration", strTitle, 
                        iColOrder [nCol]);	
    } // end of doing each column

  delete [] iColOrder;

  // what column they sorted on
  strTitle.Format ("%s Sort Sequence", strName);
  App.WriteProfileInt ("World Configuration", strTitle, iLastColumn);	

  // was it in reverse?
  strTitle.Format ("%s Sort Reverse", strName);
  App.WriteProfileInt ("World Configuration", strTitle, bReverse);	


  } // end of SaveColumnConfiguration

void CMUSHclientApp::RestoreColumnConfiguration (LPCTSTR strName, 
                                                 const int iColCount,
                                                 CListCtrl& ctlList,
                                                 int iColOrder [],
                                                 int iColWidth [],
                                                 int & iLastColumn,
                                                 BOOL & bReverse)
  {

// set list control to display whole row when selected and to allow column drag/drop


  ctlList.SendMessage (LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 
                        ctlList.SendMessage (LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0) 
                        | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP 
                        | (App.m_bShowGridLinesInListViews ? LVS_EX_GRIDLINES : 0) );

// Restore widths and positions of each column

  CString strTitle;

// restore column sequence and column width

  for (int nCol = 0; nCol < iColCount; nCol++)
    {
    strTitle.Format ("%s Col %i Width", strName, nCol);
    iColWidth [nCol] = App.GetProfileInt ("World Configuration", strTitle, iColWidth [nCol]);
    strTitle.Format ("%s Col %i Order", strName, nCol);
    iColOrder [nCol] = App.GetProfileInt ("World Configuration", strTitle, iColOrder [nCol]);

    if (iColWidth [nCol] == 0)
      iColWidth [nCol] = 10;   // just in case

    ctlList.SetColumnWidth (nCol, iColWidth [nCol]);
    } // end of doing each column

  // what column they sorted on
  strTitle.Format ("%s Sort Sequence", strName);
  iLastColumn = App.GetProfileInt ("World Configuration", strTitle, iLastColumn);

  // was it in reverse?
  strTitle.Format ("%s Sort Reverse", strName);
  bReverse = App.GetProfileInt ("World Configuration", strTitle, bReverse);	

  } // end of RestoreColumnConfiguration

int CMUSHclientApp::ExitInstance() 
{
  if (!bWine)
    CoUninitialize ();
	

// delete Element map

  DELETE_MAP (m_ElementMap, CAtomicElement); 


// delete Colours map

  DELETE_MAP (m_ColoursMap, CColours); 


  if (m_SpellChecker_Lua)
    lua_close (m_SpellChecker_Lua);
  m_SpellChecker_Lua = NULL;

	return CWinApp::ExitInstance();
}


void CMUSHclientApp::UpdateWorldCounts(void) 
  {
POSITION pos;
int nDocCount;


  App.m_bUpdateActivity = FALSE;

  App.m_timeLastActivityUpdate = CTime::GetCurrentTime();

// first, count worlds

 	pos = App.m_pWorldDocTemplate->GetFirstDocPosition();
  for (nDocCount = 0; pos; nDocCount++)
    {
    CMUSHclientDoc* pDoc = (CMUSHclientDoc*) App.m_pWorldDocTemplate->GetNextDoc(pos);
    pDoc->m_view_number = nDocCount + 1;    // so we can use Ctrl+1 etc.
    }

  }   // end of UpdateWorldCounts


void CAboutDlg::OnCredits() 
{
CCreditsDlg dlg;

  dlg.m_iResourceID = IDR_CREDITS;
  dlg.m_strTitle = "Credits";

  dlg.DoModal ();	
	
}

void CMUSHclientApp::OnConnectionQuickConnect() 
{
  // switch to quick connect
  m_TypeOfNewDocument = eQuickConnect;
  App.m_pWorldDocTemplate->OpenDocumentFile(NULL);
  // back to normal
  m_TypeOfNewDocument = eNormalNewDocument;
}

void CMUSHclientApp::OnFileNew() 
{
  m_TypeOfNewDocument = eNormalNewDocument;
  App.m_pWorldDocTemplate->OpenDocumentFile(NULL);
}

void CMUSHclientApp::OnHelpGettingstarted() 
{
App.WinHelp(ID_GETTING_STARTED + HID_BASE_COMMAND);	
}

bool GetSelection (CEdit * pEdit, CString & strSelection)
  {
int nStartChar,
    nEndChar;

  // find the selection range
  pEdit->GetSel(nStartChar, nEndChar);
  // get window text
  pEdit->GetWindowText (strSelection);

  // get selection unless no selection in which case take all of it
  if (nEndChar > nStartChar)
    {
    strSelection = strSelection.Mid (nStartChar, nEndChar - nStartChar);
    return false;
    }

  return true;    // all was selected

  } // end of GetSelection


bool CMUSHclientApp::SpellCheck (CWnd * pWnd, CEdit * pEdit)
  {
  if (!m_bSpellCheckOK)
    return true;

  if (m_SpellChecker_Lua)
    {

    lua_settop(m_SpellChecker_Lua, 0);   // clear stack

    lua_getglobal (m_SpellChecker_Lua, SPELLCHECKFUNCTION);  
    if (!lua_isfunction (m_SpellChecker_Lua, -1))
      return true;  // assume ok, what can we do?

    CString strText;
    bool bAll = GetSelection (pEdit, strText);

    lua_pushstring (m_SpellChecker_Lua, strText);  // string to be checked
    lua_pushboolean (m_SpellChecker_Lua, bAll);    // doing all?

    int narg = lua_gettop(m_SpellChecker_Lua) - 1;  // all but the function
    int error = CallLuaWithTraceBack (m_SpellChecker_Lua, narg, 1);
    
    if (error)
      {
      LuaError (m_SpellChecker_Lua, "Run-time error", SPELLCHECKFUNCTION, "Command-line spell-check");
      lua_close (m_SpellChecker_Lua);
      m_SpellChecker_Lua = NULL;
      m_bSpellCheckOK = false;
      return true;    
      }  

    if (lua_isstring (m_SpellChecker_Lua, -1))
      {
      const char * p = lua_tostring (m_SpellChecker_Lua, -1);
      if (bAll)
        pEdit->SetSel (0, -1, TRUE);
      pEdit->ReplaceSel (p, true);
      return true;
      }

    return false;
    }



  return true;

     
  }


LONG WINAPI DelayLoadDllExceptionFilter(PEXCEPTION_POINTERS pep) {
   // Assume we recognize this exception
   LONG lDisposition = EXCEPTION_EXECUTE_HANDLER;  
   // If this is a Delay-load problem, ExceptionInformation[0] points 
   // to a DelayLoadInfo structure that has detailed error info
   PDelayLoadInfo pdli = PDelayLoadInfo(pep->ExceptionRecord
      ->ExceptionInformation[0]);
   // Create a buffer where we construct error messages
   char sz[500] = { 0 };
   switch (pep->ExceptionRecord->ExceptionCode) {
   case VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND):
      // The DLL module was not found at runtime
      wsprintf(sz, "The DLL \"%s\" was not found\n\n"
                   "Please ensure it is in your path, or disable the spell checker.", pdli->szDll); 
      break;
   case VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND):
      // The DLL module was found but it doesn’t contain the function
      if (pdli->dlp.fImportByName) {
         wsprintf(sz, "Function %s was not found in %s",
            pdli->dlp.szProcName, pdli->szDll);
      } else {
         wsprintf(sz, "Function ordinal %d was not found in %s",
            pdli->dlp.dwOrdinal, pdli->szDll);
      }
      break; 
   default:
      lDisposition = EXCEPTION_CONTINUE_SEARCH;  // We don’t recognize this                                                     // exception
      break;
   }
   if (lDisposition == EXCEPTION_EXECUTE_HANDLER) {
      // We recognized this error and constructed a message, show it
      MessageBox(NULL, sz, NULL, MB_OK);
   }
   return(lDisposition);
}     // end of DelayLoadDllExceptionFilter


extern const struct luaL_reg *ptr_xmllib;

extern "C"
  {
  LUALIB_API int luaopen_rex(lua_State *L);
  LUALIB_API int luaopen_bits(lua_State *L);
  LUALIB_API int luaopen_compress(lua_State *L);
  }

LUALIB_API int luaopen_progress_dialog(lua_State *L);

void CMUSHclientApp::OpenLuaDelayed ()
  {
  if (m_SpellChecker_Lua)
    lua_close (m_SpellChecker_Lua);   // close previous one

  m_SpellChecker_Lua = MakeLuaState();   /* opens Lua */
  if (!m_SpellChecker_Lua)
    return;         // can't open Lua

  luaL_openlibs (m_SpellChecker_Lua);    // new way of opening all libraries
  luaopen_rex (m_SpellChecker_Lua);      // regular expression library
  luaopen_bits (m_SpellChecker_Lua);     // bit manipulation library
  luaopen_compress (m_SpellChecker_Lua); // compression (utils) library
  luaopen_progress_dialog (m_SpellChecker_Lua);    // progress dialog

  // add xml reader to utils lib
  luaL_register (m_SpellChecker_Lua, "utils", ptr_xmllib);

  lua_settop(m_SpellChecker_Lua, 0);   // clear stack

  // unless they explicitly enable it, remove ability to load DLLs
  DisableDLLs (m_SpellChecker_Lua);

  lua_settop(m_SpellChecker_Lua, 0);   // clear stack

   // spell checker folder pathname
  CString luaspellcheckerpath =  ExtractDirectory (m_strMUSHclientFileName);
  luaspellcheckerpath += "spellchecker.lua";

  int error = luaL_loadfile(m_SpellChecker_Lua, luaspellcheckerpath) ||
        lua_pcall (m_SpellChecker_Lua, 0, 0, 0);
  if (error)
      {
      LuaError (m_SpellChecker_Lua);
      lua_close (m_SpellChecker_Lua);
      m_SpellChecker_Lua = NULL;
      return;   // don't press on,  m_SpellChecker_Lua is NULL now.
      }  

  lua_settop(m_SpellChecker_Lua, 0);   // clear stack

  lua_getglobal (m_SpellChecker_Lua, SPELLCHECKFUNCTION);  
  if (!lua_isfunction (m_SpellChecker_Lua, -1))
    {
    ::AfxMessageBox (CFormat ("Function '%s' not in spellchecker.lua file",
                     SPELLCHECKFUNCTION),  MB_OK  | MB_ICONSTOP);
    lua_close (m_SpellChecker_Lua);
    m_SpellChecker_Lua = NULL;
    }

  } // end of OpenLuaDelayed

extern LONG WINAPI DelayLoadDllExceptionFilterLua(PEXCEPTION_POINTERS pep);


void CMUSHclientApp::InitSpellCheck (void)
  {

   // Wrap all calls to delay-load DLL functions inside SEH
   __try 
     {

     OpenLuaDelayed ();
     if (m_SpellChecker_Lua)
       m_bSpellCheckOK = true;
     else
       m_bSpellCheckOK = false; 


     }
   __except (DelayLoadDllExceptionFilter(GetExceptionInformation())) 
     {
      // Nothing to do in here, thread continues to run normally
     m_bSpellCheckOK = false;
     m_SpellChecker_Lua = NULL;
     }


  }   // end of InitSpellCheck


// have to put error message here because of __try block
void zLibError (const int izError, z_stream & m_zCompress)
  {

  if (m_zCompress.msg)
    ::AfxMessageBox (CFormat ("Could not initialise zlib decompression engine: %s",
                      m_zCompress.msg), MB_ICONEXCLAMATION);
  else
    ::AfxMessageBox (CFormat ("Could not initialise zlib decompression engine: %i",
                      izError), MB_ICONEXCLAMATION);

  }  // end of zLibError


// initialise zlib - allow for delay-load DLL not loading
int InitZlib (z_stream & m_zCompress)
  {
bool bCompressInitOK = false; 

   // Wrap all calls to delay-load DLL functions inside SEH
   __try 
     {
      int izError;
      if ((izError = inflateInit (&m_zCompress)) == Z_OK)
        bCompressInitOK = true; 
      else
        zLibError (izError, m_zCompress);
     }    // endof __try
   __except (DelayLoadDllExceptionFilter(GetExceptionInformation())) 
     {
      // Nothing to do in here, thread continues to run normally
     }

 return  bCompressInitOK;
 }   // end of InitZlib


CString CMUSHclientApp::GetUniqueString (void)
  {
  CString strNumber;
  char * p = strNumber.GetBuffer (32);
  // Format does not work with I64 as at this compiler release (VC++ 5.0)
  sprintf (p, "%I64d", GetUniqueNumber ());
  strNumber.ReleaseBuffer (-1);
  return strNumber;
  }   // end of CMUSHclientApp::GetUniqueString


void CAboutDlg::OnLicense() 
{
CCreditsDlg dlg;

  dlg.m_iResourceID = IDR_LICENSE;
  dlg.m_strTitle = "License Agreement";

  dlg.DoModal ();	
	
}

void CMUSHclientApp::OnEditColourpicker() 
{
CColourPickerDlg dlg;

  if (dlg.DoModal () != IDOK)
    return;

//  putontoclipboard (dlg.m_strMXPname);
  

}

        /*
STDMETHODIMP_(ULONG) CMUSHclientApp::XLocalClass::AddRef()
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CMUSHclientApp::XLocalClass::Release()
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  return pThis->ExternalRelease();
}
STDMETHODIMP CMUSHclientApp::XLocalClass::QueryInterface(
  REFIID iid, LPVOID* ppvObj)
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  return pThis->ExternalQueryInterface(&iid, ppvObj);
}
STDMETHODIMP CMUSHclientApp::XLocalClass::GetTypeInfoCount(
    UINT FAR* pctinfo)
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
  ASSERT(lpDispatch != NULL);
  return lpDispatch->GetTypeInfoCount(pctinfo);
}
STDMETHODIMP CMUSHclientApp::XLocalClass::GetTypeInfo(
  UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
  ASSERT(lpDispatch != NULL);
  return lpDispatch->GetTypeInfo(itinfo, lcid, pptinfo);
}
STDMETHODIMP CMUSHclientApp::XLocalClass::GetIDsOfNames(
  REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames,
  LCID lcid, DISPID FAR* rgdispid) 
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
  ASSERT(lpDispatch != NULL);
  return lpDispatch->GetIDsOfNames(riid, rgszNames, cNames, 
    lcid, rgdispid);
}
STDMETHODIMP CMUSHclientApp::XLocalClass::Invoke(
  DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult,
  EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr)
{
  METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
  LPDISPATCH lpDispatch = pThis->GetIDispatch(FALSE);
  ASSERT(lpDispatch != NULL);
  return lpDispatch->Invoke(dispidMember, riid, lcid,
    wFlags, pdispparams, pvarResult,
    pexcepinfo, puArgErr);
}

STDMETHODIMP CMUSHclientApp::XLocalClass::ShowWindow()
{
    METHOD_PROLOGUE(CMUSHclientApp, LocalClass)
    pThis->ShowWindow();
    return TRUE;
}

  */


void Generate256colours (void)
  {
  int red, green, blue;
//  const BYTE values [6] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF };

  xterm_256_colours   [0] = RGB (0, 0, 0);         // (black)       
  xterm_256_colours   [1] = RGB (128, 0, 0);       // (maroon)      
  xterm_256_colours   [2] = RGB (0, 128, 0);       // (green)       
  xterm_256_colours   [3] = RGB (128, 128, 0);     // (olive)       
  xterm_256_colours   [4] = RGB (0, 0, 128);       // (navy)        
  xterm_256_colours   [5] = RGB (128, 0, 128);     // (purple)      
  xterm_256_colours   [6] = RGB (0, 128, 128);     // (teal)        
  xterm_256_colours   [7] = RGB (192, 192, 192);   // (silver)      
                       
  xterm_256_colours   [8]  = RGB (128, 128, 128);  // (gray)                 
  xterm_256_colours   [9]  = RGB (255, 0, 0);      // (red)               
  xterm_256_colours   [10] = RGB (0, 255, 0);      // (lime)              
  xterm_256_colours   [11] = RGB (255, 255, 0);    // (yellow)            
  xterm_256_colours   [12] = RGB (0, 0, 255);      // (blue)              
  xterm_256_colours   [13] = RGB (255, 0, 255);    // (magenta)  
  xterm_256_colours   [14] = RGB (0, 255, 255);    // (cyan)        
  xterm_256_colours   [15] = RGB (255, 255, 255);  // (white)             

  /*
  for (red = 0; red < 6; red++)
    for (green = 0; green < 6; green++)
      for (blue = 0; blue < 6; blue++)
         xterm_256_colours [16 + (red * 36) + (blue * 6) + green] =
            RGB (values [red], values [blue], values [green]);
  */

// alternate algorithm - gives even 6x6x6 colour cube
//  there are 6 colours in the cube, but only 5 gaps:
//   Thus the colours will be: 0x00 / 0x33 / 0x66 / 0x99 / 0xCC / 0xFF

const BYTE colour_increment = 255 / 5;     // that is, 51 (0x33)

  for (red = 0; red < 6; red++)
      for (green = 0; green < 6; green++)
        for (blue = 0; blue < 6; blue++)
           xterm_256_colours [16 + (red * 36) + (blue * 6) + green] =
              RGB (red   * colour_increment, 
                   blue  * colour_increment, 
                   green * colour_increment);
  

  int grey;
  for (grey = 0; grey < 24; grey++)
    {
    BYTE value = 8 + (grey * 10);
    xterm_256_colours [232 + grey] = RGB (value, value, value);
    }

  } // end of Generate256colours


/* 

  Colour cube printer in Lua:

for i = 0, 255 do
  if i == 16 then
    print ""
  end -- if

  Tell (string.format (" %3i: ", i))
  ColourTell ("black", RGBColourToName (extended_colours [i]), "    ")

  if i < 16 then
    if (i % 8) == 7 then
      print ""
    end -- if 
  else
    if ((i - 16) % 6) == 5 then
      print ""
    end -- if 
  end -- if

  if i > 16 then

    if ((i - 16) % 36) == 35 then
      print ""
    end -- if
  end -- if > 16

end -- for

*/

