# Copyright (c) 1996, 1997, The Regents of the University of California.
# All rights reserved.  See Legal.htm for full text and disclaimer.

"""
PDB basic read-access class PR.py by Paul Dubois, LLNL
Version 1.1 July 1997
"""
import pprint
import pypdb
import sys

class PR:
    "PDB file read-access class."

    no_file_message = '(PR object not open on any file)'

    Error = 'PR error'

    def __del__(self):
        self.close()

    def __getattr__(self, name):
        w = self.read(self._registry[name])
        if self._cache:
            self.__dict__[name] = w
        return w

    def __init__(self, filename='', path='', pdbtype='', 
                 cache=1, verbose = 1):
        """PR(filename='', path='', pdbtype='',cache=1,verbose=1)
        opens filename if given, registers names specified by 
        PDB-name and type specifiers. 
        Object will use a cache by default. """

        self.__dict__['_handle'] = None
        self.__dict__['_registry'] = {}
        self.__dict__['_cache'] = cache
        self.set_verbosity(verbose)
        if filename:
            self.open(filename, path, pdbtype)

    def __repr__(self):
        if self.is_open():
            current_mode = 'opened for ' + self.inquire_handle().mode()
        else:
            current_mode = PR.no_file_message
        return 'PDB file %s %s.' % (self.inquire_filename(), current_mode)

    __str__ = __repr__

    def check_open(self):
        "check_open(): raise exception if not open for read."
        if not self.is_open():
            raise PR.Error, 'PR object not open for read.'

    def close(self):
        "close(): close the file."
        if self.is_open():
            if self.inquire_verbosity():
                print "Closing PDB file", \
                      self.inquire_filename()
            self.inquire_handle().close()
            r = self._registry
            c = self._cache
            d = self.__dict__
            v = self.inquire_verbosity()

            for n in d.keys():
                del d[n]

            d['_handle'] = None
            d['_registry'] = {}
            d['_cache'] = c
            self.set_verbosity(v)


    def inquire_filename(self):
        "inquire_filename() = name of this file."
        self.check_open()
        return self.inquire_handle().filename()

    def inquire_handle(self):
        "inquire_handle() = PDBfile object open on this file."
        return self._handle

    def inquire_high(self, name):
        "inquire_high(name) = high indices of name."
        self.check_open()
        pname = self.inquire_pdbname(name)
        return self.inquire_handle().high(pname)

    def inquire_low(self, name):
        "inquire_low(name) = low indices of the name."
        self.check_open()
        pname = self.inquire_pdbname(name)
        return self.inquire_handle().low(pname)

    def inquire_ls(self,path='',pdbtype=''):
        """inquire_ls(path='',pdbtype='') = list of those pdb names 
        in the file which represent objects with names matching the
        path or type equal to the given type. Blank matches all."""

        self.check_open()
        return self.inquire_handle().ls(path, pdbtype)

    def inquire_mode(self):
        "inquire_mode() = mode ('r', 'w', or 'a') of this file."
        self.check_open()
        return self.inquire_handle().mode()

    def inquire_names(self):
        "inquire_names() = sorted list of registered names"
        self.check_open()
        r = self._registry.keys()
        r.sort()
        return r

    def inquire_pdbname(self, name):
        """inquire_pdbname(name) = registered pdbname corresponding to
        given name."""
        self.check_open()
        return self._registry[name] 

    def inquire_pwd(self):
        "inquire_pwd() = present PDB directory"
        self.check_open()
        return self.inquire_handle().pwd()

    def inquire_shape(self,name):
        "inquire_shape(name) = shape of name."
        self.check_open()
        pname = self.inquire_pdbname(name)
        low = self.inquire_low(pname)
        high = self.inquire_high(pname)
        n = len(low)
        result = []
        for i in range(n):
            result.append(high[i] - low[i] + 1)
        return tuple(result)

    def inquire_type(self, name):
        "inquire_type(name) = type of name."
        self.check_open()
        pname = self.inquire_pdbname(name)
        return self.inquire_handle().type(pname)

    def inquire_verbosity(self):
        "inquire_verbosity() = current value of verbose flag."
        return self._verbose

    def is_cached(self):
        "is_cached() = value of cache flag."
        return self._cache

    def is_column_major(self):
        "is_column_major() = true if file was written by Fortran."
        self.check_open()
        return self.inquire_handle().is_column_major()

    def is_open(self):
        "is_open() = true if file is open for read"
        if self.inquire_handle() == None:
            return 0
        return 1

    def print_names(self, file=sys.stdout):
        """print_list(file=sys.stdout): pretty print the list of
        registered names to file."""

        self.check_open()
        pprint.pprint(self.inquire_names(), file)


    def open(self, filename, path='',pdbtype=''):
        """open(name, path='', pdbtype=''): open file, register names
        using register_pattern(path, pdbtype). The default is to 
        register all the names."""

        self.close()
        if filename:
            self.__dict__['_handle'] =  pypdb.open(filename)
            self.register_pattern(path, pdbtype)

    def read(self,pdbname):
        "read(pdbname) = the value of pdbname as a Python object."
        self.check_open()
# Note: cannot handle partial reads via the full read by naming
        if pdbname[-1] == ')':
            raise PR.Error, pdbname + ' -- must use read_part'
        return self.inquire_handle().read(pdbname)

    def read_part(self, name, triples):
        """read_part(name, triples) =  part of the value of name 
        as a Python object. Triples must be an array of 3 integers
        per subscript giving the low/high/stride values desired. 
        Note that these subscripts are relative to the values stored
        in the PDB file, and do not follow Python conventions."""

        self.check_open()
        pname = self.inquire_pdbname(name)
        low = self.inquire_low(name)
        high = self.inquire_high(name)
        n = len(low)
        if (3*n) != len(triples):
            raise PR.Error, \
                  name + ', wrong number of subscripts, need '+`n`
        for i in range(n):
            if triples[3*i] > triples[3*i+1] or \
               low[i] > triples[3*i] or \
               high[i] < triples[3*i+1] or \
               triples[3*i + 2] < 1:
                raise PR.Error, name + ', bad triple =' \
                      +`triples[3*i:3*i+3]`
        return self.inquire_handle().read_part(pname, triples)

    def register_name(self, name, pdbname):
        """register_name(name, pdbname) registers name as a key to 
        get the variable whose name in the file is pdbname"""
        self._registry[name] = pdbname

    def register_pattern(self, path, pdbtype):
        """register_pattern(path, pdbtype) -- 
        Register names found using inquire_ls via register_name"""

        self.check_open()
        for name in self.inquire_handle().ls(path,pdbtype):
            self.register_name(name, name)

    def set_directory(self, name):
        """set_directory(name) 
        -- change PDB directory to name, return status"""
        self.check_open()
        return self.inquire_handle().cd(name)

    def set_verbosity(self, flag):
        """verbose(flag) sets verbosity level to flag.
        0 for quiet operation, 
        1 to report closing only, 
        2 to report access to data."""
        if 0 <= flag <= 2:
            self.__dict__['_verbose'] = flag
        else:
            raise PR.Error, 'Illegal value for verbosity: '+`flag`

