/* Copyright 2003  Alexander V. Diemand

    This file is part of MolTalk.

    MolTalk 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 2 of the License, or
    (at your option) any later version.

    MolTalk 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 MolTalk; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

/* vim: set filetype=objc: */


#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "StructureFactory.oh"
#include "privateStructure.oh"
#include "PDBParser.oh"


static Class structureFactoryKlass = nil;


@implementation StructureFactory


/*
 *   return TRUE if the indicated file is compressed
 */
+(BOOL)isFileCompressed:(NSString*)filepath
{
	unsigned char buffer[6];
	buffer[0]='\0';buffer[1]='\0'; buffer[2]='\0';
	buffer[3]='\0';buffer[4]='\0'; buffer[5]='\0';
	FILE *t_file = fopen([filepath cString],"r");
	if (t_file)
	{
		fread(buffer,1,2,t_file);
		//printf("file compressed? %03d %03d\n",buffer[0],buffer[1]);
		fclose(t_file);
		if (buffer[0]==31 && buffer[1]==157)
		{	/* compress */
			return YES;
		}
		if (buffer[0]==31 && buffer[1]==139)
		{	/* gzip */
			return YES;
		}
	}
	return NO;
}


/*
 *   return TRUE if the indicated file is readable
 */
+(BOOL)checkFileStat:(NSString*)filepath
{
	struct stat fstatbuf;
	//printf ("Structure_checkFileStat: %@\n",filepath);
	if (stat ([filepath cString], &fstatbuf) == 0)
	{
		if (!S_ISREG(fstatbuf.st_mode))
		{
			NSLog(@"file %@ is not a regular one.",filepath);
			return NO;
		}
#ifndef WIN32
		gid_t ourgid = getegid ();
		uid_t ouruid = geteuid ();
		if (fstatbuf.st_uid == ouruid && (fstatbuf.st_mode&S_IRUSR))
		{
			/* might have access ? */
			return YES;
		}
		if (fstatbuf.st_gid == ourgid && (fstatbuf.st_mode&S_IRGRP))
		{
			/* might have access ? */
			return YES;
		}
		if (fstatbuf.st_mode&S_IROTH)
		{
			/* might have access ? */
			return YES;
		}
#else
		/* under windoze we just claim to have access */
		/* this needs to be done at some later stage ... */
		return YES;
#endif
	} else {
		NSLog(@"file %@ does not exist.",filepath);
	}
	return NO;
}


/*
 *   reads and parses a file in PDB format and returns the resulting object of type: @Structure
 */
+(id)newStructureFromPDBFile:(NSString*)fn
{
	return [self newStructureFromPDBFile:fn options:0L];
}


/*
 *   reads and parses a file in PDB format and returns the resulting object of type: @Structure<br>
 *   accepts a combination of options to pass to the parser.
 */
+(id)newStructureFromPDBFile:(NSString*)fn options:(long)opts
{
	if (![self checkFileStat:fn])
	{
		[NSException raise:@"Unsupported" format:@"File does not exist or we don't have the rights for reading it. file=%@",fn];
		return nil;
	}
	if ([self isFileCompressed:fn])
	{
		return [PDBParser parseStructureFromPDBFile:fn compressed:YES options:opts];
	} else {
		return [PDBParser parseStructureFromPDBFile:fn compressed:NO options:opts];
	}
}


/*
 *   reads and parses a file in PDB format from the PDB mirroring directory structure<br>
 *   and returns the resulting object of type: @Structure
 */
+(id)newStructureFromPDBDirectory:(NSString*)code
{
	return [self newStructureFromPDBDirectory:code options:0L];
}

/*
 *   reads and parses a file in PDB format from the PDB mirroring directory structure<br>
 *   and returns the resulting object of type: @Structure<br>
 *   accepts a combination of options to pass to the parser.
 */
+(id)newStructureFromPDBDirectory:(NSString*)code options:(long)opts
{
	char *pdbdir = getenv("PDBDIR");
	if (!pdbdir)
	{
		[NSException raise:@"Unsupported" format:@"Cannot determine root path of PDB directory (environment variable: PDBDIR)"];
		return nil;
	}
	if ([code length] != 4)
	{
		NSLog(@"PDB codes are exactly 4 letters long. Abort.");
		return nil;
	}
	char *s_code = (char*)[code cString];
	NSString *fname = [NSString stringWithFormat:@"%s/%c%c/%c%c%c%c.pdb",pdbdir,s_code[1],s_code[2],s_code[0],s_code[1],s_code[2],s_code[3]];
	return [self newStructureFromPDBFile:fname options:opts];
}


/*
 *   creates a new instance of type @Structure, calls @method(StructureFactory,+newInstance)
 */
+(id)newStructure
{
	if (!structureFactoryKlass)
	{
		structureFactoryKlass = self;
	}
	return [structureFactoryKlass newInstance];
}


/*
 *   internally used to create the instance of the correct type.<br>
 *   This method should be overidden in subclasses wich create subclasses of class @Structure.
 */
+(id)newInstance
{
	return AUTORELEASE([Structure new]);
}


/*
 *   sets the class which will create instances. Per default set to @StructureFactory.
 */
+(void)setDefaultStructureFactory:(Class)klass
{
#ifdef __APPLE__
	if (klass && [klass isSubclassOfClass:self])
#else
	if (klass && GSObjCIsKindOf(klass,self))
#endif
	{
		structureFactoryKlass = klass;
	} else {
		[NSException raise:@"unimplemented" format:@"class %@ does not inherit from StructureFactory.",klass];
	}
}


@end

