#! /usr/bin/perl -w

"""
    This program 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 3 of the License, or
    (at your option) any later version.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
"""

$mapreg_version = 0.02 ;

################################################################
## This script will take a cns map as input and the outputs   ##
## a new region of of the map specified at the command line   ##
################################################################

################################################################
## version history
################################################################
## 0.90 - Added option for type of map (-t) and for culling
## 0.01 - I guess this was the first one, and it was great

##############################################
# constants
##############################################
$UNDEF = "" ;
$CELL = "CELL" ;
$ASU = "ASU" ;

# banner
print "\n" ;
print "**************************************************\n" ;
print "*** mapreg version $mapreg_version\n" ;
print "***   output a region of a cns map\n" ;
print "***   copyright James C. Stroud, 2008\n" ;
print "***   distributed under the GNU Public License\n" ;
print "**************************************************\n" ;

# help
sub print_help() {
  print <<EOF ;
Usage: mapreg [-h] [-c weight] [-b border] [-s symm] [-t type]
              [-x 'a b c alpha beta gamma'] mapfile
              [x1 y1 z1 x2 y2 z2 | ASU | CELL | pdbfile ]

Flags: -h          print this help
       -b border   define a border if using a pdb file to define region
                   default is 5 (5 Angstroms)
       -c weight   culls according to a weighting factor between 0 and 1
       -x cell     the sides and angles *must* be in single quotes
       -s symm     usually symmetry number is needed (ispcgr number)
       -t type     type of input map - default is cns
       -o outfile  name of output file (name generated if not supplied)

Description: Mapreg takes a cns map as input and outputs a new
             region of the map as specified at the command line.

             The possible region specifiers are:
               x1 y1 z1 x2 y2 z2 : the region defined by the two grid unit or
                    fractional coordinate points (x1,y1,z1) and (x2,y2,z2)
               'ASU' : the CCP4 default asymmetric unit
               'CELL' : the whole unit cell
               pdbfile : a pdb file defining the limits of the region
                         if border is defined, then this will be the border
                         in Angstroms around pdbfile to define the region
             If the region specifier is left out, then mapreg will
             output the whole unit cell.

             CULLING
             =======
             If a culling factor is supplied and a pdb file is used
             for trimming, then culling will be attempted.

             Culling trims the map to the the atoms of the pdb file
             if supplied. Without the pdb file, the program terminates
             with an error if a culling factor is supplied.

             The culling weighting factor is, for all practical purposes,
             arbitrary. Play with it to get the desired results.
             Start with 0.1 and go up (tighter) or down (less tight).
             Make sure you are aware of the caveats of culling
             before you use this to make figures for publication.

EOF
}
  

use Getopt::Std ;

$opt_h = $UNDEF ;
$opt_b = $UNDEF ;
$opt_c = $UNDEF ;
$opt_x = $UNDEF ;
$opt_s = 0 ;
$opt_t = "cns" ;
$opt_o = $UNDEF ;

getopts('hb:c:x:s:t:o:');

##############################################
# help?
##############################################
if ($opt_h) {
  print_help() ;
  exit(0) ;
}

##############################################
# border?
##############################################
if ($opt_b) {
  $border = int($opt_b) ;
} else {
  $border = 5 ;
}

##############################################
# culling?
##############################################
if ($opt_c) {
  $cull = $opt_c ;
  print "\nCulling will be attempted with $cull weighting.\n"
}

##############################################
# cell
##############################################
if ($opt_x) {
  $cell = $opt_x ;
  print "\nUsing cell of $cell.\n" ;
}

##############################################
# symmetry?
##############################################
if ($opt_s) {
  $symm = int($opt_s) ;
  print "\nUsing symmetry $symm (ispcgr)\n" ;
} else {
  $symm = 1 ;
  print "**\n" ;
  print "** No symmetry provided, using triclinic (#1 in ispcgr).\n" ;
  print "**\n" ;
}


##############################################
# maptype?
##############################################
if ($opt_t) {
  $maptype = $opt_t ;
  print "\nMap type is $maptype\n" ;
} else {
  $maptype = "cns" ;
  print "**\n" ;
  print "** No maptype provided, using cns.\n" ;
  print "**\n" ;
}

##############################################
# maptype?
##############################################
$outfile = $opt_o ;
if ($outfile) {
  print "\nWill write output map as $outfile\n" ;
} else {
  print "**\n" ;
  print "** No filename provided, generating one.\n" ;
  print "**\n" ;
}




##############################################
# check for file name
##############################################
if (!$ARGV[0]) {
  print "\nNo file given or misuse of arguments. Aborting.\n\n" ;
  print "Usage: mapreg [-h] [-b border] [-s symm] [-t type] mapfile\n" ;
  print "              [x1 y1 z1 x2 y2 z2 | ASU | CELL | pdbfile]\n" ; 
  print "  use \"mapreg -h\" for help\n\n" ;
  exit ;
}

$mapin = shift(@ARGV) ;


##############################################
# Determine the region
##############################################
$region = $UNDEF ;
$pdbfile = $UNDEF ;
$num_args = @ARGV ;
if ($num_args == 0) {
  $region = $CELL ;
  $region_log = $CELL ;
  $mapout = "$CELL.$mapin" ;
  $logfile = "log.$mapout.txt" ;
  print "\nOutput map will have $region as region.\n" ;
}
if ($num_args == 1) {
  if ($ARGV[0] =~ /^$CELL$/i) {
    $region = $CELL ;
    $region_log = $CELL ;
    $mapout = "$CELL.$mapin" ;
    $logfile = "log.$mapout.txt" ;
    print "\nOutput map will have $region as region.\n" ;
  } elsif ($ARGV[0] =~ /^$ASU$/i) {
    $region = $ASU ;
    $region_log = $ASU ;
    $mapout = "$ASU.$mapin" ;
    $logfile = "log.$mapout.txt" ;
    print "\nOutput map will have $region as region.\n" ;
  } elsif (-e $ARGV[0]) {
    $region = $ARGV[0] ;
    $pdbfile = $ARGV[0] ;
    $mapout = "$ARGV[0].$mapin" ;
    $logfile = "log.$mapout.txt" ;
    $region_log = $ARGV[0] . ", $border Angstrom border" ;
    print "\nOutput map will be defined by $region_log\n" ;
  }
}
if ($num_args == 6) {
  $x1 = $ARGV[0] ;
  $y1 = $ARGV[1] ;
  $z1 = $ARGV[2] ;
  $x2 = $ARGV[3] ;
  $y2 = $ARGV[4] ;
  $z2 = $ARGV[5] ;
  $region = sprintf("$x1 $x2 $y1 $y2 $z1 $z2",
                    %f7.2, %f7.2, %f7.2, %f7.2, %f7.2, %f7.2) ;
  $region_log = sprintf("($x1,$y1,$z1) and ($x2,$y2,$z2)",
                             %f7.2, %f7.2, %f7.2, %f7.2, %f7.2, %f7.2) ;
  $mapout = "BOX.$mapin" ;
  $logfile = "log.$mapout.txt" ;
  print "\nOutput map will be defined by the points:\n".
        "   $region_log\n\n" ;
}    
if (! $region) {
  print "\n\n" ;
  print "*************************************\n" ;
  print "** Region is not properly defined! **\n" ;
  print "*************************************\n\n" ;
  print_help() ;
  exit(0) ;
}

if ($outfile) {
  $mapout = $outfile ;
}

##############################################
# make sure we know where CCP4 bin is
##############################################
if (! $ENV{CCP4}) {
  print <<EOF ;

Could not find path to CCP4 suite.
Please set up CCP4 by:
  1. The usual way you do it, or
  2. Entering "CCP4" at the command prompt, or
  3. Entering "source /usr/programs/ccp4/CCP4.csh"
     at the command prompt, or
  4. Asking your system administrator to help you.

EOF
  exit ;
}


##################################
#    Make some room in mapman    #
##################################
$ENV{"MAPSIZE"} = 100000000 ;


#######################################
# print log file header
#######################################
open (LOGFILE, ">$logfile") or die "Unable to open $logfile\n" ;

print LOGFILE<<EOF ;
-----------------------------------------------------------------
File: $logfile
Log file for mapreg version $mapreg_version
  Region of $mapin defined by $region_log will be
  output to $mapout. Using symmetry $symm (ispcgr).
EOF
 
close LOGFILE ;

if (system("date>>$logfile")) {
  print "\nError accessing $logfile \n" ;
  exit ;
}

open (LOGFILE, ">>$logfile") ;

print LOGFILE "-----------------------------------------------------------------\n\n" ;

close LOGFILE ;


#######################################################
# convert non ccp4 map to ccp4 map in mapman               #
#######################################################
if (($maptype ne "ccp4") or ($maptype ne "CCP4")) {
  $ccp4map = "ccp4map.map" ;
  $cns2ccp4 = "mapman" .
              "<< EOF >> $logfile\n" .
              "read map $mapin $maptype\n" .
              "write map $ccp4map ccp4\n" .
              "quit\n" .
              "EOF\n" ;
  print "\nConverting to ccp4 map with mapman.\n" ;
  system($cns2ccp4) ;
} else {
  $ccp4map = $mapin ;
}


#######################################################
# Extending Map with ccp4 mapmask                     #
#######################################################
if ($cull) {
  $extended_map = "mapmask.map" ;
} else {
  $extended_map = $mapout ;
}

if ($pdbfile) {  # using a pdbfile to define region
  $extend = "mapmask MAPIN $ccp4map MAPOUT $extended_map ".
            "xyzin $pdbfile << EOF >> $logfile\n" .
            "BORDER $border\n".
            "SYMM $symm\n" .
            "MODE MAPIN\n" .
            "EOF\n" ;
} else {
  $extend = "mapmask MAPIN $ccp4map MAPOUT $extended_map" .
            "<< EOF >> $logfile\n" .
            "XYZLIM $region\n" .
            "SYMM $symm\n" .
            "MODE MAPIN\n" .
            "END\n" . 
            "EOF\n" ;
}
print "\nExtending map with mapmask.\n" ;
# print $extend ;
system($extend) ;


if ($cull) {

  print "\n********************************************\n" ;
  print "** culling...\n" ;
  print "********************************************\n" ;

  # get info from the extended map--critical info should be preserved
  $infoerase = "info.erase" ;
  $mapinfo = "mapman << EOF >> $infoerase\n" .
             "read map $extended_map ccp4\n" .
             "list map\n".
             "quit\nEOF\n" ;
  print "\nGleaning map info from $mapin.\n" ;
  system($mapinfo) ;

  open (INFO, $infoerase) or die "Couldn't open info file." ;

  while(1) {
    $line = <INFO> ;
    if (!$line) {
      last ;
    }
    if ($line =~ /^ Grid\s*=\s*(\d+\s+\d+\s+\d+)\s*$/) {
      $grid = $1 ;
      print "==> Found grid : $grid\n" ;
    }
    if ($line =~ /^ Cell\s*=\s*(\d+\.\d+\s+.*)$/) {
      if (!$cell) {
        $cell = $1 ;
        print "==> Found cell : $cell\n" ;
      }
    }
  } 

  close(INFO) ;

  # check a couple of critical settings before proceeding
  if (!$cell) {
    print("\n****************************************************\n") ;
    print("Couldn't determine cell. Use -h flag for more info.\n") ;
    print("****************************************************\n") ;
    exit(0) ;
  }
  if (!$symm) {
    print("\n**************************************************\n") ;
    print("Symmetry needed. Set the -h flag for more info.\n") ;
    print("**************************************************\n") ;
    exit(0) ;
  }

  # create the full culling map around the asu using the pdb
  $cullingmap = "cull.map" ;
  $junkpdb = "junk.pdb" ;
  $cullmask = "sfall " .
              "XYZIN $pdbfile " .
              "XYZOUT $junkpdb " .
              "MAPOUT $cullingmap << EOF >> $logfile\n" .
              "TITL making cull map from $pdbfile\n" .
              "MODE ATMMAP\n" .
              "CELL $cell\n" .
              "GRID $grid\n" .
              "SYMM $symm\n" .
              "END\nEOF\n" ;
  print "\nCreating the culling mask with sfall.\n" ;
  system($cullmask) ;

  #######################################################
  # extend/clip the cullmask with mapmask
  #######################################################
  $extended_mask = "cullmask.map" ;
  $extend = "mapmask " .
            "MAPIN $cullingmap " .
            "MAPOUT $extended_mask " .
            "XYZIN $pdbfile << EOF >> $logfile\n" .
            "BORDER $border\n".
            "SYMM $symm\n" .
            "MODE MAPIN\n" .
            "EOF\n" ;
  print "\nClipping the culling mask with mapmask.\n" ;
  system($extend) ;

               
  #######################################################
  # apply cullmask as delta function to the map
  #######################################################
  $cullmap = "mapman " .
             "<< EOF >> $logfile\n" .
               "read map1 $extended_mask ccp4\n" .
               "zero map1 $cull 100000000.0\n" .
               "change_value map1 0.0 100000000.0 1.0\n" .
               "list map1\n" .
               "read map2 $extended_map ccp4\n" .
               "operate map1 * map2\n" .
               "list map2\n" .
               "list map1\n" .
               "write map1 $mapout ccp4\n" .
               "quit\n" .
               "EOF\n" ;
  print "\nApplying culling to map region.\n" ;
  system($cullmap) ;
} # done with culling


#######################################################
# clean up
#######################################################
# system("rm $ccp4map $extended_map " .
#        "   $extended_mask $cullingmap $infoerase") ;
print "\nSucesss.\n\n" ;
exit(0) ;
