from __future__ import absolute_import, division, print_function
import libtbx.load_env
import os

import six
from six.moves import range


output_fname = "rama8000_tables.h"
path_to_files = libtbx.env.find_in_repositories(
    os.path.join("chem_data", "rotarama_data"))
file_names = {
  'general':  "rama8000-general-noGPIVpreP.data",
  'glycine':  "rama8000-gly-sym.data",
  'cis_pro':  "rama8000-cispro.data",
  'trans_pro':"rama8000-transpro.data",
  'pre_pro':  "rama8000-prepro-noGP.data",
  'ile_val':  "rama8000-ileval-nopreP.data",
}

template = """
/*
This file contains tables for Ramachandran evaluation.
This file is generated by the following procedure, don't edit it manually
and keep consistent with the template there:
mmtbx/validation/ramachandran/convert_from_test.py
*/
#include <vector>
#include <scitbx/array_family/shared.h>
#include <scitbx/array_family/versa.h>
#include <scitbx/array_family/accessors/c_grid.h>


namespace mmtbx {{ namespace validation {{ namespace ramachandran {{

#define RAMA_GENERAL  0
#define RAMA_GLYCINE  1
#define RAMA_CISPRO   2
#define RAMA_TRANSPRO 3
#define RAMA_PREPRO   4
#define RAMA_ILE_VAL  5

#define RAMALYZE_OUTLIER  0
#define RAMALYZE_ALLOWED  1
#define RAMALYZE_FAVORED  2
#define RAMALYZE_ANY      3
#define RAMALYZE_NOT_FAVORED 4

  const std::string res_types[] = {{"general", "glycine", "cis-proline",
    "trans-proline", "pre-proline", "isoleucine or valine"}};

  const double linear_table_general[] = {{{general}}};
  const double linear_table_glycine[] = {{{glycine}}};
  const double linear_table_cis_pro[] = {{{cis_pro}}};
  const double linear_table_trans_pro[] = {{{trans_pro}}};
  const double linear_table_pre_pro[] = {{{pre_pro}}};
  const double linear_table_ile_val[] = {{{ile_val}}};

  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_general(linear_table_general,    180,180);
  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_glycine(linear_table_glycine,    180,180);
  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_cis_pro(linear_table_cis_pro,    180,180);
  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_trans_pro(linear_table_trans_pro,180,180);
  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_pre_pro(linear_table_pre_pro,    180,180);
  scitbx::af::const_ref<double, scitbx::af::c_grid<2> > table_ile_val(linear_table_ile_val,    180,180);


}}}}}} // namespace mmtbx::validation::ramachandran
"""


def array_2d_to_string(arr):
  result = ""
  cur_str_len = 0
  for row in arr:
    for num in row:
      str_num = "{:.6g},".format(num)
      result += str_num
      cur_str_len += len(str_num)
      if cur_str_len > 100:
        result += "\n"
        cur_str_len = 0
  return result

def read_file_and_fill_arr(file_descr, arr):
  for line in file_descr.readlines():
    if line.startswith('#'): continue
    phi_str, psi_str, value_str = line.split()
    phi = int(float(phi_str))
    psi = int(float(psi_str))
    value = float(value_str)
    arr[(phi+179)//2][(psi+179)//2] = value

def run():
  dict_with_all_values = {}
  for key in file_names.keys():
    dict_with_all_values[key] = [[0 for x in range(180)] for y in range(180)]

  # reading. Not much error-handling, because if we have a error, we should fix
  # it here and not produce corrupted .h file

  for key, fname in six.iteritems(file_names):
    print("converting file", os.path.join(path_to_files, fname))
    inp_f = open(os.path.join(path_to_files, fname), 'r')
    read_file_and_fill_arr(inp_f, dict_with_all_values[key])
    inp_f.close()

  out_f = open(output_fname, 'w')
  out_f.write(template.format(
      general=array_2d_to_string(dict_with_all_values['general']),
      glycine=array_2d_to_string(dict_with_all_values['glycine']),
      cis_pro=array_2d_to_string(dict_with_all_values['cis_pro']),
      trans_pro=array_2d_to_string(dict_with_all_values['trans_pro']),
      pre_pro=array_2d_to_string(dict_with_all_values['pre_pro']),
      ile_val=array_2d_to_string(dict_with_all_values['ile_val']))
  )
  out_f.close()

if __name__ == '__main__':
  run()
