#ifndef __SecondaryStructure_H
#define __SecondaryStructure_H


#include "Helix.h"
#include "Sheet.h"
#include "Atom.h"
#include "StringUtils.h"

#include <vector>
#include <iostream>
#include <utility>
#include <string>

#ifdef g29
#include <hash_map>
#else
#include <ext/hash_map>
using __gnu_cxx::hash_map;
#endif

using std::ifstream;
using std::ofstream;
using std::pair;

/*
CLASS 
  SecondStruct  
  
  This class encapsulate all the reading of the secondary structure 
  from the PDB file and gives various methods of verification on them

KEWORDS
  secondary, structure, alpha, helix, beta, strand, sheet, connection,
  molecule, atom, PDB

AUTHORS
  Zipi Fligelamn (zipo@math.tau.ac.il)

  Copyright: SAMBA group, Tel-Aviv Univ. Israel, 1999.

CHANGES LOG
<UL> 
<LI> 02.05.02 - Oranit Shem-Tov (ornit@math.tau.ac.il):
     - Fixing a small bug in the "readSSFromPDBFile"
     methods. Specifically, in the previous version, these methods
     missed the first strand of a beta sheet 
</UL>

GOALS
  The goals of this class is save for the user the need to anderstand all
  the particulars of the representaion of the secondary structures in the 
  PDB and yet allow him to use its infromation. The class has various quering
  methods

USAGE
*/
class SecondStruct 
{
public:
  // typedef SmallVector<Helix> Helices;
  //typedef SmallVector<Sheet> Sheets;
  typedef vector< Helix> Helices;
  typedef vector< Sheet> Sheets;
  typedef enum { Unknown=0, AHelix, BSheet, Turn} SSType; 

  const static char DSSP_ALPHA_HELIX_SS_TYPE = 'H';
  const static char DSSP_3_HELIX_SS_TYPE = 'G';
  const static char DSSP_5_HELIX_SS_TYPE = 'I';

  const static char DSSP_STRAND_SS_TYPE = 'E';
  
  // GROUP: Constructors
  
  //// Empty Constructor
  SecondStruct();
  
  //// A constructor that will read all the relevand infromation from
    // the PDB file and then close it
  explicit SecondStruct(const char* const PDBFileName); 
  
  //// Copy constructor
  SecondStruct(const SecondStruct& ss);
  
  // GROUP: Data members infromation
  
  //// The small array of Helices
  inline const Helices& AlphaHelices() const;
  
  //// The small array of BetaSheets
  inline const Sheets&  BetaSheets() const;
  
  void readSSFromPDBFile(ifstream& PDBstream);

  //// Read all the relevant information about the helices and the
  // strands of the molecule, descibed in the givan PDB file and
  // then close it
  // author: Oranit Shem-Tov (ornit@math.tau.ac.il) 
  static void readSSFromPDBFile(const char* const pdbFileName,
				vector< Helix>& helices,
				vector< Strand>& strands); 


  //// Read all the relevant information about the helices and the
  // strands of the molecule, descibed in the givan PDB stream.
  // Note: After invoking the method, the stream stays open.  
  // Author: Oranit Shem-Tov (ornit@math.tau.ac.il) 
  static void readSSFromPDBFile(ifstream& pdbStream,
				vector< Helix>& helices,
				vector< Strand>& strands);
    
  //// Read all the relevant information about the helices and the
  // strands of the molecule, descibed in the givan DSSP file and
  // then close it
  // author: Oranit Shem-Tov (ornit@math.tau.ac.il) 
  static void readSSFromDsspFile(const char* const dsspFileName,
				 vector< Helix>& helices,
				 vector< Strand>& strands); 
  
  //// Read all the relevant information about the helices and the
  // strands of the molecule, descibed in the givan DSSP stream.
  // Note: After invoking the method, the stream stays open.  
  // Author: Oranit Shem-Tov (ornit@math.tau.ac.il) 
  // Implementation Note: The format of the DSSP format is 
  // described in the following URL:
  // http://www.cmbi.kun.nl/gv/dssp/descrip.html
  static void readSSFromDsspFile(ifstream& dsspStream,
                                 vector< Helix>& helices,
                                 vector< Strand>& strands);

  template< class TMolecule>
  static void loadSSEs(const string& file,
		       const int fileType,
		       TMolecule& molecule);
  
  //// Gets a char which represents a secondary structure assignment and
  // determines if it a helix.
  // Author: Oranit Shem-Tov (ornit@math.tau.ac.il)
  static bool isDsspHelix(char dsspSSType) {
    return (dsspSSType == DSSP_ALPHA_HELIX_SS_TYPE) ||
      (dsspSSType == DSSP_3_HELIX_SS_TYPE) ||
      (dsspSSType == DSSP_5_HELIX_SS_TYPE);
  }


  //* Group : Operators *//
    
    //// a safe assignment operator
    SecondStruct& operator=(const SecondStruct& rhs);
    // GROUP: Inspecting particular information
    
    //// Requesting a certain helix by its id.
    Helix& operator[](const int helixId);
    
    //// Requesting a certain sheet by its letter id. If it not found
    // the end of the vector is returned
    // The user need to check this out before proceeding
    // A particular strand can afterwood be requested since the BetaSheet
    // class inherits from SmallArray where a operator[] is defined
    Sheet& operator[](const char sheetId);
    
    // GROUP: Adding information 
    //// Adding a new Helix 
    void addAHelix(const Helix& helix);
    //// Adding a new Beta sheet
    void addBSheet(const Sheet& sheet);
        
    // GROUP: Atoms belogning inspections
    
    //// Checking whether a certain residue in a certain chain is 
    // located in a particular alpha helix
    bool isResInAHelix(const short helixNum, const int resNum) const;
    
    //// Checking whether a certain residue in a certain chain is 
    // located in a particular beta sheet
    // Since this is done by checking all the strands until finding the
    // strand that the residue is in it, this act might be computationally 
    // expensive
    bool isInBSheet(const char sheetId[],  const int resNum) const;
    
    //// given a vector of indecises will return true if the indices
    // come from the same SS structure or not 
    // at the moment atoms that come from the same sheet but not
  // from the same strand are considered as from different SS
    bool isFromSameSS(const vector< short>& indices);

    //// same as above only with a regular array.
    bool isFromSameSS(const short indices[], int len);
    
    //// Making a general search on all the secondary structures
    // returning the type if the residue was found in one of them and 
    // known if not
    SSType residueInSS(const int resNum) const;
    
    //// Looking for the residue in all the helices and returning the 
    // helix number if found, and -1 if not.
    short numOfAHelix(const int resNum) const;
    
    //// Looking for the residue in all the sheets and returning
    // the char id of the sheet if found, and '\0' if not
    const char* numOfBSheet(const int resNum) const;
    
    //// Looking for the residue in all the sheets and returning a pair
    // the pointer to the char[3] id of the sheet if found, 
    // and NULL if not, as the first argument. 
    // The strand number in case there is a sheet and -1 if not.
    pair< const char *,short> sheetAndStrandId(const int resNum) const;

  
  //// Listing of all the secondary structure in the printing format
  // defined inside Helix, Strand and BetaSheet classes
  friend ostream& operator<<(ostream& s, const SecondStruct& sst);
  

  
private:
  // DSSP Constants:
  // Implementation Note: The field numbers start from 0
  const static unsigned short DSSP_MAX_LENGTH_OF_RECORD = 180;
  const static short DSSP_START_OF_PDB_RESIDUE_FIELD = 5;
  const static short DSSP_SS_TYPE_FIELD = 16;
  const static short DSSP_CHAIN_FIELD = 11;
  

  // PDB Constants:
  const static unsigned short PDB_MAX_LENGTH_OF_RECORD = 85;
    
  // data members
  Helices alpha;
  Sheets beta;
};



/*************************
 Inline definitions
 ************************/
const SecondStruct::Helices& SecondStruct::AlphaHelices() const
{
  return alpha;
}

const SecondStruct::Sheets& SecondStruct::BetaSheets() const
{
  return beta;
}



template< class TMolecule>
void SecondStruct::loadSSEs(const string& file,
			    const int fileType,
			    TMolecule& molecule) {
  vector< Helix> helices;
  vector< Strand> strands;
  
  /* if (fileType == DSSP) { */
/*     SecondStruct::readSSFromDsspFile(file.c_str(), helices, strands); */
/*   } else if (fileType == DSSPC) { */
/*     SecondStruct::readSSFromDsspFile(file.c_str(), helices, strands); */
/*   } else {  */
/*     SecondStruct::readSSFromPDBFile(file.c_str(), helices, strands); */
/*   } */

  if (fileType == 0) { 
    SecondStruct::readSSFromDsspFile(file.c_str(), helices, strands); 
  } else {
    SecondStruct::readSSFromPDBFile(file.c_str(), helices, strands);
  } 
  

  ////
  // Maps a residue index to its SSE number
  typedef hash_map< unsigned int, pair< char,short> >::iterator HashIter;
  hash_map< unsigned int, pair< char,short> > residue2SSEHashMap;  
  
  for (unsigned int helixIndex = 0 ; 
       helixIndex < helices.size() ; 
       helixIndex++) {
    Helix& helix = helices[helixIndex];

    for (int i = helix.startResidue() ; 
	 i <= helix.endResidue() ; 
	 i++) {
      residue2SSEHashMap[i] = pair< char,short>( 'H', helix.sseIdNum());	  //!!!helix.???
    }
  }
  
  for (unsigned int strandIndex = 0 ; 
       strandIndex < strands.size() ; 
       strandIndex++) {

      Strand& strand = strands[strandIndex];

      for (int i = strand.startResidue() ; 
	 i <= strand.endResidue() ; 
	   i++) {
	residue2SSEHashMap[i] = pair<char,short>( 'S', strand.sseIdNum());	 // strand.???? 
      }
  }

  for (unsigned int i = 0 ; i < molecule.size() ; i++) {
    HashIter iter=residue2SSEHashMap.find(molecule[i].residueIndex());
    if(iter != residue2SSEHashMap.end()){
      molecule[i].setSSE( iter->second );
    }else{
      molecule[i].setSSE( pair< char,int>('L',0) );
    }
  }
}  






#endif 

