/* 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 <string.h>

#include "Residue.oh"
#include "Chain.oh"
#include "Atom.oh"


@implementation Residue

static NSDictionary *translate3LetterTo1Letter=nil;


+(void)initialize	//@nodoc
{
	if (self == [Residue class])
	{
		if (!translate3LetterTo1Letter)
		{
			translate3LetterTo1Letter = [NSDictionary dictionaryWithObjectsAndKeys:
				@"A",@"ALA", @"R",@"ARG", @"N",@"ASN", @"D",@"ASP", @"C",@"CYS",
				@"E",@"GLU", @"Q",@"GLN", @"G",@"GLY", @"H",@"HIS", @"I",@"ILE",
				@"L",@"LEU", @"K",@"LYS", @"M",@"MET", @"F",@"PHE", @"P",@"PRO",
				@"S",@"SER", @"T",@"THR", @"W",@"TRP", @"Y",@"TYR", @"V",@"VAL", nil];
			RETAIN(translate3LetterTo1Letter);
		}
	}
}


-(id)init	//@nodoc
{
	[super init];
	atomarr = RETAIN([NSMutableArray arrayWithCapacity:12]);
	name = nil;
	number = nil;
	subcode = 32; /* per default */
	verified = NO;
	modname = nil;
	moddesc = nil;
	seqnum = -1;
	return self;
}


-(void)dealloc	//@nodoc
{
	//printf("Residue_dealloc %@\n",self);
	if (name)
	{
		RELEASE(name);
	}
	if (number)
	{
		RELEASE(number);
	}
	if (atomarr)
	{
		int i;
		for (i=0; i<[atomarr count]; i++)
		{
			[[atomarr objectAtIndex:i] dropAllBonds];
		}
		[atomarr removeAllObjects];
		RELEASE(atomarr);
	}
	[super dealloc];
}


/*
 *   return this residue's number
 */
-(NSNumber*)number
{
	return number;
}


-(int)sequenceNumber
{
	return seqnum;
}


/*
 *   return this residue's subcode
 */
-(char)subcode
{
	return subcode;
}


/*
 *   return this residue's name
 */
-(NSString*)name
{
	return name;
}


/*
 *   return this residue's name (modified, original standard form)
 */
-(NSString*)modname
{
	if (modname)
	{
		return modname;
	} else {
		return name;
	}
}


/*
 *   return description of modification of this residue
 */
-(NSString*)moddescription
{
	return moddesc;
}


/*
 *   returns a key for this residue (=concatenation of number and subcode)
 */
-(NSString*)key
{
	NSString *str;
	if (subcode==32)
	{
		str=[NSString stringWithFormat: @"%@",number];
	} else {
		str=[NSString stringWithFormat: @"%@%c",number,subcode];
	}
	return str;
}


/*
 *   returns a string describing this residue (=concatenation of name and number)
 */
-(NSString*)description
{
	NSString *str;
	if (subcode==32)
	{
		str=[NSString stringWithFormat: @"%@%@",name,number];
	} else {
		str=[NSString stringWithFormat: @"%@%@%c",name,number,subcode];
	}
	return str;
}


/*
 *   return the one letter code for this type of residue
 */
-(NSString*)oneLetterCode
{
        NSString *res;
	if ([self isModified])
	{
		res = [translate3LetterTo1Letter objectForKey: modname];
	} else {
		res = [translate3LetterTo1Letter objectForKey: name];
	}
        if (!res)
        {
            res = @"X";
        }
        return res;
}


/*
 *   return the chain this residue belongs to
 */
-(Chain*)chain
{
	return chain;
}


/*
 *   return YES if this residue is one of the "known" nucleic acids
 */
-(BOOL)isNucleicAcid
{
	if ([name isEqualToString:@"  A"] ||
	    [name isEqualToString:@"  T"] ||
	    [name isEqualToString:@"  C"] ||
	    [name isEqualToString:@"  G"] ||
	    [name isEqualToString:@"  U"] ||
	    [name isEqualToString:@"  I"] ||
	    [name isEqualToString:@" +A"] ||
	    [name isEqualToString:@" +T"] ||
	    [name isEqualToString:@" +C"] ||
	    [name isEqualToString:@" +G"] ||
	    [name isEqualToString:@" +U"] ||
	    [name isEqualToString:@" +I"])
	{
		return YES;
	} else {
		return NO;
	}
}


/*
 *   return YES if this residue is one of the 20 standard amino acids
 */
-(BOOL)isStandardAminoAcid
{
	NSString *one;
	if ([self isModified])
	{
		one = [translate3LetterTo1Letter objectForKey: modname];
	} else {
		one = [translate3LetterTo1Letter objectForKey: name];
	}
	if (one)
	{
		return YES;
	} else {
		return NO;
	}
}


/*
 *   return YES if this residue has all (backbone + sidechain) atoms present
 */
-(BOOL)haveAtomsPresent
{
	if (!verified)
	{
		[NSException raise:@"Unsupported" format:@"this residue has not yet been verified."];
		return NO;
	} else {
		return atomsComplete;
	}
}


/*
 *   returns YES if this residue is a modified one 
 */
-(BOOL)isModified
{
	return modname != nil;
}


/*
 *   calculate and return distance of this residue's CA atom to the reference's CA atom
 */
-(double)distanceCATo:(Residue*)r2
{
	Atom *ca1 = [self getCA];
	Atom *ca2 = [r2 getCA];
	double dist = -1.0;
	if (ca1 && ca2)
	{
		dist = [ca1 distanceTo: ca2];
	}
	return dist;
}


/*
 *   add atom to this residue
 */
-(id)addAtom:(Atom*)atom
{
	[atomarr addObject: atom];
	if ([[atom name] isEqualToString:@"CA"])
	{
		/* cache Calpha atoms */
		t_ca = atom;
	}
	return self;
}


/*
 *   return a named atom in this residue
 */
-(Atom*)getAtomWithName: (NSString*)p_name
{
	int i;
	Atom *t_atm;
	for (i=0; i<[atomarr count]; i++)
	{
		t_atm = [atomarr objectAtIndex:i];
		if ([p_name isEqualToString:[t_atm name]])
		{
			return t_atm;
		}
	}
	return nil;
}


/*
 *   return an atom in this residue with the given number
 */
-(Atom*)getAtomWithNumber:(NSNumber*)p_number
{
	int i;
	Atom *t_atm;
	for (i=0; i<[atomarr count]; i++)
	{
		t_atm = [atomarr objectAtIndex:i];
		if ([p_number isEqualToNumber:[t_atm number]])
		{
			return t_atm;
		}
	}
	return nil;
}


-(Atom*)getAtomWithInt:(unsigned int)p_number
{
	return [self getAtomWithNumber: [NSNumber numberWithInt:p_number]];
}


/*
 *   return atom with name @"CA"
 */
-(Atom*)getCA
{
	return t_ca;
}


/*
 *   return enumerator over all atoms
 */
-(NSEnumerator*)allAtoms
{
	return [atomarr objectEnumerator];
}


/*
 *   transform this residue by the given matrix
 */
-(id)transformBy: (Matrix53*)m
{
	//printf("Residue-transformBy %@\n",self);
	NSEnumerator *e_atoms = [self allAtoms];
	id atom;
	while ((atom = [e_atoms nextObject]))
	{
		[atom transformBy: m];
	}
	return self;
}


/*
 *   convenience function to compute a residue's key from its number and subcode
 */
+(NSString*)computeKeyFromInt:(int)p_number subcode:(char)p_subcode
{
	NSString *res;
	if (p_subcode==32)
	{
		res = [NSString stringWithFormat:@"%d",p_number];
	} else {
		res = [NSString stringWithFormat:@"%d%c",p_number,p_subcode];
	}
	return res;
}


/*
 *   convenience function to translate 3-letter codes to 1-letter
 */
+(NSString*)translate3LetterTo1LetterCode: (NSString*)c3letter
{
	return [translate3LetterTo1Letter objectForKey: c3letter];
}

@end

