#ifndef PARTICLE_H
#define PARTICLE_H

#include <string>
#include <vector>
#include <src/macros.h>
#include <src/jaz/gravis/t4Matrix.h>

class DynamoParticle
{
	public:
		
		DynamoParticle();
		DynamoParticle(std::string line);
		
			int tag;			// tag of particle file in data folder
			int aligned;		// value 1: marks the particle for alignment
			int averaged;		// value 1: the particle was included in the average
			double dx;			// x shift from center (in pixels)
			double dy;			// y shift from center (in pixels)
			double dz;			// z shift from center (in pixels)
			double tdrot;		// euler angle (rotation around z, in degrees)
			double tilt;		// euler angle (rotation around new x, in degrees)
			double narot;		// euler angle (rotation around new z, in degrees)
			double cc;			// Cross correlation coefficient
			double cc2;			// Cross correlation coefficient after thresholding II
			int cpu;			// processor that aligned the particle
			int ftype;			// 0: full range; 1: tilt around y ( ... dhelp dtutorial for more options)
			double ymintilt;	// minimum angle in the tilt series around tilt axis (i.e. -60)
			double ymaxtilt;    // maximum angle in the tilt series around tilt axis (i.e. 60)
			double xmintilt;    // minimum angle in the tilt series around second tilt axis (i.e. -60)
			double xmaxtilt;    // maximum angle in the tilt series around second x (i.e. 60)
			double fs1;         // free parameter for fourier sampling #1()
			double fs2;         // free parameter for fourier sampling #2()
			int tomo;			// tomogram number 
			int reg;			// for arbitrary assignations of regions inside tomograms
			int classId;        // class number 
			int annotation;		// use this field for assigning arbitrary labels
			double x;			// x coordinate in original volume
			double y;			// y coordinate in original volume
			double z;			// z coordinate in original volume
			double dshift;		// norm of shift vector
			double daxis;		// difference in axis orientation
			double dnarot;		// difference in narot
			double dcc;			// difference in CC
			int otag;			// original tag (subboxing)
			int npar;			// number of added particles (compactions) / subunit label (subboxing)
			int ref;			// reference. Used in multireference projects
			int sref;			// subreference (i.e. generated by Dynamo PCA)
			double apix;		// angstrom per pixel
			double def;			// defocus (in micron)
			double eig1;		// "eigencoefficient" #1
			double eig2;		// "eigencoefficient" #2
			
			
			std::string getFormattedTag() const;
			
			void setAlibiOrientation(gravis::d4Matrix A);
			
			inline gravis::d4Matrix getAlignmentMatrixAlias4x4(int w, int h, int d) const
			{
				const double phi = DEG2RAD(tdrot);
				const double theta = DEG2RAD(tilt);
				const double chi = DEG2RAD(narot);
				
				const double sinPhi = sin(phi), cosPhi = cos(phi);
				const double sinTheta = sin(theta), cosTheta = cos(theta);
				const double sinChi = sin(chi), cosChi = cos(chi);
				
				// Center at size/2
				gravis::d4Matrix Tc(
					1, 0, 0, -w/2,
					0, 1, 0, -h/2,
					0, 0, 1, -d/2,
					0, 0, 0, 1);
				
				// The particle rotates clockwise about its z axis.
				gravis::d4Matrix R0(
					 cosPhi,-sinPhi, 0, 0,
					 sinPhi, cosPhi, 0, 0,
						  0,      0, 1, 0,
						  0,      0, 0, 1);
				
				// the particle rotates clockwise about its new x axis.
				gravis::d4Matrix R1(
					 1,         0,        0, 0,
					 0,  cosTheta,-sinTheta, 0,
					 0,  sinTheta, cosTheta, 0,
					 0,         0,        0, 1);
				
				// the particle rotates clockwise about its new z axis.
				gravis::d4Matrix R2(
					 cosChi,-sinChi, 0, 0,
					 sinChi, cosChi, 0, 0,
						  0,      0, 1, 0,
						  0,      0, 0, 1);
				
				// Shift to 3D position of center
				gravis::d4Matrix Ts(
					1, 0, 0, x + dx,
					0, 1, 0, y + dy,
					0, 0, 1, z + dz,
					0, 0, 0, 1);
				
				return Ts * R2 * R1 * R0 * Tc;
            }

            inline gravis::d4Matrix getAlignmentMatrixAlibi4x4(int w, int h, int d) const
            {
                const double phi = DEG2RAD(tdrot);
                const double theta = DEG2RAD(tilt);
                const double chi = DEG2RAD(narot);

                const double sinPhi = sin(phi), cosPhi = cos(phi);
                const double sinTheta = sin(theta), cosTheta = cos(theta);
                const double sinChi = sin(chi), cosChi = cos(chi);

                // Center at size/2
                gravis::d4Matrix Tc(
                    1, 0, 0, -w/2,
                    0, 1, 0, -h/2,
                    0, 0, 1, -d/2,
                    0, 0, 0, 1);

                // The particle rotates clockwise about its z axis.
                gravis::d4Matrix R0(
                     cosPhi, sinPhi, 0, 0,
                     -sinPhi, cosPhi, 0, 0,
                          0,      0, 1, 0,
                          0,      0, 0, 1);

                // the particle rotates clockwise about its new x axis.
                gravis::d4Matrix R1(
                     1,         0,        0, 0,
                     0,  cosTheta, sinTheta, 0,
                     0, -sinTheta, cosTheta, 0,
                     0,         0,        0, 1);

                // the particle rotates clockwise about its new z axis.
                gravis::d4Matrix R2(
                     cosChi, sinChi, 0, 0,
                    -sinChi, cosChi, 0, 0,
                          0,      0, 1, 0,
                          0,      0, 0, 1);

                // Shift to 3D position of center
                gravis::d4Matrix Ts(
                    1, 0, 0, x + dx,
                    0, 1, 0, y + dy,
                    0, 0, 1, z + dz,
                    0, 0, 0, 1);

                return Ts * R0 * R1 * R2 * Tc;
            }

            inline gravis::d4Matrix getShiftMatrixAlibi4x4(int w, int h, int d) const
            {

                // Center at size/2
                gravis::d4Matrix Tc(
                    1, 0, 0, -w/2,
                    0, 1, 0, -h/2,
                    0, 0, 1, -d/2,
                    0, 0, 0, 1);

                // Shift to 3D position of center
                gravis::d4Matrix Ts(
                    1, 0, 0, x + dx,
                    0, 1, 0, y + dy,
                    0, 0, 1, z + dz,
                    0, 0, 0, 1);

                return Ts * Tc;
            }

			inline gravis::d3Vector getPosition() const
			{
				return gravis::d3Vector(x + dx, y + dy, z + dz);
			}
			
			void setOrientation(gravis::d3Vector north, bool transpose = false);
};

std::ostream & operator << (std::ostream &out, const DynamoParticle& c);

#endif
