#pragma once
#include <iphlpapi.h>
#include "vector.h"

// This allows us to access the SQL database.
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
no_namespace rename("EOF", "EndOfFile")

// The Privacy Levels
typedef enum {
	bcNormal, // visible to everyone
	bcVisibleToBuddies // visible only to buddies
} bcPrivacyLevel;

// The record data definitions
class UserRecord {
public:
	UserRecord(){ SetDefault(); };
	~UserRecord(){};
	UserRecord & operator=(UserRecord& x);

	void SetDefault();

	int UserId;
	_bstr_t UserLogin;
	_bstr_t Password;
	_bstr_t FirstName;
	_bstr_t LastName;
	_bstr_t Email;
	int YearId;
	int LoggedIn;
	DATE LoggedInSince;
	int Idle;
	DATE IdleSince;
	int LocationId;
	int LastLocationId;
	int Away;
	_bstr_t AwayMessage;
	int PrivacyLevel;
	_bstr_t SecretWeapon;
	_bstr_t Interest;
	_bstr_t Background;

	int update;
};

class EventRecord {
public:
	EventRecord(){ SetDefault(); };
	~EventRecord(){};
	EventRecord & operator=(EventRecord& x);

	void SetDefault();

	int EventId;
	_bstr_t EventName;
	_bstr_t Description;
	int OwnerId;
	int GroupId;
	int LocationId;
	_bstr_t OtherLocation;
	int CategoryId;
	DATE CreatedTime;
	DATE StartTime;
	DATE EndTime;
	int Eternal;
	int Private;
	_bstr_t Keywords;

	int update;
};

class GroupRecord {
public:
	GroupRecord(){ SetDefault(); };
	~GroupRecord(){};
	GroupRecord & operator=(GroupRecord& x);

	void SetDefault();

	int GroupId;
	_bstr_t GroupName;
	_bstr_t Description;
	int OwnerId;
	int Idle;
	DATE IdleSince;
	int LocationId;
	int PublicView;
	int PublicEdit;

	int update;
};

class EventPermissionRecord {
public:
	EventPermissionRecord(){ SetDefault(); };
	~EventPermissionRecord(){};
	EventPermissionRecord & operator=(EventPermissionRecord& x);

	void SetDefault();

	int CanEdit;
	int CanView;

	int update;
};

class EventSubscriptionRecord {
public:
	EventSubscriptionRecord(){ SetDefault(); };
	~EventSubscriptionRecord(){};
	EventSubscriptionRecord & operator=(EventSubscriptionRecord& x);

	void SetDefault();

	DATE TrackingStart;
	DATE TrackingEnd;
	int Eternal;
	int CanEdit;
	int CanView;

	int update;
};

class EventCategoryRecord {
public:
	EventCategoryRecord(){ SetDefault(); };
	~EventCategoryRecord(){};
	EventCategoryRecord & operator=(EventCategoryRecord& x);

	void SetDefault();

	int CategoryId;
	_bstr_t Category;

	int update;
};

class LocationRecord {
public:
	LocationRecord(){ SetDefault(); };
	~LocationRecord(){};
	LocationRecord & operator=(LocationRecord& x);

	void SetDefault();

	int LocationId;
	_bstr_t LocationName;
	int ParentId;
	int Wireless;
	int nUsers;
	int nEvents;
	int Visible;

	int update;
};

class YearRecord {
public:
	YearRecord() { SetDefault(); };
	~YearRecord(){};
	YearRecord & operator=(YearRecord& x);

	void SetDefault();

	int YearId;
	_bstr_t DegreeYear;

	int update;
};

class VersionRecord {
public:
	VersionRecord() { SetDefault(); };
	~VersionRecord(){};
	VersionRecord & operator=(VersionRecord& x);

	void SetDefault();

	int Build;
	_bstr_t URL;
	int ShowURL;
	int AllowLogin;

	int update;
};

// database objects that don't have records but still need ids
extern int NullUserId;
extern int NullLocationId;
extern int OtherLocationId;
extern int NullGroupId;
extern int NullCategoryId;
extern int NullYearId;
extern int NullEventId;

// The Database Layer (just for kat ;-) )
class BridgeDB
{
public:
	BridgeDB();
	~BridgeDB();

	int Connect(const TCHAR * source, const TCHAR * table, const TCHAR * username, const TCHAR * password);
	int Connect(); // read the connection info from the config file
	void Disconnect();
	void LoseConnection();
	int Reconnect();
	
	int ConnectionTimeout; // in seconds
	int CommandTimeout; // in seconds

	// notification log
	int LogNotification(int originator, int operant, TCHAR * type, TCHAR * reason, int optional);

	// version info
	int GetVersionInfo(int build, VersionRecord & record);

	// user access
	int GetPassword(int userId, std::wstring& password);
	int EstimateLocation(int Mac1, int Mac2, unsigned int signalstrength, int & locationId);
	int GetUsersAtLocation(int locationId, Vector<int>& userIds);
	int GetOnlineUsers(Vector<int>& userIds);
	int GetUser(int userId, UserRecord & record);
	int GetUserState(int userId, UserRecord & record);
	int GetUsername(int userId, std::wstring& username);
	int GetUserId(const TCHAR * username, int & userId);
	int GetAllUsers(Vector<int>& userIds);
	int SetPassword(int userId, const TCHAR * password);
	int Authenticate(int userId, const TCHAR * password, int & authenticated);
	int UserExist(int userId, int & exists);
	int GetYears(Vector<YearRecord>& records);
	int GetYear(int yearId, std::wstring & year);

	int SetUserLocation(int userId, int locationId);
	int SetUserAway(int userId, int away, const TCHAR * msg);
	int SetUserIdle(int userId, int idle, int secs);
	int SetUserLogin(int userId, int loggedin);
	int SetUserPrivacyLevel(int userId, int privacy);
	int UpdateUser(UserRecord& record);

	// buddy list
	int GetBuddyList(int userId, Vector<int>& buddyIds);
	int GetOnlineBuddyList(int userId, Vector<int>& buddyIds);
	int AddToBuddyList(int userId, int buddyId);
	int RemoveFromBuddyList(int userId, int buddyId);
	int GetUsersThatWatchMe(int buddyId, Vector<int>& userIds);

	// group access
	int GetGroup(int groupId, GroupRecord& record);
	int GetGroupId(const TCHAR * groupname, int & groupId);
	int GetActiveSubscribedGroups(int userId, Vector<int>& groups);
	int GetSubscribedGroups(int userId, Vector<int>& groups);
	int GetGroupMembers(int groupId, Vector<int>& userIds);
	int GetAttendingGroupMembers(int groupId, Vector<int>& userIds);
	int GetAllGroups(Vector<int>& groupIds);
	int SubscribeToGroup(int userId, int groupId);
	int UnsubscribeFromGroup(int userId, int groupId);
	int UpdateGroup(GroupRecord& record);
	int EnterGroupMeeting(int userId, int groupId);
	int LeaveGroupMeeting(int userId, int groupId);
	int GetSubscribableGroups(int userId, Vector<int>& groupIds);


	int SetGroupIdle(int groupId, int idle);

	// event access
	int GetEvent(int eventId, EventRecord& record);
	int GetEventId(const TCHAR * eventname, int & eventId);
	int GetSubscribedEvents(int userId, Vector<int>& events);
	int GetTrackedEvents(int userId, Vector<int>& events);
	int GetActiveSubscribedPublicEvents(int userId, Vector<int>& events);
	int GetAllEvents(Vector<int>& eventIds);
	int GetPublicEvents(Vector<int>& eventIds);
	int GetPrivateEvents(int userId, Vector<int>& eventIds);
	int GetSubscribableEvents(int userId, Vector<int>& eventIds);
	int GetAllActiveEvents(Vector<int>& eventIds);
	int GetEventsByLocation(int locationId, Vector<int>& eventIds);
	int GetActiveEventsByLocation(int locationId, Vector<int>& eventIds);
	int SetEventPermission(int userId, int eventId, int CanEdit, int CanView);
	int SubscribeToEvent(int userId, int eventId, DATE& TrackingStart, DATE& TrackingEnd, int Eternal);
	int UnsubscribeFromEvent(int userId, int eventId);
	int DeleteEventPermission(int userId, int eventId);
	int GetActiveGroupEvents(int groupId, Vector<int>& eventIds);
	int UnsubscribeGroupFromEvent(int groupId, int eventId);
	int GetAllEventCategories(Vector<int>& categoryIds);
	int GetEventCategory(int categoryId, EventCategoryRecord& record);
	int UpdateEvent(EventRecord& record);
	int GetEventMembers(int eventId, Vector<int>& members);
	int GetEventCanViews(int eventId, Vector<int>& canviews);
	int GetEventPermission(int userId, int eventId, EventPermissionRecord& record);
	int GetEventSubscription(int userId, int eventId, EventSubscriptionRecord& record);
	int EventIsPrivate(int eventId, int & isprivate);

	// location access
	int GetLocation(int locationId, LocationRecord& record);
	int GetSubscribedLocations(int userId, Vector<int>& locationIds);
	int GetAllLocations(Vector<int>& locationIds, int build = 999);
	int GetVisibleLocations(Vector<int>& locationIds);
	int SubscribeToLocation(int userId, int locationId);
	int UnsubscribeFromLocation(int userId, int locationId);
	int AddLocationSignature(int locationId, int SignalStrength, int Mac1, int Mac2);
	int SetLocationEventCount(int locationId, int nevents);

	// creation
	int CreateUser(UserRecord& record);
	int CreateGroup(GroupRecord& record);
	int CreateEvent(EventRecord& record);
	int CreateLocation(LocationRecord& record);

	// deletion
	int DeleteUser(int userId);
	int DeleteGroup(int groupId);
	int DeleteEvent(int eventId);
	int DeleteLocation(int locationId);

	int connected;

	// returns the record in the cache if it exists, otherwise gets it from the db
	int GetCachedUser(int userId, UserRecord & record, int update = 0);
	int GetCachedUserState(int userId, UserRecord & record, int update = 0);
	int GetCachedUsername(int userId, std::wstring & username, int update = 0);
	int GetCachedGroup(int groupId, GroupRecord & record, int update = 0);
	int GetCachedEvent(int eventId, EventRecord & record, int update = 0);
	int GetCachedLocation(int locationId, LocationRecord & record, int update = 0);
	int GetCachedEventCategory(int categoryId, EventCategoryRecord & record, int update = 0);
	int GetCachedYears(Vector<YearRecord>& y, int update = 0);

	// returns the actual record in the cache
	UserRecord * CachedUser(int userId);
	LocationRecord * CachedLocation(int locationId);
	GroupRecord * CachedGroup(int groupId);
	EventRecord * CachedEvent(int eventId);
	EventCategoryRecord * CachedEventCategory(int categoryId);

	void DeleteCachedUser(int userId);
	void DeleteCachedLocation(int locationId);
	void DeleteCachedGroup(int groupId);
	void DeleteCachedEvent(int eventId);
	void DeleteCachedEventCategory(int categoryId);

	void ClearCache();

private:
	/////////////////
	// This section maintains the
	// client-side cache of db data

	std::list<UserRecord *> users;
	std::list<LocationRecord *> locations;
	std::list<GroupRecord *> groups;
	std::list<EventRecord *> events;
	std::list<EventCategoryRecord *> eventcategories;
	Vector<YearRecord> years;

	// critical sections to protect the cache
	// from multi-threaded bashing
	CRITICAL_SECTION uCS; 
	CRITICAL_SECTION lCS;
	CRITICAL_SECTION gCS;
	CRITICAL_SECTION eCS;
	CRITICAL_SECTION ecCS;
	CRITICAL_SECTION yCS;

	_RecordsetPtr ExecuteWithRetry(_CommandPtr & pCom);
	_ConnectionPtr GetConnection();
	_ConnectionPtr connection;


};

// the return codes for the db calls
#define dbSuccess 1
#define dbInvalidParam -1
#define dbException 0