// ScolInterface.cpp: implementation of the ScolInterface class.
//
//////////////////////////////////////////////////////////////////////

#include "ScolInterface.h"
#include <stdio.h>
#include <string>
#include <tchar.h>

//////////////////////////////////////////////////////////////////////
// Default Download URL
char DefDownloadURL[]="http://www.scolring.org/rsc/scol_plugin.exe";

//////////////////////////////////////////////////////////////////////
// Default Download Size
long DefDownloadSize=53625758;

char *str_tolower(char *string) {
  char *cp;
  /* change each character to it's lower case pedant */
  for (cp=string; *cp; cp++) *cp = (char) tolower(*cp);
  return(string);
}


int h2i(const char* s)
// Converts a string into an integer. The strings is expected
// to contain a non-empty sequence of hexadecimal digits
{
  int res = 0;
  if (!s || *s == '\0')
    return false;
  int n = 0;
  while (*s) {
    int d;
    if (isdigit(*s))
      d = *s-'0';
    else if (*s >= 'A' && *s <= 'F')
      d = 10+(*s-'A');
    else if (*s >= 'a' && *s <= 'f')
      d = 10+(*s-'a');
    else break;
    n = (n<<4)+d;
    s++;
  }
  res = n;
  return res;
}

HINSTANCE GetDllInstance()
{
    MEMORY_BASIC_INFORMATION mbi = {0};
    if( VirtualQuery(GetDllInstance, &mbi, sizeof(mbi)) )
    {
        return (HINSTANCE)mbi.AllocationBase;
    }

    return NULL;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ScolInterface::ScolInterface()
{
	//////////////////////////////
	// init 
	//
	m_boolForceInstall = FALSE;
	m_strScolVersionNeeded[0] = NULL;
  m_boolScolStartSuccess = FALSE;
  m_scolprocess = NULL;

	// Default Download URL
	strcpy(m_strDownloadURL,DefDownloadURL);

	// Default Download Size
	m_longDownloadSize = DefDownloadSize;

  m_axwin=NULL;

	// Get the scol file path and test version :
	m_boolScolLoadSuccess = GetScolExe();
}

ScolInterface::~ScolInterface()
{
  // Shut Down Scol if not already Done
  CloseScolMachine();
}

void ScolInterface::CloseScolMachine()
{
  if (m_boolScolStartSuccess)
  {
    if (m_hscol != NULL)
      SendMessage(m_hscol, WM_DESTROY, 0, 0);

    m_axwin = NULL;
    m_hscol = NULL;
    m_boolScolStartSuccess=FALSE;

    //Clear Message Stack
    SkipMessages();

    if(m_scolprocess != NULL)
    {
      DWORD dwExitCode = 0;
      GetExitCodeProcess(m_scolprocess, &dwExitCode);
      if(dwExitCode == STILL_ACTIVE)
      {
        TerminateProcess(m_scolprocess, 0);    
      }
    }
  }
}

void ScolInterface::ScolClosed()
{
}

void ScolInterface::ForceInstall()
{
	m_boolForceInstall=true;
}
/////////////////////////////////////////////////////////////////////
// Write Accessor for m_strDownloadURL member var
void ScolInterface::set_DownloadURL(char *DWURL)
{
	sprintf(m_strDownloadURL,"%s",DWURL);
}

/////////////////////////////////////////////////////////////////////
// Write Accessor for m_longDownloadSize member var
void ScolInterface::set_DownloadSize(long Size)
{
	m_longDownloadSize = Size;
}

/////////////////////////////////////////////////////////////////////
// Write Accessor for m_strScolVersionNeeded member var
void ScolInterface::set_ScolVersionNeeded(char *SVersion)
{
	sprintf(m_strScolVersionNeeded,"%s",SVersion);
}

/////////////////////////////////////////////////////////////////////
// Read Accessor for m_strDownloadURL member var
char* ScolInterface::get_DownloadURL()
{
	return m_strDownloadURL;
}

/////////////////////////////////////////////////////////////////////
// Read Accessor for m_longDownloadSize member var
long ScolInterface::get_DownloadSize()
{
	return m_longDownloadSize;
}

/////////////////////////////////////////////////////////////////////
// Read Accessor for m_strScolVersionNeeded member var
char* ScolInterface::get_ScolVersionNeeded()
{
	return m_strScolVersionNeeded;
}

HWND ScolInterface::get_AxWindow()
{
  return m_axwin;
}

HWND ScolInterface::get_ScolHWindow()
{
  return m_hscol;
}

void ScolInterface::set_ScolHWindow(HWND win)
{
  m_hscol = win;
}

////////////////////////////////////////////////////////////////////
// Search for Scol Dll, Load it, and get Entry points
// Return true if everything is OK
// Return false if Dll is not found or LoadLibrary failed
bool ScolInterface::GetScolExe()
{
	char version[1024];

  long msize=1024;
  int i;


  ///////////////////////////////////////
  // Look for Scol Dll Path
  // New Detection method
  GetModuleFileName(GetDllInstance(), m_scolpath, MAX_PATH);
  
  //correct slashes
  i=strlen(m_scolpath);
  while (i && (m_scolpath[i]!='\\') && (m_scolpath[i]!='/')) i--;

  if (m_scolpath[i]=='\\' || m_scolpath[i]=='/') 
  {
    m_scolpath[i+1]=0;
  }

  if (!getScolDllName())
    return false;  
  
  if (!getScolProtoName())
    return false;
  
  char regkey[MAX_PATH];
  sprintf(regkey, "%s\\shell\\open\\command", m_scolproto);
  
  if (RegQueryValue(HKEY_CLASSES_ROOT, regkey, m_scolexe, &msize)!=ERROR_SUCCESS)
  {
    if (RegQueryValue(HKEY_CLASSES_ROOT, "scol_file\\shell\\open\\command", m_scolexe, &msize)!=ERROR_SUCCESS)
    {
      if (RegQueryValue(HKEY_CLASSES_ROOT, "scm_auto_file\\shell\\open\\command", m_scolexe, &msize)!=ERROR_SUCCESS)
		    return false;
    }
  }
  
  //$BB sub the first '"'
  if (m_scolexe[0]=='"') 
    sprintf(m_scolexe, "%s", (m_scolexe+1));
  
  i = 0;
  while (((i <= msize) && (m_scolexe[i]!='\"'))) i++;
  m_scolexe[i] = 0;  

  if (m_strScolVersionNeeded[0] != NULL)
	{
		if (!getScolVersion (version)) 
		{
			//::MessageBox(NULL,"Couldn't find the scol vesion. (may be a missing usm.ini)","Error...",0);
			return false;
		}
		
		if ((h2i(version)) < (h2i(m_strScolVersionNeeded)))
		{
			char * dlg;
			sprintf (dlg, "You need to update your scol voyager. cur %i -> %i", (h2i(version)), (h2i(m_strScolVersionNeeded)));
			//::MessageBox(NULL,dlg,"Error...",0);
			return false;
		}
	}

  return true;
}


/////////////////////////////////////////////////////////////////////
// Search for default Scol partition
// Return true if everything is OK
// Return false if not found
bool ScolInterface::getScolPartition (char* path, char* dir)
{
  char buf[1024];
  FILE* ini;
  bool cont=false;
  char line[1024],*p;

  strcpy (buf,path);
  strcat (buf,"usm.ini");
  ini=fopen(buf,"rt");
  if (!ini) return false;
  
  while (!feof(ini))
  {
    fgets(line,1024,ini);
    p=line;
    while (*p==32 || *p=='\t') p++;
    if (!strnicmp(p,"diska ./",8))
    {
      //fclose(ini);
      p+=8;
      while (*p==32) p++;
      if (*p)
      {
        strcpy(dir,p);
        p=dir;
        p+=strlen(dir);
        while (*p!=32 && p>dir) p--;
        if (*(p)==32) *(p)=0;
      }
    }
  }
  fclose(ini);
  if (!dir[0])
    return false;
  else
    return true;
}


/////////////////////////////////////////////////////////////////////
// Search for Scol Version file in the specified path
// Return true if everything is OK
// Return false if file not found
bool ScolInterface::getScolVersion (char* version)
{
	char path[MAX_PATH];
  char dir[MAX_PATH];
  
  sprintf(path, "%s", m_scolpath);
	if (!getScolPartition(path, dir))
	{
		return false;
	}
  
	strcat(path,dir);
  
  char buf[1024];
  FILE* ini;
  bool cont=false;
  char line[1024],*p;

  strcpy (buf,path);
  strcat (buf,"\\locked\\etc\\version.txt");

  ini=fopen(buf,"rt");
  if (!ini) return false;
  
  while (!feof(ini))
  {
    fgets(line,1024,ini);
    p=line;
    while (*p==32 || *p=='\t') p++;
    if (!strnicmp(p,"version ",8))
    {
      fclose(ini);
      p+=8;
      while (*p==32) p++;
      if (!*p)
				return false;
      strcpy(version,p);
      p=version;
      p+=strlen(version);
			while (*p!=32 && p>version) p--;

      return true;
    }
  }
  fclose(ini);
  return false;
}


/////////////////////////////////////////////////////////////////////
// Search for Scol Dll in the specified path
// Return true if everything is OK
// Return false if Dll is not found 
bool ScolInterface::getScolDllName ()
{
  char buf[MAX_PATH];
  char name[MAX_PATH];
  FILE* ini;
  bool cont=false;
  char line[1024],*p;

  strcpy (buf,m_scolpath);
  strcat (buf,"usm.ini");
  ini=fopen(buf,"rt");
  if (!ini) return false;
  
  while (!feof(ini))
  {
    fgets(line,1024,ini);
    p=line;
    while (*p==32 || *p=='\t') p++;
    if (!strnicmp(p,"scol ",5))
    {
      fclose(ini);
      p+=5;
      while (*p==32) p++;
      if (!*p)
        return false;
      strcpy(name,p);
      p=name;
      p+=strlen(name);
      while (*p!=32 && p>name) p--;
      if (*(p)==32)
        *(p)=0;

      sprintf(m_scoldll, "%s%s", m_scolpath, name);
      return true;
    }
  }
  fclose(ini);
  return false;
}


/////////////////////////////////////////////////////////////////////
// Search for Scol Prototype in the specified path
// Return true if everything is OK
// Return false if Dll is not found 
bool ScolInterface::getScolProtoName ()
{
  std::string filepath(m_scolpath);
  FILE* ini;

  char line[1024];
  
  filepath.append("usm.ini");

  ini=fopen(filepath.c_str(), "rt");
  if (!ini) return false;
  
  while (!feof(ini))
  {
    fgets(line, 1024, ini);
    std::string lineAsStr(line);
    
    // remove carriage return
    lineAsStr = lineAsStr.erase(lineAsStr.find_last_not_of(" \t\r\n")+1); // trim right

    size_t foundEnd;
    size_t found = lineAsStr.find("proto ");
    if (found!=std::string::npos)
    {
      fclose(ini);
      lineAsStr = lineAsStr.substr(found + 6);
      found    = lineAsStr.find_first_not_of(" ");
      foundEnd = lineAsStr.find_first_of(" ", found);

      // Need to cut
      if (foundEnd != std::string::npos)
        lineAsStr = lineAsStr.substr(found, foundEnd - found);
      
      sprintf_s(m_scolproto, 260, "%s", lineAsStr.c_str());
      return true;
    }
  }
  fclose(ini);
  return false;
}


//////////////////////////////////////////////////////////////////////////////////
// Launch Scol Machine :
//
long ScolInterface::LaunchScolMachine(const char *CmdLine, long CmdShow, long MemSize, HWND PlugWin)
{
  char Cmdl[1024];
	bool goodVersion = true;
	char version[1024];
  memset(version, 0, 1024);
  memset(Cmdl, 0, 1024);

  // already loaded kill first
  if (m_boolScolStartSuccess)
  {
    CloseScolMachine();
    
    //wait for scol voyager close if no more process
    //while (m_boolScolStartSuccess)
    //  Sleep(1);
  }
  
	if (m_strScolVersionNeeded[0] != NULL)
	{
		if (!getScolVersion (version))
		{
			//::MessageBox(NULL,"Couldn't find the scol version. (may be a missing usm.ini)","Error...",0);
			goodVersion = false;
		}
		
		if ((h2i(version)) < (h2i(m_strScolVersionNeeded)))
		{
      //::MessageBox(NULL,"You need to update your scol voyager.","Error...",0);
			goodVersion = false;
		}
	}

  //////////////////////////////////////////////
  // verify Scol Installation
  // If Scol is Not Loaded then lauch Installer
  // TODO better interface
  if ((!m_boolScolLoadSuccess)||(!goodVersion)||(m_boolForceInstall))
  {
    if(m_boolForceInstall)
    {
		  ScolInstaller* MyScolInstaller;
  		
		  // Create new Scol Installer
		  MyScolInstaller = new ScolInstaller(m_strDownloadURL, m_longDownloadSize);
  		
		  // Launch Installation
		  if (MyScolInstaller->LaunchInstaller(PlugWin))
		  {
		    delete MyScolInstaller;
		    // Initialise Scol Library
		    m_boolScolLoadSuccess=GetScolExe();
		    if (!m_boolScolLoadSuccess)
        {
			    m_boolScolStartSuccess=false;  
          return -3;
        }
		  }
		  else
		  {
		    delete MyScolInstaller;
		    ::MessageBox(PlugWin,"Installation of Scol failed!","ERROR",MB_ICONERROR);
        m_boolScolStartSuccess=false;
		    return -4;
		  };
    }
    else
    {
      m_boolScolStartSuccess=false;
		  return -2;
    }
  }

  // why ?
  SkipMessages();


  ////////////////////////////////////////////
  // LaunchMachine
  
  m_axwin = PlugWin;
  sprintf(Cmdl, "%s %s %i", m_scolexe, CmdLine, (int)PlugWin);
  
  STARTUPINFO startupInfo;
  PROCESS_INFORMATION processInfo;
  
  ZeroMemory(&startupInfo, sizeof(startupInfo));
  startupInfo.cb = sizeof(startupInfo);
  startupInfo.dwFlags = STARTF_USESHOWWINDOW;
  startupInfo.wShowWindow = SW_SHOW;
  
  CreateProcess(NULL, Cmdl, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
  
  if (processInfo.hProcess)
  {
	  m_scolprocess = processInfo.hProcess;
    m_boolScolStartSuccess=true;

    return 1;
  }
  else
  {
	  m_boolScolStartSuccess=false;
    return -1;
  }
}

///////////////////////////////////////////////////////////////////////////////
// Send Message to Scol 
long ScolInterface::SendMessageToScol(const char *Msg)
{
	COPYDATASTRUCT cpd;
  LRESULT ret;

  // verify that Scol is Running
	if (!m_boolScolStartSuccess)
		return -1;
  
	// If OK, send message thought axwindow
  char* ScolMsg = (char*) malloc(strlen(Msg)+1);
	strcpy(ScolMsg,Msg);
  
  if (m_hscol != NULL)
  {
    cpd.dwData = WM_SCOLAXMESS;
    cpd.cbData = (strlen(ScolMsg) + 1);
    cpd.lpData = (PVOID)ScolMsg;
    ret = SendMessage(m_hscol,WM_COPYDATA,(WPARAM)m_axwin,(LPARAM)(LPVOID)&cpd);
    free(ScolMsg);
    return 1;
  }
	else
  {
    free(ScolMsg);
		return -1;
  }
}

///////////////////////////////////////////////////////////////////////////////
// 
void ScolInterface::SkipMessages()
{
  MSG msg;
  BOOL bRet;
  int i;

  i=0;

  while( (bRet = PeekMessage(&msg,NULL,0,0,PM_REMOVE))!=0)
	//while( (bRet = GetMessage(&msg, PlugWin, 0, 0 )) != 0)
	{ 
		if (bRet == -1)
		{
			// handle the error and possibly exit
		}
		else
		{
			DispatchMessage(&msg); 
		}
	}
}