/*

gdpc - a program for visualising molecular dynamic simulations
Copyright (C) 2000 Jonas Frantz

    This file is part of gdpc.

    gdpc 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.

    gdpc 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


Authors email : jonas.frantz@helsinki.fi

*/

#include <gtk/gtk.h>
#include <stdio.h>
#include <math.h>
#include "parameters.h"

static double ic[3][3]={{1.0,0.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}};

double FrameTime=0;


/************************************************************************/
/* Transforms relative coordinates in input file to absolute coordinates*/
/* on the drawable pixmap.						*/
/************************************************************************/
gint transf_abs(double x, double xmin, double xmax, gint absxsize) 
{
double newx;

    newx=(x-xmin)/(xmax-xmin);
    return (gint) (newx*absxsize);
}


/************************************************************************/
/* This function does the actual drawing of the circles accordingly to	*/
/* mode.								*/
/************************************************************************/
void drawcircles(GdkPixmap *pixmap, GdkColor *colors, double xmin, double xmax,
		 double ymin, double ymax, double zmin, double zmax, gint absxsize,
		 gint absysize, gint mode, gint radius, gint vary,
		 struct xyzstruc *coords,gboolean usetypes, gint numtypes,
		 gint numatoms) 
{
gint x, y, c, i, rtmp;
GdkGC *gc;

    gc = gdk_gc_new (pixmap);

    for(i=0;i<numatoms;i++) {
	x = transf_abs(coords[i].xcoord,xmin,xmax,absxsize);
	y = transf_abs(coords[i].ycoord,ymin,ymax,absysize);
	if(coords[i].zcoord>=zmin && coords[i].zcoord<=zmax) {

	    if (usetypes) c = transf_abs(coords[i].atype,0,numtypes+1,NUMCOLORS);
	    else c = transf_abs(coords[i].zcoord,zmin,zmax,NUMCOLORS);

	    if (vary==1) {
		rtmp = (int)(radius*(0.5*(coords[i].zcoord-zmin)/(zmax-zmin))+0.5*radius);
	    }
	    else if (vary==2) {
		rtmp = (int)(radius*(0.5*(-coords[i].zcoord+zmax)/(zmax-zmin))+0.5*radius);
	    }
	    else rtmp = radius;

	    gdk_gc_set_foreground (gc, &colors[c+2]);
	    if(x>0 && y>0 && x<(absxsize-radius/2) && y<(absysize-radius/2)) {
		if (mode==0) {
		    gdk_draw_rectangle(pixmap,gc,TRUE,x-rtmp/2+xborder,
					(absysize-y)-rtmp/2+yborder,rtmp,rtmp);
		}
		else if (mode==1) {
		    gdk_draw_arc(pixmap,gc,TRUE,x-rtmp/2+xborder,
				(absysize-y)-rtmp/2+yborder,rtmp,rtmp,0,360*64);
		}
		else if (mode==2) {
		    gdk_gc_set_foreground (gc, &colors[c*8+2]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-rtmp/2+xborder,
				 (absysize-y)-rtmp/2+yborder,
				 rtmp,rtmp,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+3]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.453125*rtmp+xborder,
				 (absysize-y)-0.453125*rtmp+yborder,
				 rtmp*0.875,rtmp*0.875,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+4]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.40625*rtmp+xborder,
				 (absysize-y)-0.40625*rtmp+yborder,
				 rtmp*0.75,rtmp*0.75,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+5]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.359375*rtmp+xborder,
				 (absysize-y)-0.359375*rtmp+yborder,
				 rtmp*0.625,rtmp*0.625,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+6]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.3125*rtmp+xborder,
				 (absysize-y)-0.3125*rtmp+yborder,
				 rtmp*0.5,rtmp*0.5,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+7]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.265625*rtmp+xborder,
				 (absysize-y)-0.265625*rtmp+yborder,
				 rtmp*0.375,rtmp*0.375,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+8]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.2175*rtmp+xborder,
				 (absysize-y)-0.2175*rtmp+yborder,
				 rtmp*0.25,rtmp*0.25,0,360*64);
		    gdk_gc_set_foreground (gc, &colors[c*8+9]);
		    gdk_draw_arc(pixmap,gc,TRUE,x-0.155*rtmp+xborder,
				 (absysize-y)-0.155*rtmp+yborder,
				 rtmp*0.125,rtmp*0.125,0,360*64);
		}
	    }
	}
    }
    g_object_unref(gc);
}

									
/************************************************************************/
/* This function rotates the coordinates of the atoms, sorts them and	*/
/* calls the drawcircles to draw them.					*/
/************************************************************************/
void rotateatoms(struct DrawStruct DrawData)
{
gint i, j, numframe, numatoms;

double isin, icos, jsin, jcos, ksin, kcos;
double maxx, minx, maxy, miny, maxz, minz;
double imsin, imcos, jmsin, jmcos;
double ictmp[3];
double newic[3][3];

struct xyzstruc *newcoords;
struct xyzstruc *coords;
struct GlobalParams *params;

GdkPixmap *pixmap;
GdkColor *colors;

    pixmap = DrawData.pixmap;
    colors = DrawData.colors;
    params = DrawData.params;
    numframe = DrawData.NumFrame;

    coords = params->framedata[numframe];
    numatoms = params->frameanum[numframe];

    minx = 0.0;
    maxx = 0.0;
    miny = 0.0;
    maxy = 0.0;
    minz = 0.0;
    maxz = 0.0;
    isin = sin(params->iangle*(-PI/180.0));
    icos = cos(params->iangle*(-PI/180.0));
    jsin = sin(params->jangle*(PI/180.0));
    jcos = cos(params->jangle*(PI/180.0));
    ksin = sin(params->kangle*(-PI/180.0));
    kcos = cos(params->kangle*(-PI/180.0));
    imsin = sin(params->jmangle*(-PI/180.0));
    imcos = cos(params->jmangle*(-PI/180.0));
    jmsin = sin(params->imangle*(-PI/180.0));
    jmcos = cos(params->imangle*(-PI/180.0));

    if(params->erase) {
	cleardrawable(DrawData);
    }

    newcoords = (struct xyzstruc *) g_malloc(numatoms*sizeof(struct xyzstruc));

    for (i=0;i<3;i++) newic[0][i] = ic[0][i]*jcos*kcos + ic[1][i]*(-jcos*ksin) + ic[2][i]*jsin;
    for (i=0;i<3;i++) newic[1][i] = ic[0][i]*(isin*jsin*kcos + icos*ksin) + ic[1][i]*(-isin*jsin*ksin + icos*kcos)+ ic[2][i]*(-isin*jcos);
    for (i=0;i<3;i++) newic[2][i] = ic[0][i]*(-icos*jsin*kcos + isin*ksin) + ic[1][i]*(icos*jsin*ksin + isin*kcos) + ic[2][i]*icos*jcos;
    for (i=0;i<3;i++) for (j=0;j<3;j++) ic[i][j] = newic[i][j];

    for (i=0;i<3;i++) newic[0][i] = ic[0][i]*jmcos + ic[2][i]*jmsin;
    for (i=0;i<3;i++) newic[1][i] = ic[0][i]*imsin*jmsin + ic[1][i]*imcos + ic[2][i]*(-imsin*jmcos);
    for (i=0;i<3;i++) newic[2][i] = ic[0][i]*(-imcos*jmsin) + ic[1][i]*imsin + ic[2][i]*imcos*jmcos;
    for (i=0;i<3;i++) for (j=0;j<3;j++) ic[i][j] = newic[i][j];

    for(i=0;i<numatoms;i++) {
	newcoords[i].xcoord = ic[0][0]*coords[i].xcoord+ic[0][1]*
				coords[i].ycoord+ic[0][2]*coords[i].zcoord;
	newcoords[i].ycoord = ic[1][0]*coords[i].xcoord+ic[1][1]*
				coords[i].ycoord+ic[1][2]*coords[i].zcoord;
	newcoords[i].zcoord = ic[2][0]*coords[i].xcoord+ic[2][1]*
				coords[i].ycoord+ic[2][2]*coords[i].zcoord;
	newcoords[i].atype = coords[i].atype;
	newcoords[i].index = i;
        if (newcoords[i].xcoord>maxx) maxx = newcoords[i].xcoord;
        if (newcoords[i].ycoord>maxy) maxy = newcoords[i].ycoord;
        if (newcoords[i].zcoord>maxz) maxz = newcoords[i].zcoord;
        if (newcoords[i].xcoord<minx) minx = newcoords[i].xcoord;
        if (newcoords[i].ycoord<miny) miny = newcoords[i].ycoord;
        if (newcoords[i].zcoord<minz) minz = newcoords[i].zcoord;
    }

    params->iangle = 0.0;
    params->jangle = 0.0;
    params->kangle = 0.0;
    params->imangle = 0.0;
    params->jmangle = 0.0;

    if (ic[0][0]!=0.0) params->zc = atan(ic[0][1]/ic[0][0])*(180.0/PI);
    else params->zc = 0.0;
    if (ic[0][0]<0.0 && ic[0][1]>0.0) params->zc+=180;
    else if (ic[0][0]<0.0 && ic[0][1]<0.0) params->zc+=180;
    else if (ic[0][0]>0.0 && ic[0][1]<0.0) params->zc+=360;
    
    ictmp[0]=ic[2][0]*cos(-params->zc*(PI/180.0))-ic[2][1]*sin(-params->zc*(PI/180.0));

    if (ic[2][2]!=0.0) params->yc = atan(-ictmp[0]/ic[2][2])*(180.0/PI);
    else params->yc = 0.0;
    if (ic[2][2]<0.0 && ictmp[0]>0.0) params->yc+=180;
    else if (ic[2][2]<0.0 && ictmp[0]<0.0) params->yc+=180;
    else if (ic[2][2]>0.0 && ictmp[0]<0.0) params->yc+=360;

    ictmp[0]=ic[1][0]*cos(-params->zc*(PI/180.0))-ic[1][1]*sin(-params->zc*(PI/180.0));
    ictmp[1]=ic[1][0]*sin(-params->zc*(PI/180.0))+ic[1][1]*cos(-params->zc*(PI/180.0));
    ictmp[2]=ictmp[0]*sin(-params->yc*(PI/180.0))+ic[1][2]*cos(-params->yc*(PI/180.0));

    if (ictmp[1]!=0.0) params->xc = atan(ictmp[2]/ictmp[1])*(180.0/PI);
    else params->xc = 0.0;
    if (ictmp[1]<0.0 && ictmp[2]>0.0) params->xc+=180;
    else if (ictmp[1]<0.0 && ictmp[2]<0.0) params->xc+=180;
    else if (ictmp[1]>0.0 && ictmp[2]<0.0) params->xc+=360;

    if (params->xc<=0.0) params->xc+=360.0;
    if (params->xc>=360.0) params->xc-=360.0;
    if (params->yc<=0.0) params->yc+=360.0;
    if (params->yc>=360.0) params->yc-=360.0;
    if (params->zc<=0.0) params->zc+=360.0;
    if (params->zc>=360.0) params->zc-=360.0;

    if (params->sort==2) {
	sortatoms(newcoords,0,numatoms-1,FALSE);	    
    }
    else sortatoms(newcoords,0,numatoms-1,TRUE);	    

    for (i=0;i<numatoms;i++) {
	newcoords[i].zcoord = coords[newcoords[i].index].zcoord;
    }

    if(params->xmin==65535.0) {
	params->xmax2[numframe] = maxx;
	params->xmin2[numframe] = minx;
    }
    else {
	params->xmax2[numframe] = params->xmax;
	params->xmin2[numframe] = params->xmin;
    }
    if(params->ymin==65535.0) {
	params->ymax2[numframe] = maxy;
	params->ymin2[numframe] = miny;
    }
    else {
	params->ymax2[numframe] = params->ymax;
	params->ymin2[numframe] = params->ymin;
    }
    if(params->zmin==65535.0) {
	params->zmax2[numframe] = maxz;
	params->zmin2[numframe] = minz;
    }
    else {
	params->zmax2[numframe] = params->zmax;
	params->zmin2[numframe] = params->zmin;
    }

    drawcircles(pixmap,colors, params->xmin2[numframe],
		params->xmax2[numframe], params->ymin2[numframe],
		params->ymax2[numframe], params->zmin2[numframe],
		params->zmax2[numframe], params->absxsize,
		params->absysize, params->mode,params->radius, params->vary,
		newcoords, params->usetypes, params->numtypes, numatoms);

    FrameTime = params->atime[numframe];
    g_free(newcoords);
}


/************************************************************************/
/* Clears the drawable area and draws the rectangle which represents	*/
/* border of the simulationbox.						*/
/************************************************************************/
void cleardrawable(struct DrawStruct DrawData) 
{
GdkGC *gc;
gint bgcol, fgcol;

    if (DrawData.params->whitebg) {
	bgcol = 1;
	fgcol = 0;
    }else {
	bgcol = 0;
	fgcol = 1;
    }

    gc = gdk_gc_new (DrawData.pixmap);
    gdk_gc_set_foreground (gc, &DrawData.colors[bgcol]);
    gdk_draw_rectangle (DrawData.pixmap, gc, TRUE, 0, 0,
			DrawData.params->absxsize+2*xborder,
			DrawData.params->absysize+2*yborder);
    gdk_gc_set_foreground (gc, &DrawData.colors[fgcol]);
    gdk_draw_rectangle (DrawData.pixmap, gc, TRUE, xborder-2, yborder-2,
			DrawData.params->absxsize+4,
			DrawData.params->absysize+4);
    gdk_gc_set_foreground (gc, &DrawData.colors[bgcol]);
    gdk_draw_rectangle (DrawData.pixmap, gc, TRUE, xborder, yborder,
			DrawData.params->absxsize,
			DrawData.params->absysize);
    g_object_unref(gc);
}


/************************************************************************/
/* This function resets the orientation of the system by reseting the 	*/
/* vector that handles the rotation.					*/
/************************************************************************/
void resetic() 
{
    ic[0][0] = 1.0; ic[0][1] = 0.0; ic[0][2] = 0.0;
    ic[1][0] = 0.0; ic[1][1] = 1.0; ic[1][2] = 0.0;
    ic[2][0] = 0.0; ic[2][1] = 0.0; ic[2][2] = 1.0;
}
