/*

Copyright (C) 2001, 2003, 2005 Matthew P. Hodges
This file is part of XMakemol.

XMakemol 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, or (at your option)
any later version.

XMakemol 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 XMakemol; see the file COPYING.  If not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <math.h>
#include <stdio.h>
#include <Xm/Xm.h>              /* Needed for globals.h */

#include "globals.h"
#include "bonds.h"
#include "defs.h"
#include "draw.h"
#include "view.h"

#define FIG_DEPTH_RANGE (995)
#define FIG_SCALE (10)
#define FIG_USER_COLOR_OFFSET (32)

static int use_colour;

static double max_depth, min_depth;
static FILE *output;

void convert_to_canvas_coords (double *, double *, Boolean);
double get_atom_radius (int);
double get_h_bond_width (int, int);
int get_no_dashes (void);
double get_z_depth (void);
void update_canvas_bond_points (int, int, Boolean, double);
void update_canvas_hbond_points (int, int, Boolean);

void write_fig_atom (int);
void write_fig_bond (int, int);
void write_fig_hbond (int, int);
void write_fig_preamble (void);

void
write_fig_file (char *filename, Boolean with_colour)
{
  int i, si, sj;

  struct node *ptr;

  use_colour = with_colour;

  output = fopen (filename, "w");

  write_fig_preamble ();

  /* Get min/max depths */

  max_depth = -1e20; min_depth = 1e20;

  for (i = 0; i < no_atoms; i++)
    {
      if (atoms[i].z < min_depth) min_depth = atoms[i].z;
      if (atoms[i].z > max_depth) max_depth = atoms[i].z;
    }

  /* Loop over atoms */

  depth = get_z_depth ();

  for (i = 0; i < no_atoms; i++)
    {
      si = sorted_atoms[i];

      if (atom_flag
          && ((! depth_is_on) || (depth - atoms[si].z > Z_TOL))
          && atoms[si].visi)
        {
          write_fig_atom (si);
        }

      /* Loop over bonds */

      if (bond_flag)
        {
          ptr = bond_adjacency_list[si];

          while (ptr != NULL)
            {
              sj = (ptr->v);
            
              if (((! depth_is_on)
                   || (((depth - atoms[si].z > Z_TOL)
                        && (depth - atoms[sj].z > Z_TOL))))
                  && atoms[si].visi
                  && atoms[sj].visi
                  && (atoms[si].z <= atoms[sj].z))
                {
                  write_fig_bond (si, sj);
                }
              ptr = ptr->next;
            }
        }

      /* Loop over H-bonds */

      if (any_hydrogen && any_hbondpartner && hbond_flag)
        {
          ptr = hbond_adjacency_list[si];
        
          while (ptr != NULL)
            {
              sj = (ptr->v);
            
              if (((! depth_is_on)
                   || (((depth - atoms[si].z > Z_TOL)
                        && (depth - atoms[sj].z > Z_TOL))))
                  && atoms[si].visi
                  && atoms[sj].visi
                  && (atoms[si].z <= atoms[sj].z))
                {
                  write_fig_hbond (si, sj);
                }
              ptr = ptr->next;
            }
        }
    }

  fclose (output);
}

void
write_fig_preamble ()
{
  int i, index = FIG_USER_COLOR_OFFSET;

  fprintf (output, "#FIG 3.2\n");
  fprintf (output, "Landscape\n"); /* orientation */
  fprintf (output, "Center\n"); /* justification */
  fprintf (output, "Metric\n"); /* units */
  fprintf (output, "A4\n");     /* papersize */
  fprintf (output, "100.0\n");  /* export/print magnification  */
  fprintf (output, "Single\n"); /* single page */
  fprintf (output, "-2\n");     /* no transparency */
  fprintf (output, "# Generated by XMakemol\n"); /* comment */
  fprintf (output, "1200 1\n");  /* resolution/origin at lower left
                                   corner */

  if (use_colour)
    {
      for (i = 0; i < no_atom_types; i++)
        {
          fprintf (output, "%d %d #%02X%02X%02X\n",
                   0,           /* object code (color) */
                   index,       /* color number */
                   element[atom_types[i]].red   / 256,
                   element[atom_types[i]].green / 256,
                   element[atom_types[i]].blue  / 256);

          index++;
        }
    }
}

void
write_fig_atom (int si)
{
  int color;

  char label[4] = "", number[8] = "", text[12] = "";

  double current_depth;
  double radius;
  double xyz_in[3], xy_out[2];

  radius = get_atom_radius (si);
          
  xyz_in[0] = atoms[si].x;
  xyz_in[1] = atoms[si].y;
  xyz_in[2] = atoms[si].z;

  convert_to_canvas_coords (xyz_in, xy_out, 0);

  /* Calculate depth */

  current_depth =
    2 + FIG_DEPTH_RANGE * (1.0 - ((atoms[si].z - min_depth) /
                                  (max_depth - min_depth)));

  /* Set the colour */

  if (use_colour)
    {
      color = FIG_USER_COLOR_OFFSET + atoms[si].type;
    }
  else
    {
      color = 7;                  /* white */
    }

  fprintf (output, "%d %d %d %d %d %d %d %d %d %3.1f %d %3.1f %d %d %d %d %d %d %d %d\n",
           1,                   /* object_code (ellipse) */
           3,               /* sub_type (ellipse defined by radius) */
           0,                   /* line_style (solid) */
           1,                   /* line thickness */
           0,                   /* pen color */
           color,               /* fill color */
           (int) current_depth, /* depth */
           -1,                  /* pen style (not used) */
           20,                  /* area fill (full saturation) */
           0.0,                 /* style val (dash length) */
           0,                   /* direction (clockwise) */
           0.0,                 /* angle (full circle) */
           (int) (xy_out[0] * FIG_SCALE), /* center_x */
           (int) (xy_out[1] * FIG_SCALE), /* center_y */
           (int) (radius * FIG_SCALE), /* radius_x */
           (int) (radius * FIG_SCALE), /* radius_y */
           (int) (xy_out[0] * FIG_SCALE), /* start_x */
           (int) (xy_out[1] * FIG_SCALE), /* start_y */
           (int) ((xy_out[0] + radius) * FIG_SCALE), /* end_x */
           (int) (xy_out[1] * FIG_SCALE)
           );

  /* Write labels and or symbols */

  if (at_nos_flag)
    {
      sprintf (number, "%d ", si + 1);
    }

  if (at_sym_flag)
    {
      sprintf (label, "%s", atoms[si].label);
    }

  sprintf (text, "%s%s\\001", number, label);

  if (strlen (text) > 0)
    {
      fprintf (output, "%d %d %d %d %d %d %d %f %d %f %f %d %d %s\n",
               4,               /* object code (text) */
               1,               /* center justified */
               0,               /* color (black) */
               (int) current_depth - 1, /* depth */
               -1,              /* pen style (not used) */
               0,               /* font (default PostScript) */
               12,              /* font size in points */
               0.0,             /* angle of text */
               1,               /* font flags */
               0.0,             /* height */
               0.0,             /* length */
               (int) (xy_out[0] * FIG_SCALE), /* x */
               (int) (xy_out[1] * FIG_SCALE), /* y */
               text);
    }
}

void
write_fig_bond (int si, int sj)
{
  int color;

  double current_depth;

  update_canvas_bond_points (si, sj, 0, 0.0);
                        
  current_depth =
    2 + FIG_DEPTH_RANGE * (1.0 - ((atoms[sj].z - min_depth) /
                                  (max_depth - min_depth)));

  if (use_colour)
    {
      /* Draw polygon 0-1-4-5 (see diagram in draw.c) */

      color = FIG_USER_COLOR_OFFSET + atoms[si].type;

      fprintf (output, "%d %d %d %d %d %d %d %d %d %3.1f %d %d %d %d %d %d\n\t%d %d %d %d %d %d %d %d %d %d\n",
               2,               /* object_code (polyline) */
               1,               /* sub_type (polyline) */
               0,               /* line_style (solid) */
               1,               /* line_thickness */
               0,               /* pen color */
               color,           /* fill color */
               (int) current_depth + 1, /* depth */
               -1,              /* pen style (not used) */
               20,              /* area fill (full saturation) */
               0.0,             /* style val (dash length) */
               0,               /* join style (not used?) */
               0,               /* cap style (not used?) */
               -1,              /* radius */
               0,               /* forward arrow (off) */
               0,               /* backward arrow (off) */
               5,               /* number of points */
               (int) (canvas_bond_points[0].x * FIG_SCALE),
               (int) (canvas_bond_points[0].y * FIG_SCALE),
               (int) (canvas_bond_points[1].x * FIG_SCALE),
               (int) (canvas_bond_points[1].y * FIG_SCALE),
               (int) (canvas_bond_points[4].x * FIG_SCALE),
               (int) (canvas_bond_points[4].y * FIG_SCALE),
               (int) (canvas_bond_points[5].x * FIG_SCALE),
               (int) (canvas_bond_points[5].y * FIG_SCALE),
               (int) (canvas_bond_points[0].x * FIG_SCALE),
               (int) (canvas_bond_points[0].y * FIG_SCALE)
               );

      /* Draw polygon 1-2-3-4 (see diagram in draw.c) */

      color = FIG_USER_COLOR_OFFSET + atoms[sj].type;

      fprintf (output, "%d %d %d %d %d %d %d %d %d %3.1f %d %d %d %d %d %d\n\t%d %d %d %d %d %d %d %d %d %d\n",
               2,               /* object_code (polyline) */
               1,               /* sub_type (polyline) */
               0,               /* line_style (solid) */
               1,               /* line_thickness */
               0,               /* pen color */
               color,           /* fill color */
               (int) current_depth + 1, /* depth */
               -1,              /* pen style (not used) */
               20,              /* area fill (full saturation) */
               0.0,             /* style val (dash length) */
               0,               /* join style (not used?) */
               0,               /* cap style (not used?) */
               -1,              /* radius */
               0,               /* forward arrow (off) */
               0,               /* backward arrow (off) */
               5,               /* number of points */
               (int) (canvas_bond_points[1].x * FIG_SCALE),
               (int) (canvas_bond_points[1].y * FIG_SCALE),
               (int) (canvas_bond_points[2].x * FIG_SCALE),
               (int) (canvas_bond_points[2].y * FIG_SCALE),
               (int) (canvas_bond_points[3].x * FIG_SCALE),
               (int) (canvas_bond_points[3].y * FIG_SCALE),
               (int) (canvas_bond_points[4].x * FIG_SCALE),
               (int) (canvas_bond_points[4].y * FIG_SCALE),
               (int) (canvas_bond_points[1].x * FIG_SCALE),
               (int) (canvas_bond_points[1].y * FIG_SCALE)
               );
    }
  else
    {
      /* Draw polygon 0-2-3-5 (see diagram in draw.c) */

      fprintf (output, "%d %d %d %d %d %d %d %d %d %3.1f %d %d %d %d %d %d\n\t%d %d %d %d %d %d %d %d %d %d\n",
               2,               /* object_code (polyline) */
               1,               /* sub_type (polyline) */
               0,               /* line_style (solid) */
               1,               /* line_thickness */
               0,               /* pen color */
               7,               /* fill color (white) */
               (int) current_depth + 1, /* depth */
               -1,              /* pen style (not used) */
               20,              /* area fill (full saturation) */
               0.0,             /* style val (dash length) */
               0,               /* join style (not used?) */
               0,               /* cap style (not used?) */
               -1,              /* radius */
               0,               /* forward arrow (off) */
               0,               /* backward arrow (off) */
               5,               /* number of points */
               (int) (canvas_bond_points[0].x * FIG_SCALE),
               (int) (canvas_bond_points[0].y * FIG_SCALE),
               (int) (canvas_bond_points[2].x * FIG_SCALE),
               (int) (canvas_bond_points[2].y * FIG_SCALE),
               (int) (canvas_bond_points[3].x * FIG_SCALE),
               (int) (canvas_bond_points[3].y * FIG_SCALE),
               (int) (canvas_bond_points[5].x * FIG_SCALE),
               (int) (canvas_bond_points[5].y * FIG_SCALE),
               (int) (canvas_bond_points[0].x * FIG_SCALE),
               (int) (canvas_bond_points[0].y * FIG_SCALE)
               );
    }
}

void
write_fig_hbond (int si, int sj)
{
  double current_depth, current_width;
  double hbond_length, dash_length;
  double x, y;

  update_canvas_hbond_points (si, sj, 0);

  x = canvas_hbond_points[0].x - canvas_hbond_points[1].x;
  y = canvas_hbond_points[0].y - canvas_hbond_points[1].y;

  hbond_length = sqrt ((x * x) + (y * y));

  dash_length =
    (int) (hbond_length / (2 * get_no_dashes() - 1));

  dash_length *= 2.0 / 3.0;     /* Why */

  /* Draw polygon 0-2-3-5 (see diagram in draw.c) */

  current_depth =
    2 + FIG_DEPTH_RANGE * (1.0 - ((atoms[sj].z - min_depth) /
                                  (max_depth - min_depth)));

  current_width = get_h_bond_width (si, sj);

  current_width *= 2.0 / 3.0;         /* Why? */

  fprintf (output, "%d %d %d %d %d %d %d %d %d %3.1f %d %d %d %d %d %d\n\t%d %d %d %d\n",
           2,                   /* object_code (polyline) */
           1,                   /* sub_type (polyline) */
           1,                   /* line_style (dashed) */
           (int) current_width, /* line_thickness */
           0,                   /* pen color */
           7,                   /* fill color (white) */
           (int) current_depth + 1, /* depth */
           -1,                  /* pen style (not used) */
           20,                  /* area fill (full saturation) */
           dash_length,         /* style val (dash length) */
           0,                   /* join style (not used?) */
           0,                   /* cap style (not used?) */
           -1,                  /* radius */
           0,                   /* forward arrow (off) */
           0,                   /* backward arrow (off) */
           2,                   /* number of points */
           (int) (canvas_hbond_points[0].x * FIG_SCALE),
           (int) (canvas_hbond_points[0].y * FIG_SCALE),
           (int) (canvas_hbond_points[1].x * FIG_SCALE),
           (int) (canvas_hbond_points[1].y * FIG_SCALE)
           );
}
