/************************************************************************
    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
    
************************************************************************/

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

#include "stdafx.h"

#include <vector>
#include "oEvent.h"
#include "gdioutput.h"
#include "oDataContainer.h"
#include "csvparser.h"

#include "TabAuto.h"

#include "random.h"
#include "SportIdent.h"

#include "meos.h"
#include <cassert>
typedef bool (__cdecl* OPENDB_FCN)(void);
typedef int (__cdecl* SYNCHRONIZE_FCN)(oBase *obj);


bool oEvent::connectToServer()
{
  if (isThreadReconnecting())
    return false;

	if(msOpenDatabase)
		return true;
		
	hMod=LoadLibrary("meosdb.dll");

	msOpenDatabase = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msOpenDatabase");	
	msConnectToServer = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msConnectToServer");	
	msSynchronizeUpdate = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msSynchronizeUpdate");
	msSynchronizeRead = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msSynchronizeRead");
	msSynchronizeList = (SYNCHRONIZELIST_FCN)GetProcAddress(hMod, "msSynchronizeList");
	msRemove = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msRemove");
  msMonitor = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msMonitor");
  msDropDatabase = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msDropDatabase");
	msUploadRunnerDB = (SYNCHRONIZE_FCN)GetProcAddress(hMod, "msUploadRunnerDB");
  
  msGetErrorState = (ERRORMESG_FCN)GetProcAddress(hMod, "msGetErrorState");
  msResetConnection = (OPENDB_FCN)GetProcAddress(hMod, "msResetConnection");
  msReConnect = (OPENDB_FCN)GetProcAddress(hMod, "msReConnect");

	if (!msOpenDatabase || !msSynchronizeUpdate || !msSynchronizeRead 
    || !msConnectToServer || !msReConnect || !msGetErrorState 
    || !msMonitor || !msDropDatabase || !msUploadRunnerDB) {
		MessageBox(NULL, "Cannot load MySQL library.", NULL, MB_OK);
		// handle the error
		FreeLibrary(hMod);
    hMod=0;
		return false;//SOME_ERROR_CODE;
	}

	return true;
}

void oEvent::startReconnectDaemon()
{
  if (isThreadReconnecting())
    return;

  MySQLReconnect msqlr;
  msqlr.interval=1;
  HasDBConnection=false;
  tabAutoAddMachinge(msqlr);
}

bool oEvent::msSynchronize(oBase *ob)
{
	if(!msSynchronizeRead) return true;

	int ret = msSynchronizeRead(ob);

  char err[256];
  if(msGetErrorState(err))
    gdibase.addInfoBox("sqlerror", err, 15000);

  if (ret==0) {
    verifyConnection();
    return false;
  }

	if (typeid(*ob)==typeid(oTeam)) {
		static_cast<pTeam>(ob)->apply(false, 0);
	}

  if (ret==1) {
    sprintf_s(err, "Varning: ändringar i %s blev överskrivna", ob->getInfo().c_str());
    gdibase.RemoveFirstInfoBox("sqlwarning");
    gdibase.addInfoBox("sqlwarning", err, 5000);
  }
	return ret!=0;
}

void oEvent::resetChangeStatus(bool onlyChangable)
{
  if (HasDBConnection) {
    if (!onlyChangable) {
       // The object here has no dependeces that makes it necessary to set/rest
  
      for (list<oFreePunch>::iterator it=oe->punches.begin();
	        it!=oe->punches.end(); ++it)
        it->resetChangeStatus();

      //synchronize changed objects
      for (list<oCard>::iterator it=oe->Cards.begin();
            it!=oe->Cards.end(); ++it)
        it->resetChangeStatus();

	    for (list<oClub>::iterator it=oe->Clubs.begin(); 
            it!=oe->Clubs.end(); ++it)
        it->resetChangeStatus();

      for (list<oControl>::iterator it=oe->Controls.begin();
	          it!=oe->Controls.end(); ++it)
        it->resetChangeStatus();

      for (list<oCourse>::iterator it=oe->Courses.begin();
	          it!=oe->Courses.end(); ++it)
        it->resetChangeStatus();

      for (list<oClass>::iterator it=oe->Classes.begin();
	        it!=oe->Classes.end(); ++it)
        it->resetChangeStatus();
    }

    for (list<oRunner>::iterator it=oe->Runners.begin();
	      it!=oe->Runners.end(); ++it)
      it->resetChangeStatus();

    for (list<oTeam>::iterator it=oe->Teams.begin();
	      it!=oe->Teams.end(); ++it)
      it->resetChangeStatus();
  }
}

void oEvent::storeChangeStatus(bool onlyChangable)
{
  if (HasDBConnection) {
    if (!onlyChangable) {
       // The object here has no dependeces that makes it necessary to set/rest
      for (list<oFreePunch>::iterator it=oe->punches.begin();
	          it!=oe->punches.end(); ++it)
        it->storeChangeStatus();
 
      for (list<oCard>::iterator it=oe->Cards.begin();
            it!=oe->Cards.end(); ++it)
        it->storeChangeStatus();

	    for (list<oClub>::iterator it=oe->Clubs.begin(); 
            it!=oe->Clubs.end(); ++it)
        it->storeChangeStatus();

      for (list<oControl>::iterator it=oe->Controls.begin();
	          it!=oe->Controls.end(); ++it)
        it->storeChangeStatus();

      for (list<oCourse>::iterator it=oe->Courses.begin();
	          it!=oe->Courses.end(); ++it)
        it->storeChangeStatus();

      for (list<oClass>::iterator it=oe->Classes.begin();
	        it!=oe->Classes.end(); ++it)
        it->storeChangeStatus();
    }

    for (list<oRunner>::iterator it=oe->Runners.begin();
	      it!=oe->Runners.end(); ++it)
      it->storeChangeStatus();

    for (list<oTeam>::iterator it=oe->Teams.begin();
	      it!=oe->Teams.end(); ++it)
      it->storeChangeStatus();
  }
}


bool oEvent::synchronizeList(oListId id)
{
	if(!msSynchronizeList) return true;
  
  if(!HasDBConnection)
    return true;

  if(!autoSequence) {
    msSynchronize(this);
    resetSQLChanged();
  }

	if( !msSynchronizeList(this, id) ) {
    autoSequence=false;
    verifyConnection();
    return false;  
  }

  if (!autoSequence) {
		reEvaluateChanged();
    resetChangeStatus();
		return true;
	}
  autoSequence=false;
  return true;
}

bool oEvent::needReEvaluate() {
	return sqlChangedRunners | 
         sqlChangedClasses |
	       sqlChangedCourses |
	       sqlChangedControls |
	       sqlChangedCards |
         sqlChangedTeams;
}

bool oEvent::needRefreshClass(int classId) {
	if (sqlChangedClasses | sqlChangedCourses | sqlChangedControls)
    return true;

  if (classId == 0)
	  return  sqlChangedCards | sqlChangedTeams | sqlChangedRunners;

  pClass c = getClass(classId);

  return c && c->sqlChanged;
}

void oEvent::resetSQLChanged() {
	sqlChangedRunners = false;
	sqlChangedClasses = false;
	sqlChangedCourses = false;
	sqlChangedControls = false;
	sqlChangedClubs = false;
	sqlChangedCards = false;
	sqlChangedPunches = false;
	sqlChangedTeams = false;

  for (list<oRunner>::iterator it=oe->Runners.begin();
    it!=oe->Runners.end(); ++it) {
    it->storeChangeStatus();
    it->sqlChanged = false;
  }
  for (list<oTeam>::iterator it=oe->Teams.begin();
    it!=oe->Teams.end(); ++it) {
      it->storeChangeStatus();
    it->sqlChanged = false;
  }

  for (list<oClass>::iterator it=oe->Classes.begin();
    it!=oe->Classes.end(); ++it) {
    it->storeChangeStatus();
    it->sqlChanged = false;
  }
}
  

bool BaseIsRemoved(const oBase &ob){return ob.isRemoved();}

//Returns true if data is changed.
bool oEvent::autoSynchronizeLists(bool SyncPunches)
{
	if(!msSynchronizeList) return false;
  
  if(!HasDBConnection)
    return false;

	bool changed=false;
	string ot;

  // Reset change data and store update status on objects
  // (which might be incorrectly changed during sql update)
  resetSQLChanged();
  
  //Synchronize ourself
  ot=sqlUpdated;
  msSynchronize(this);
  if(sqlUpdated!=ot) {
    changed=true;
    gdibase.setWindowTitle(getTitleName());
  }
	//Controls
	int oc = sqlCounterControls;
  ot = sqlUpdateControls;
  autoSequence=true; //Avoid resync
  synchronizeList(oLControlId);
	changed |= oc!=sqlCounterControls;
  changed |= ot!=sqlUpdateControls;
	
  //Courses
	oc = sqlCounterCourses;
  ot = sqlUpdateCourses;
	autoSequence=true; //Avoid resync
  synchronizeList(oLCourseId);
	changed |= oc!=sqlCounterCourses;
  changed |= ot!=sqlUpdateCourses;

  //Classes
	oc = sqlCounterClasses;
  ot = sqlUpdateClasses;
  autoSequence=true; //Avoid resync
	synchronizeList(oLClassId);
	changed |= oc!=sqlCounterClasses;
  changed |= ot!=sqlUpdateClasses;

	//Clubs
	oc = sqlCounterClubs;
  ot = sqlUpdateClubs;
  autoSequence=true; //Avoid resync
	synchronizeList(oLClubId);
	changed |= oc!=sqlCounterClubs;
  changed |= ot!=sqlUpdateClubs;

	//Cards
	oc = sqlCounterCards;
  ot = sqlUpdateCards;
  autoSequence=true; //Avoid resync
	synchronizeList(oLCardId);
	changed |= oc!=sqlCounterCards;
  changed |= ot!=sqlUpdateCards;

	//Runners
	oc = sqlCounterRunners;
  ot = sqlUpdateRunners;
  autoSequence=true; //Avoid resync
	synchronizeList(oLRunnerId);
	changed |= oc!=sqlCounterRunners;
  changed |= ot!=sqlUpdateRunners;

	//Teams
	oc = sqlCounterTeams;
  ot = sqlUpdateTeams;
  autoSequence=true; //Avoid resync
	synchronizeList(oLTeamId);
	changed |= oc!=sqlCounterTeams;
  changed |= ot!=sqlUpdateTeams;

  if (changed)
    reCalculateLeaderTimes(0);

	if(SyncPunches){
		//Punches
		oc = sqlCounterPunches;
    ot = sqlUpdatePunches;
    autoSequence=true; //Avoid resync
		synchronizeList(oLPunchId);
		changed |= oc!=sqlCounterPunches;
    changed |= ot!=sqlUpdatePunches;
	}

	if (changed) {
    if (needReEvaluate())
      reEvaluateChanged();

    //Restore changed staus on object that might have been changed
    //during sql update, due to partial updates
    resetChangeStatus();
		return true;
	}

	return false;
}


bool oEvent::connectToMySQL(const string &server, const string &user, const string &pwd, int port)
{
  if (isThreadReconnecting())
    return false;

	if(!connectToServer())
		return false;

	MySQLServer=server;
	MySQLPassword=pwd;
	MySQLPort=port;
	MySQLUser=user;

  //Delete non-server competitions.
  list<CompetitionInfo> saved;
  list<CompetitionInfo>::iterator it;
	for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
		if(it->Server.empty())
			saved.push_back(*it);
	}
	cinfo=saved;
	
  if (!msConnectToServer(this)) {
    char bf[256];
    msGetErrorState(bf);
    MessageBox(NULL, bf, "Error", MB_OK);
    return false;
  }

  autoSequence=false;
	return true;
}


bool oEvent::uploadSynchronize()
{  
  if (isThreadReconnecting())
    throw std::exception("Internt fel i anslutningen. Starta om MeOS");

  for (list<CompetitionInfo>::iterator it = cinfo.begin(); it != cinfo.end(); ++it) {
    if (it->FullPath == CurrentNameId && it->Server.length()>0) {
      if (!gdibase.ask("ask:overwrite_server"))
        return false;
    }
  }

	HasDBConnection=false;

	if (!msSynchronizeUpdate)
		throw std::exception("Internt fel. Starta om MeOS");

	if( !msOpenDatabase(this) )
		throw std::exception("Kunde inte öppna databasen.");

	if( !msSynchronizeUpdate(this) )
		throw std::exception("Kunde inte ladda upp tävlingen.");

  if( !msUploadRunnerDB(this) )
  	throw std::exception("Kunde inte ladda upp löpardatabasen.");


	HasDBConnection=true;
  autoSequence=false;

  // Save local version of database
  saveRunnerDatabase(CurrentNameId, false);

	return true;
}

//Load a (new) competition from the server.
bool oEvent::readSynchronize(const CompetitionInfo &ci)
{
  if(ci.Id<=0)
    throw std::exception("help:12290");

  if (isThreadReconnecting())
    return false;

	HasDBConnection=false;

	if(!msConnectToServer)
		return false;

	MySQLServer=ci.Server;
	MySQLPassword=ci.ServerPassword;
	MySQLPort=ci.ServerPort;
	MySQLUser=ci.ServerUser;

  //Delete non-server competitions.
  list<CompetitionInfo> saved;
  list<CompetitionInfo>::iterator it;
	for (it=cinfo.begin(); it!=cinfo.end(); ++it) {
		if(it->Server.empty())
			saved.push_back(*it);
	}
	cinfo=saved;
	
  if (!msConnectToServer(this)) {
    char bf[256];
    msGetErrorState(bf);
    throw std::exception(bf);
    //MessageBox(NULL, bf, "Error", MB_OK);
    return false;
  }

	newCompetition("");
	Id=ci.Id;
	strcpy_s(CurrentNameId, ci.FullPath.c_str());

	char file[260];
	sprintf_s(file, "%s.dbmeos", CurrentNameId);
	getUserFile(CurrentFile, file);
  if( !msOpenDatabase(this) ) {
    char bf[256];
    msGetErrorState(bf);
    throw std::exception(bf);
  }

  updateFreeId();
  HasDBConnection=false;
  
  openRunnerDatabase(CurrentNameId);

  msSynchronizeRead(this);

  // Cache database locally
  saveRunnerDatabase(CurrentNameId, false);

  HasDBConnection=true;  
  autoSequence=false;

  // Setup multirunner links
  for (oRunnerList::iterator it = Runners .begin(); it != Runners.end(); ++it) 
    it->createMultiRunner(false,false);

  // Remove incorrect references
  for (oRunnerList::iterator it = Runners .begin(); it != Runners.end(); ++it) {
    if (it->multiRunner.size() > 0 ) {
      vector<pRunner> &pr = it->multiRunner;
      for (size_t k=0; k<pr.size(); k++) {
        if (pr[k]==0 || pr[k]->tParentRunner != &*it) {
          it->multiRunner.clear();
          it->updateChanged();
          it->synchronize();
          break;
        }
      }
    }
  }

  // Check duplicates
  vector<bool> usedInTeam(qFreeRunnerId+1);
  bool teamCorrected = false;

  for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
    if (it->correctionNeeded) {
      it->updateChanged();
      teamCorrected = true;
    }

    for (size_t i = 0; i < it->Runners.size(); i++) {
      pRunner r = it->Runners[i];
      
      if (r != 0) {
        int expectedIndex = -1;
        if (it->Class)
          expectedIndex = it->Class->getLegRunnerIndex(i);

        if (expectedIndex>=0 && expectedIndex != r->getMultiIndex()) {
          int baseLeg = it->Class->getLegRunner(i);
          it->setRunner(baseLeg, r->getMultiRunner(0), true);
          teamCorrected = true;
        }        
      }
    }

    for (size_t i = 0; i < it->Runners.size(); i++) {
      pRunner r = it->Runners[i];
      if (r != 0) {
        if (usedInTeam[r->Id]) {
          it->Runners[i] = 0; // Reset duplicate runners
          it->updateChanged();
          teamCorrected = true;
          if (r->tInTeam == &*it)
            r->tInTeam = 0;
        }
        else
          usedInTeam[r->Id] = true;
      }
    }
  }
  usedInTeam.clear();

  if (teamCorrected) {
    for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
      it->apply(true, 0);
    }
  }

	reEvaluateAll(false);
  vector<string> out;
  checkChanged(out);
  assert(out.empty());

  for (oRunnerList::iterator it = Runners.begin(); it != Runners.end(); ++it) {
    if (it->correctionNeeded) {
      it->createMultiRunner(true, true);
      if (it->tInTeam && it->Class) {
        pTeam t = it->tInTeam;
        int nr = min(int(it->Class->getNumStages())-1, int(it->multiRunner.size()));
        t->setRunner(0, &*it, true);
        for (int k=0; k<nr; k++)
          t->setRunner(k+1, it->multiRunner[k], true);
        t->updateChanged();
        t->synchronize();
      }
      it->synchronizeAll();
    }
  }

 	return true;
}

bool oEvent::reConnect(char *errorMsg256)
{
  if(HasDBConnection)
    return true;

  if (isThreadReconnecting()){
    strcpy_s(errorMsg256, 256, "Synkroniseringsfel.");
    return false;
  }

  if (!msReConnect) {
    strcpy_s(errorMsg256, 256, "Inte ansluten mot meosdb.dll");
    return false;
  }

  if(msReConnect()) {
    HasDBConnection=true;

    //synchronize changed objects
    for (list<oCard>::iterator it=oe->Cards.begin();
          it!=oe->Cards.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

		for (list<oClub>::iterator it=oe->Clubs.begin(); 
          it!=oe->Clubs.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

    for (list<oControl>::iterator it=oe->Controls.begin();
		      it!=oe->Controls.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

    for (list<oCourse>::iterator it=oe->Courses.begin();
		      it!=oe->Courses.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

    for (list<oClass>::iterator it=oe->Classes.begin();
		    it!=oe->Classes.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

	  for (list<oRunner>::iterator it=oe->Runners.begin();
		    it!=oe->Runners.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

	  for (list<oTeam>::iterator it=oe->Teams.begin();
		    it!=oe->Teams.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

    for (list<oFreePunch>::iterator it=oe->punches.begin();
		    it!=oe->punches.end(); ++it)
      if(it->isChanged())
        it->synchronize(false);

    autoSynchronizeLists(true);

    return true;
  }

  msGetErrorState(errorMsg256);
  return false;
}

//Returns number of changed, non-saved elements.
int oEvent::checkChanged(vector<string> &out) const
{
  int changed=0;
  char bf[256];
  out.clear();

  for (list<oCard>::iterator it=oe->Cards.begin();
    it!=oe->Cards.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Card %d", it->CardNo);
      out.push_back(bf);
      it->synchronize();
    }

	for (list<oClub>::iterator it=oe->Clubs.begin(); 
    it!=oe->Clubs.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Club %s", it->name.c_str());
      out.push_back(bf);
      it->synchronize();
    }

  for (list<oControl>::iterator it=oe->Controls.begin();
	  it!=oe->Controls.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Control %d", it->Numbers[0]);
      out.push_back(bf);
      it->synchronize();
    }

  for (list<oCourse>::iterator it=oe->Courses.begin();
	      it!=oe->Courses.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Course %s", it->Name.c_str());
      out.push_back(bf);
      it->synchronize();
    }

  for (list<oClass>::iterator it=oe->Classes.begin();
	    it!=oe->Classes.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Class %s", it->Name.c_str());
      out.push_back(bf);
      it->synchronize();
    }

  for (list<oRunner>::iterator it=oe->Runners.begin();
	    it!=oe->Runners.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Runner %s", it->Name.c_str());
      out.push_back(bf);
      it->synchronize();
    }
  for (list<oTeam>::iterator it=oe->Teams.begin();
	    it!=oe->Teams.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Team %s", it->Name.c_str());
      out.push_back(bf);
      it->synchronize();
    }
  for (list<oFreePunch>::iterator it=oe->punches.begin();
	    it!=oe->punches.end(); ++it)
    if(it->isChanged()) {
      changed++;
      sprintf_s(bf, "Punch SI=%d, %d", it->CardNo, it->Type);
      out.push_back(bf);
      it->synchronize();
    }
  
  return changed;
}

bool oEvent::verifyConnection()
{
  if (!HasDBConnection)
    return false;

  if (isThreadReconnecting())
    return false;

  if (!msMonitor)
    return false;

  if (!msMonitor(this)) {
    startReconnectDaemon();
    return false;
  }
  return true;
}

const string &oEvent::getServerName() const
{
  return serverName;
}

void oEvent::closeDBConnection()
{  
  bool hadDB=HasDBConnection;
 
  if(isThreadReconnecting()) {
    //Don't know what to do?!  
  }
  gdibase.setWaitCursor(true);
  if (HasDBConnection) {
    autoSynchronizeLists(true);
  }
  HasDBConnection=false;

  if (msResetConnection) 
    msResetConnection();

  Id=0;

  if (!oe->empty() && hadDB) {
    save();
    tOriginalName=Name;
    Name+=" (Lokal kopia från: " + serverName + ")";
    char cn[260];
    sprintf_s(cn, "%s.%s.meos", CurrentNameId, serverName.c_str());
    getUserFile(CurrentFile, cn);
    serverName.clear();
    save();
    gdibase.setWindowTitle(Name);
  }
  else serverName.clear();

  gdibase.setWaitCursor(false);  
}

void oEvent::listConnectedClients(gdioutput &gdi)
{
  gdi.addString("", 1, "Anslutna klienter:");
  char bf[256];
  gdi.fillRight();
  gdi.pushX();
  int x=gdi.getCX();
  for (size_t k=0;k<connectedClients.size();k++) {
    sprintf_s(bf, "%d.", k+1);
    gdi.addStringUT(0, bf);
    gdi.addStringUT(gdi.getCY(), x+30, 0, connectedClients[k]);
    gdi.popX();
    gdi.dropLine();
  }
  gdi.fillRight();
  gdi.dropLine(2);
}

DWORD oEvent::clientCheckSum() const
{
  DWORD cs=0;
  for (size_t k=0;k<connectedClients.size();k++)
    for (size_t i=0;i<connectedClients[k].length();i++)
      cs += BYTE(connectedClients[k][i]) << ((i*5+k*7)%24);

  return cs;
}

void oEvent::validateClients()
{
  currentClientCS = clientCheckSum();
}

bool oEvent::hasClientChanged() const
{
  return currentClientCS!=clientCheckSum();
}

void oEvent::dropDatabase()
{  
  bool dropped = false;
  if (HasDBConnection) {
    HasDBConnection=false;
    
    if (msDropDatabase) 
      dropped = msDropDatabase(this)!=0;
  }
  else throw std::exception("Inte ansluten");

  if (!dropped) {
    if(msGetErrorState) {
      char bf[256];
      msGetErrorState(bf);
      if(strlen(bf)>0)
        throw std::exception(bf);
    }
    
    throw std::exception("Operationen misslyckades. Orsak okänd.");
  }
  clear();
}
