/*
Copyright (C) 2003 by Sean David Fleming

sean@ivec.org

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

The GNU GPL can also be found at http://www.gnu.org
*/

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "gdis.h"
#include "coords.h"
#include "model.h"
#include "file.h"
#include "parse.h"
#include "matrix.h"
#include "interface.h"

#define DEBUG_MORE 0
#define MAX_KEYS 15

/* main structures */
extern struct sysenv_pak sysenv;
extern struct elem_pak elements[];

/****************************/
/* write a coordinate block */
/****************************/
gint write_arc_frame(FILE *fp, struct model_pak *model)
{
gint m, flag;
gdouble x[3];
GSList *clist, *mlist;
struct core_pak *core;
struct mol_pak *mol;
time_t t1;

/* get & print the date (streamlined slightly by sxm) */
t1 = time(NULL);
fprintf(fp,"!DATE %s",ctime(&t1));

if (model->periodic)
  {
  if (model->periodic == 2)
    {
/* saving a surface as a 3D model - make depth large enough to fit everything */
    fprintf(fp,"PBC %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f (P1)\n",
                model->pbc[0], model->pbc[1], 2.0*model->rmax,
                180.0*model->pbc[3]/PI,
                180.0*model->pbc[4]/PI,
                180.0*model->pbc[5]/PI);
    }
  else
    {
    fprintf(fp,"PBC %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f (P1)\n",
                model->pbc[0], model->pbc[1], model->pbc[2],
                180.0*model->pbc[3]/PI,
                180.0*model->pbc[4]/PI,
                180.0*model->pbc[5]/PI);
    }
  }

m=0;
for (mlist=model->moles ; mlist ; mlist=g_slist_next(mlist))
  {
  mol = mlist->data;

/* flag - atom written to file? (ie if UNDELETED) */
  flag=0;
/* m - number of atoms in molecule */
  for (clist=mol->cores ; clist ; clist=g_slist_next(clist))
    {
    core = clist->data;
    if (core->status & DELETED)
      continue;

/* everything is cartesian after latmat mult */
    ARR3SET(x, core->x);
    vecmat(model->latmat, x);
/* unique molecule numbering for BIOSYM files (>0) */
    if (core->atom_type)
      {
      fprintf(fp, "%-4s  %14.9f %14.9f %14.9f CORE %-6d %-7s %-2s ",
                  core->atom_label,x[0],x[1],x[2],m+1, core->atom_type,
                  elements[core->atom_code].symbol);
      }
    else
      {
      fprintf(fp, "%-4s  %14.9f %14.9f %14.9f CORE %-6d ?       %-2s ",
                  core->atom_label,x[0],x[1],x[2],m+1, 
                  elements[core->atom_code].symbol);
      }

  if (core->charge < 0.0)
    fprintf(fp, "%5.3f\n", core->charge);
  else
    fprintf(fp, " %4.3f\n", core->charge);

/* indicate we've written at least one atom */
    flag++;
    }
/* if at least one atom in the molecule was written - inc mol numbering */
  if (flag)
    {
    fprintf(fp,"end\n");
    m++;
    }
  }
fprintf(fp,"end\n");

return(0);
}

/***********************/
/* write biosym header */
/***********************/
gint write_arc_header(FILE *fp, struct model_pak *model)
{
/* print header */
fprintf(fp,"!BIOSYM archive 3\n");
if (model->periodic)
  fprintf(fp,"PBC=ON\n");
else
  fprintf(fp,"PBC=OFF\n");
gdis_blurb(fp);

return(0);
}

/******************/
/* Biosym writing */
/******************/
gint write_arc(gchar *filename, struct model_pak *data)
{
FILE *fp;

/* checks */
g_return_val_if_fail(data != NULL, 1);
g_return_val_if_fail(filename != NULL, 2);

/* open the file */
fp = fopen(filename, "wt");
if (!fp)
  return(3);

/* TODO - write multiple frames if it has them */
write_arc_header(fp, data);
write_arc_frame(fp, data);

fclose(fp);
return(0);
}

/****************************************************************/
/* determine if a BIOSYM fragment label was generated by MARVIN */
/****************************************************************/
gint is_marvin_label(const gchar *label)
{
if (label[0] != 'R' && label[0] != 'r')
  return(FALSE);

if (!g_ascii_isdigit(label[1]))
  return(FALSE);

if (label[2] != 'A' && label[2] != 'a' && label[2] != 'B' && label[2] != 'b')
  return(FALSE);

if (label[3] != 'C' && label[3] != 'c' && label[3] != 'S' && label[3] != 's')
  return(FALSE);

return(TRUE);
}

gint marvin_region(const gchar *label)
{
return(label[1] - '1');
}

gint marvin_core(const gchar *label)
{
if (label[3] == 'C' || label[3] == 'c')
  return(TRUE);
return(FALSE);
}

/*************************************************/
/* read a car/arc block into the model structure */
/*************************************************/
/* NB: assumes fp is at a !DATE line */
void read_arc_block(FILE *fp, struct model_pak *data)
{
gint region, core_flag, end_count, num_tokens;
gchar **buff;
GSList *clist, *slist;
struct core_pak *core;
struct shel_pak *shel;

g_assert(fp != NULL);
g_assert(data != NULL);

/* FIXME - this breaks measurement updates */
/*
free_core_list(data);
*/

/* init */
clist = data->cores;
slist = data->shels;
end_count=0;

/* loop while there's data */
for (;;)
  {
  buff = get_tokenized_line(fp, &num_tokens);
  if (!buff)
    break;

/* increment/reset end counter according to input line */
  if (g_ascii_strncasecmp("end", *buff, 3) == 0)
    end_count++;
  else
    end_count = 0;

/* skip single end, terminate on double end */
  if (end_count == 1)
    {
    g_strfreev(buff);
    continue;
    }
  if (end_count == 2)
    break;

/* cell dimension search */
  if (g_ascii_strncasecmp("pbc", *buff, 3) == 0)
    {
    if (num_tokens > 6)
      {
      data->pbc[0] = str_to_float(*(buff+1));
      data->pbc[1] = str_to_float(*(buff+2));
      data->pbc[2] = str_to_float(*(buff+3));
      data->pbc[3] = PI*str_to_float(*(buff+4))/180.0;
      data->pbc[4] = PI*str_to_float(*(buff+5))/180.0;
      data->pbc[5] = PI*str_to_float(*(buff+6))/180.0;
/* implicit 2D */
      if (data->pbc[2] == 0)
        {
        data->pbc[2] = 1.0;
        data->pbc[3] = 0.5*PI;
        data->pbc[4] = 0.5*PI;
        data->periodic = 2;
        }
      }
    else if (num_tokens > 3)
      {
      data->pbc[0] = str_to_float(*(buff+1));
      data->pbc[1] = str_to_float(*(buff+2));
      data->pbc[5] = PI*str_to_float(*(buff+3))/180.0;
      }

    g_strfreev(buff);
    continue;
    }

/* process a single coord line */
  if (num_tokens < 8) 
    {
    gui_text_show(ERROR, "unexpected end of file reading arc file\n");
    g_strfreev(buff);
    return;
    }

/* process core/shell candidates */
  if (num_tokens > 8)
  if (elem_symbol_test(*(buff+7)))
    {
    core_flag=TRUE;
    region = 0;

/* MARVIN core/shell/region parse */ 
    if (is_marvin_label(*(buff+4)))
      {
      if (!marvin_core(*(buff+4)))
        core_flag = FALSE;
      region = marvin_region(*(buff+4));
      data->region_empty[region] = FALSE;
      }

/* overwrite existing core (if any) */
    if (core_flag)
      {
      if (clist)
        {
        core = clist->data;
        clist = g_slist_next(clist);
        }
      else
        {
/* otherwise, add a new core */
/* NB: must append (not prepend) since we may overwrite & the order must be the same */
/* TODO - we could prepend cores (it's faster) and then reverse the list at the end */
/* but it's more complicated as we should ONLY reverse newly added cores */
        core = new_core(*(buff+7), data);
        data->cores = g_slist_append(data->cores, core);
        }

/* set the proper label */
g_free(core->atom_label);
core->atom_label = g_strdup(*buff);

/* set values */
      core->x[0] = str_to_float(*(buff+1));
      core->x[1] = str_to_float(*(buff+2));
      core->x[2] = str_to_float(*(buff+3));
      core->charge = str_to_float(*(buff+8));

g_free(core->atom_type);
core->atom_type = g_strdup(*(buff+6));

      core->region = region;
      core->lookup_charge = FALSE;
      }
    else
      {
/* overwrite existing shell (if any) */
      if (slist)
        {
        shel = slist->data;
        slist = g_slist_next(slist);
        }
      else
        {
/* otherwise, add a new shell */
        shel = new_shell(*(buff+7), data);
        data->shels = g_slist_append(data->shels, shel);
        }

/* set the proper label */
g_free(shel->shell_label);
shel->shell_label = g_strdup(*buff);

/* set values */
      shel->x[0] = str_to_float(*(buff+1));
      shel->x[1] = str_to_float(*(buff+2));
      shel->x[2] = str_to_float(*(buff+3));
      shel->charge = str_to_float(*(buff+8));
      shel->region = region;
      shel->lookup_charge = FALSE;
      }
    }
  g_strfreev(buff);
  }

/* convert input cartesian coords to fractional */
if( data->periodic)
  {
  matrix_lattice_init(data);
  coords_make_fractional(data);
  }
data->fractional = TRUE;

g_strfreev(buff);
}

/*********************/
/* biosym frame read */
/*********************/
gint read_arc_frame(FILE *fp, struct model_pak *data)
{
/* frame overwrite */
read_arc_block(fp, data);
return(0);
}

/******************/
/* Biosym reading */
/******************/
gint read_arc(gchar *filename, struct model_pak *data)
{
gint frame=0, num_tokens;
gchar **buff;
FILE *fp;

/* checks */
g_return_val_if_fail(data != NULL, 1);
g_return_val_if_fail(filename != NULL, 2);

/* NEW - under windows, must be "rb" instead of "rt" - even though it's a text file */
fp = fopen(filename, "rb");
if (!fp)
  return(3);

/* loop while there's data */
for (;;)
  {
  buff = get_tokenized_line(fp, &num_tokens);
  if (!buff)
    {
    g_strfreev(buff);
    break;
    }

/* pbc on/off search */
  if (g_ascii_strncasecmp("pbc=on", *buff, 6) == 0)
    data->periodic = 3;
  else if (g_ascii_strncasecmp("pbc=off", *buff, 7) == 0)
    data->periodic = 0;
  else if (g_ascii_strncasecmp("pbc=2d", *buff, 6) == 0)
    data->periodic = 2;

/* coords search */
  if (g_ascii_strncasecmp("!date", *buff, 5) == 0)
    {
/* NEW */
    add_frame_offset(fp, data);

/* only load the required frame */
    if (frame == data->cur_frame)
      read_arc_block(fp, data);

/* increment counter */
    frame++;
    }
  g_strfreev(buff);
  }

/* got everything */
data->num_frames = frame;

/* get rid of frame list if only one frame */
if (data->num_frames == 1)
  {
  free_list(data->frame_list);
  data->frame_list = NULL;
  }

/* model setup */
strcpy(data->filename, filename);
g_free(data->basename);
data->basename = parse_strip(filename);

model_prep(data);

return(0);
}

