userdata *users=NULL;
int userscount=0;
groupdata *groups=NULL;
int groupscount=0;
char *conf;

int send_to_user(Request req, Ack *ack );

void write_users()
{
	int i;
	for( i = 0; i < userscount; i++ )
	{
		printf( "%s\n", users[i].name );
	}
}

void write_groups()
{
	int i;
	int j;
	int count;
	for( i = 0; i < groupscount; i++ )
	{
		count = 0;
		printf( "%s\n", groups[i].name );
		for( j = 0; j < MAX_USERS; j++ )
			if( groups[i].users[j] != NULL )
			{
				count++;
				printf( " %d) %s \n", count, groups[i].users[j]->name );
			}
		}
}

/*
Function returns 0 when user doesn't exist
or number of user in array beginning from 1
*/
int user_exist( char username[LOGIN_LENGTH+1] )
{
	int i;
	for( i = 0; i < userscount; i++ )
		if( strcmp(users[i].name,username) == 0 )
			return i+1;
	return 0;
}

int group_exist( char groupname[MAX_GROUPNAME_LENGTH+1] )
{
	int i;
	for( i = 0; i < groupscount; i++ )
		if( strcmp(groups[i].name,groupname) == 0 )
			return i+1;
	return 0;
}

int valid_password( int usrind, char password[MAX_USERPASS_LENGTH+1] )
{
	if( strcmp(users[usrind].password,password) == 0 )
		return 1;
	else
		return 0;
}

int log_in(Request req)
{
	int i,j;
	printf("log:%s haslo:%s\n",req.login,req.data);
	if( ( i = user_exist( req.login ) ) )
	{
		if ( valid_password( i-1, req.data ) ) // password in field data of request struct
		{
			if ( ( !users[i-1].logged ) && ( users[i-1].failedlogins < 3 ) )
			{
				users[i-1].failedlogins=0;
				Request request;
				Ack ack;
				users[i-1].logged = 1;
				for( j = 0; j < users[i-1].bufcount; j++ )
				{
					request.m_type = users[i-1].msgbuf[j].m_type;
					strcpy(request.login,users[i-1].msgbuf[j].login);
					strcpy(request.data,users[i-1].msgbuf[j].data);
					strcpy(request.msg,users[i-1].msgbuf[j].msg);
					send_to_user( request, &ack );
				}
				users[i-1].bufcount = 0;
				return 0;
			}
		}
		else
		{
			if ( ++users[i-1].failedlogins >= 3 )
				return 2;
		}
	}
	return 1;
}

int log_out(Request req)
{
	int i;
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
		{
			users[i-1].logged = 0;
			return 0;
		}
	return 1;
}

void insert( int fd, char *str )
{
	int pos;
	char chr;
	int i;
	for( i = 0; i < strlen(str); i++  )
	{
		chr=str[i];
		pos = lseek( fd, 0, SEEK_CUR );
		lseek( fd, -1, SEEK_END );
		while( lseek( fd, 0, SEEK_CUR ) >= pos )
		{
			char c;
			read( fd, &c, 1 );
			write( fd, &c, 1 );
			lseek( fd, -3, SEEK_CUR );
		}
		lseek( fd, 1, SEEK_CUR );
		write( fd, &chr, 1 );
		lseek( fd, pos + 1, SEEK_SET );
	}
}

void delete_to( int fd, char ender )
{
	char chr;
	int pos;
	while ( ( ( read( fd, &chr, 1 ) ) > 0 ) && ( chr != ender ) )
	{
		pos = lseek( fd, 0, SEEK_CUR ) - 1;
		char c;
		while( read( fd, &c, 1 ) > 0 )
		{
			lseek( fd, -2, SEEK_CUR );
			write( fd, &c, 1 );
			lseek( fd, 1, SEEK_CUR );
		}
		ftruncate( fd, lseek( fd, 0, SEEK_CUR ) - 1 );
		lseek( fd, pos, SEEK_SET );
	}
	lseek( fd, -1, SEEK_CUR );
}

int change_password(Request req)
{
	int i;
	int fd;
	char chr;
	char user[LOGIN_LENGTH + 1];
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
		{
			strcpy(users[i-1].password,req.data);
			
			fd = open( conf, O_RDWR );
			while ( read( fd, &chr, 1 ) )
			{
				if( chr != 'u' )
					while ( ( read( fd, &chr, 1 ) ) && ( chr != '\n' ) );
				else
				{
					lseek( fd, 1, SEEK_CUR );
					read( fd, user, LOGIN_LENGTH );
					if( strncmp( user, req.login, LOGIN_LENGTH ) == 0 )
					{
						lseek( fd, 1, SEEK_CUR );
						delete_to( fd, '\n' );//usuwa od bierzacej pozycji az napotka znak
						insert( fd, req.data );//wstawia w miejsce w ktorym sie znajduje i przesuwa zawartosc pliku
						break;
					}
				}
			}
			close( fd );
			return 0;
		}
	return 1;
}


int show_all_users( Request req, Ack *ack )
{
	int i,j;
	strcpy(ack->msg,"");
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
		{
			for( j = 0; j < userscount; j++ )
				{
					sprintf( ack->msg, "%s%s\n", ack->msg, users[j].name );
				}
			return 0;
		}
	return 1;
}

int show_logged_users( Request req, Ack *ack )
{
	int i,j;
	strcpy(ack->msg,"");
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
		{
			for( j = 0; j < userscount; j++ )
				if( users[j].logged )
				{
					sprintf( ack->msg, "%s%s\n", ack->msg, users[j].name );
				}
			return 0;
		}
	return 1;
}

int show_group_member_users( Request req, Ack *ack )
{
	int i,j,k;
	strcpy(ack->msg,"");
	if( ( i = user_exist( req.login ) ) )
		if( ( k = group_exist( req.data ) ) )
			if( users[i-1].logged )
			{
				for( j = 0; j < userscount; j++ )
					if( groups[k-1].users[j] != NULL )
					{
						sprintf( ack->msg, "%s%s\n", ack->msg, groups[k-1].users[j]->name );
					}
				return 0;
			}
	return 1;
}


int add_user_to_group( Request req )
{
	int i,j,k;
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
			if( ( j = group_exist( req.data ) ) )
				for( k = 0; k < MAX_USERS; k++ )
					if( ( groups[j-1].users[k] == NULL ) || ( groups[j-1].users[k] == &users[i-1] ) )
					{
						groups[j-1].users[k] = &users[i-1];
						write_groups();
						return 0;
					}
	return 1;
}

int remove_user_from_group( Request req )
{
	int i,j,k,l;
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
			if( ( j = group_exist( req.data ) ) )
				for( k = 0; k < MAX_USERS; k++ )
					if( ( groups[j-1].users[k] == NULL ) || ( groups[j-1].users[k] == &users[i-1] ) )
					{
						for( l = k; l < MAX_USERS - 1; l++ )
							groups[j-1].users[l] = groups[j-1].users[l+1];
						write_groups();
						return 0;
					}
	return 1;
}

int show_all_groups( Request req, Ack *ack )
{
	int i,j;
	strcpy(ack->msg,"");
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
		{
			for( j = 0; j < groupscount; j++ )
			{
				sprintf( ack->msg, "%s%s\n", ack->msg, groups[j].name );
			}
			return 0;
		}
	return 1;
}


int get_user_queue( char username[LOGIN_LENGTH+1] )
{
	int queue;
	queue = msgget( atoi( &username[3] ), IPC_CREAT | 0666 );
	return queue;
}

void delete_usersgroups()
{
	free(users);
	free(groups);
	userscount=0;
	groupscount=0;
}

int add_user( userdata user )
{
	int i;
	if ( userscount >= MAX_USERS )
		return 1;
	if( strncmp( user.name, "inf", 3 ) != 0 )
		return 1;
	for( i = 3; i < LOGIN_LENGTH; i++ )
		if( ( user.name[i] < '0' ) || ( user.name[i] > '9' ) )
			return 1;
	users=( userdata* )realloc( users, ++userscount * sizeof( userdata ) );
	strcpy( users[userscount-1].name, user.name );
	strcpy( users[userscount-1].password, user.password );
	users[userscount-1].logged=0;
	users[userscount-1].queue=get_user_queue( user.name );
	users[userscount-1].bufcount = 0;
	users[userscount-1].failedlogins=0;
	return 0;
}

int add_group( groupdata group )
{
	if ( groupscount >= MAX_GROUPS )
		return 1;
	groups=( groupdata* )realloc( groups, ++groupscount * sizeof( groupdata ) );
	strcpy( groups[groupscount-1].name, group.name );
	int i;
	for( i = 0; i < MAX_USERS; i++ )
		groups[groupscount-1].users[i] = NULL;
	return 0;
}

int send_to_user(Request req, Ack *ack )
{
	int i,j;
	Request msg;
	strcpy(ack->msg,"");
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
			if( ( j = user_exist( req.data ) ) )
			{
				if( users[j-1].logged )
				{
					msg.m_type = M_MSG;
					strcpy(msg.login,req.login);
					strcpy(msg.data,req.data);
					strcpy(msg.msg,req.msg);
					msgsnd( get_user_queue( msg.data ), &msg, sizeof( msg ) - sizeof( msg.m_type ), 0 );
					return 0;
				}
				else
					if( users[j-1].bufcount < MAX_QUEUE_LENGTH )
					{
						int k = 0;
						k = (users[j-1].bufcount)++;
						users[j-1].msgbuf[k].m_type = M_MSG;
						strcpy(users[j-1].msgbuf[k].login,req.login);
						strcpy(users[j-1].msgbuf[k].data,req.data);
						strcpy(users[j-1].msgbuf[k].msg,req.msg);
						return 0;
					}
					
			}
	return 1;
}


int send_to_group( Request req )
{
	int i,k,j;
	Ack ack;
	if( ( i = user_exist( req.login ) ) )
		if( users[i-1].logged )
			if( ( k = group_exist( req.data ) ) )
			{
				for( j = 0; j < userscount; j++ )
					if( ( groups[k-1].users[j] != NULL ) && ( strcmp( users[i-1].name, groups[k-1].users[j]->name ) != 0 ) )
					{
						strcpy(req.data,groups[k-1].users[j]->name);
						send_to_user( req, &ack );
					}
				return 0;
			}
	return 1;
}

void close_queues()
{
	int j;
	for( j = 0; j < userscount; j++ )
	{
		msgctl( users[j].queue, IPC_RMID, 0 );
	}
}

int read_server_conf( char *filename )
{
	int fd;
	char chr;
	int sep,k;
	groupdata group;
	userdata user;
	
	conf = (char*)malloc( strlen(filename) + 1 );
	strcpy( conf, filename );
	fd = open( filename, O_RDONLY );
	if ( fd == 0 )
	{
		printf( "Blad otwarcia pliku\n" );
		return 1;
	}
	while ( read( fd, &chr, 1 ) > 0 )
	{
		if ( chr == '#' )
		{
			while ( ( read( fd, &chr, 1 ) > 0 ) && ( chr != '\n' ) );
			continue;
		}
		if ( chr == 'g' )
		{
			k=0;
			sep=0;
			while ( ( read( fd, &chr, 1 ) > 0 ) && ( chr != '\n' ) )
			{
				if( chr == ':' )
				{
					sep++;
					k=0;
				}
				else
				{
					if ( sep == 1 )
					{
						if ( k >= MAX_GROUPNAME_LENGTH )
						{
							close(fd);
							printf( "Zbyt długa nazwa grupy\n" );
							return 1;
						}
						group.name[k] = chr;
						k++;
						group.name[k] = 0;
					}
					else
					{
						close(fd);
						printf( "Zla struktura pliku\n" );
						return 1;
					}
				}
			}
			if ( k == 0 )
			{
				close(fd);
				printf( "Zla struktura pliku\n" );
				return 1;
			}
			if ( add_group(group) )
			{
				close(fd);
				printf( "Za duza liczba grup\n" );
				return 1;
			}
			continue;
		}
		if ( chr == 'u' )
		{
			k=0;
			sep=0;
			while ( ( read( fd, &chr, 1 ) > 0 ) && ( chr != '\n' ) )
			{
				if( chr == ':' )
				{
					if ( ( sep == 1) && ( k < LOGIN_LENGTH ) )
					{
						close(fd);
						printf( "Za krotka nazwa uzytkownika\n" );
						return 1;
					}
					sep++;
					k=0;
				}
				else
				{
					if ( sep == 0 )
					{
						close(fd);
						printf( "Zla struktura pliku\n" );
						return 1;
					}
					else
						if ( sep == 1 )
						{
							if ( k >= LOGIN_LENGTH )
							{
								close(fd);
								printf( "Zbyt dluga nazwa uzytkownika\n" );
								return 1;
							}
							user.name[k] = chr;
							k++;
							user.name[k] = 0;
						}
						else
							if ( sep == 2 )
							{
								if ( k >= MAX_USERPASS_LENGTH )
								{
									close(fd);
									printf( "Zbyt dlugie haslo\n" );
									return 1;
								}
								user.password[k] = chr;
								k++;
								user.password[k] = 0;
							}
				}
			}
			if ( ( k == 0 ) && ( sep == 2) )
				strcpy(user.password,user.name);
			else
				if ( ( k == 0 ) || ( sep != 2 ) )
				{
					close(fd);
					printf( "Zla struktura pliku\n" );
					return 1;
				}
			if ( add_user( user ) )
			{
				close(fd);
				printf( "Zla nazwa uzytkownika lub za duza liczba uzytkownikow\n" );
				return 1;
			}
			continue;
		}
	}
	close( fd );
	return 0;
}
