//
//  msym.h
//  libmsym
//
//  Created by Marcus Johansson on 30/01/15.
//  Copyright (c) 2015 Marcus Johansson. 
//
//  Distributed under the MIT License ( See LICENSE file or copy at http://opensource.org/licenses/MIT )
//

#ifndef __MSYM_H
#define __MSYM_H

#ifdef __cplusplus
extern "C" {
#endif

#include "msym_EXPORTS.h" // automatically generated by cmake
    
#include "msym_error.h"
    
    typedef struct _msym_context * msym_context;

    typedef enum _msym_geometry {
        MSYM_GEOMETRY_UNKNOWN = 0,
        MSYM_GEOMETRY_SPHERICAL = 1,
        MSYM_GEOMETRY_LINEAR = 2,
        MSYM_GEOMETRY_PLANAR_REGULAR = 3,
        MSYM_GEOMETRY_PLANAR_IRREGULAR = 4,
        MSYM_GEOMETRY_POLYHEDRAL_PROLATE = 5,
        MSYM_GEOMETRY_POLYHEDRAL_OBLATE = 6,
        MSYM_GEOMETRY_ASSYMETRIC = 7
    } msym_geometry_t;
    
    typedef struct _msym_symmetry_operation {
        enum _msym_symmetry_operation_type {
            MSYM_SYMMETRY_OPERATION_TYPE_IDENTITY = 0,
            MSYM_SYMMETRY_OPERATION_TYPE_PROPER_ROTATION = 1,
            MSYM_SYMMETRY_OPERATION_TYPE_IMPROPER_ROTATION = 2,
            MSYM_SYMMETRY_OPERATION_TYPE_REFLECTION = 3,
            MSYM_SYMMETRY_OPERATION_TYPE_INVERSION = 4
        } type;
        int order;                              // Order of proper/improper rotation
        int power;                              // Power (e.g. C2^2 = I)
        enum _msym_symmetry_operation_orientation {
            MSYM_SYMMETRY_OPERATION_ORIENTATION_NONE = 0,
            MSYM_SYMMETRY_OPERATION_ORIENTATION_HORIZONTAL = 1,
            MSYM_SYMMETRY_OPERATION_ORIENTATION_VERTICAL = 2,
            MSYM_SYMMETRY_OPERATION_ORIENTATION_DIHEDRAL = 3
        } orientation;
        double v[3];                            // Proper/improper rotation vector or reflection plane normal
        int cla;                                // Class of symmetry operation (point group dependant)
    } msym_symmetry_operation_t;
    
    typedef enum _msym_point_group_type {
        MSYM_POINT_GROUP_TYPE_Kh  = 0,
        MSYM_POINT_GROUP_TYPE_K   = 1,
        MSYM_POINT_GROUP_TYPE_Ci  = 2,
        MSYM_POINT_GROUP_TYPE_Cs  = 3,
        MSYM_POINT_GROUP_TYPE_Cn  = 4,
        MSYM_POINT_GROUP_TYPE_Cnh = 5,
        MSYM_POINT_GROUP_TYPE_Cnv = 6,
        MSYM_POINT_GROUP_TYPE_Dn  = 7,
        MSYM_POINT_GROUP_TYPE_Dnh = 8,
        MSYM_POINT_GROUP_TYPE_Dnd = 9,
        MSYM_POINT_GROUP_TYPE_Sn  = 10,
        MSYM_POINT_GROUP_TYPE_T   = 11,
        MSYM_POINT_GROUP_TYPE_Td  = 12,
        MSYM_POINT_GROUP_TYPE_Th  = 13,
        MSYM_POINT_GROUP_TYPE_O   = 14,
        MSYM_POINT_GROUP_TYPE_Oh  = 15,
        MSYM_POINT_GROUP_TYPE_I   = 16,
        MSYM_POINT_GROUP_TYPE_Ih  = 17

    } msym_point_group_type_t;
    
    typedef struct _msym_subgroup {
        msym_point_group_type_t type;
        int n;
        int order;
        msym_symmetry_operation_t *primary;
        msym_symmetry_operation_t **sops;
        struct _msym_subgroup *generators[2];
        char name[8];
    } msym_subgroup_t;
    
    typedef struct _msym_thresholds {
        double zero;                            // For determining if something is zero (e.g. vectors close to center of mass)
        double geometry;                        // For translating inertial tensor eigenvalues to geometric structures
        double angle;                           // For determining angles, (e.g. if vectors are parallel)
        double equivalence;                     // Equivalence test threshold
        double eigfact;                         // Jacobi eigenvalue algorithm threshold
        double permutation;                     // Equality test when determining permutation for symmetry operation
        double orthogonalization;               // For orthogonalizing orbital subspaces
    } msym_thresholds_t;
    
    
    typedef struct _msym_element {
        void *id;                               // custom identifier
        double m;                               // Mass
        double v[3];                            // Position
        int n;                                  // Nuclear charge
        char name[4];                           // Name
    } msym_element_t;
    
    typedef struct _msym_equivalence_set {
        msym_element_t **elements;              // Pointers to elements
        double err;                             // Maximum error when detecting this equivalence set
        int length;                             // Number of elements
    } msym_equivalence_set_t ;
    
    typedef struct _msym_real_spherical_harmonic {
        int n;                                  // Principal
        int l;                                  // Azimuthal
        int m;                                  // Liniear combination of magnetic quantum number (e.g. 2pz = 0, 2px = 1, 2py = -1)
    } msym_real_spherical_harmonic_t;
    
    typedef struct _msym_basis_function {
        void *id;                               // custom identifier
        enum _msym_basis_type {
            MSYM_BASIS_TYPE_REAL_SPHERICAL_HARMONIC = 0,
            MSYM_BASIS_TYPE_CARTESIAN = 1
        } type;
        msym_element_t *element;
        union {
            msym_real_spherical_harmonic_t rsh;       // Atomic orbital basis
        } f;
        char name[8];
    
    } msym_basis_function_t;
    
    typedef struct _msym_partner_function {
        int i;          // index of partner 0
        int d;          // component (dimension)
    } msym_partner_function_t;
    
    typedef struct _msym_salc {
        int d;              // dimension of space (same as msym_character_table_t.s[msym_subrepresentation_space_t.s].d)
        int fl;             // number of basis functions
        void *pf;           // partner functions double[d][fl]
        msym_basis_function_t **f;
    } msym_salc_t;
    
    typedef struct _msym_subrepresentation_space {
        int s;      // symmetry species
        int salcl;  // nr of SALCs
        msym_salc_t *salc;
    } msym_subrepresentation_space_t;
    
    typedef struct _msym_symmetry_species {
        int d; // dimensionality of a (ir)reducible representation of this species
        int r; // sum over all x ct->classc[x]*ct->table[i][x]^2/pg->order (can be decomposed into r irreducible representations)
        char name[8];
    } msym_symmetry_species_t;
    
    typedef struct _msym_character_table {
        int d;
        int *classc;
        msym_symmetry_operation_t **sops;
        msym_symmetry_species_t *s;
        void *table;  //double[d][d]
    } msym_character_table_t;

    
    msym_context MSYM_EXPORT msymCreateContext();
    msym_error_t MSYM_EXPORT msymReleaseContext(msym_context ctx);
    
    const msym_thresholds_t MSYM_EXPORT *msymGetDefaultThresholds();
    msym_error_t MSYM_EXPORT msymSetThresholds(msym_context ctx, const msym_thresholds_t *thresholds);
    msym_error_t MSYM_EXPORT msymGetThresholds(msym_context ctx, const msym_thresholds_t **thresholds);
    msym_error_t MSYM_EXPORT msymSetElements(msym_context ctx, int length, msym_element_t *elements);
    msym_error_t MSYM_EXPORT msymGetElements(msym_context ctx, int *length, msym_element_t **elements);
    msym_error_t MSYM_EXPORT msymSetBasisFunctions(msym_context ctx, int length, msym_basis_function_t *basis);
    msym_error_t MSYM_EXPORT msymGetBasisFunctions(msym_context ctx, int *length, msym_basis_function_t **basis);
    msym_error_t MSYM_EXPORT msymGetPointGroupType(msym_context ctx, msym_point_group_type_t *t, int *n);
    msym_error_t MSYM_EXPORT msymSetPointGroupByName(msym_context ctx, const char *name);
    msym_error_t MSYM_EXPORT msymSetPointGroupByType(msym_context ctx, msym_point_group_type_t type, int n);
    msym_error_t MSYM_EXPORT msymGetPointGroupName(msym_context ctx, int l, char *buf);
    msym_error_t MSYM_EXPORT msymGetSubgroups(msym_context ctx, int *l, const msym_subgroup_t **subgroups);
    msym_error_t MSYM_EXPORT msymSelectSubgroup(msym_context ctx, const msym_subgroup_t *subgroup);
    msym_error_t MSYM_EXPORT msymGetSymmetryOperations(msym_context ctx, int *sopsl, const msym_symmetry_operation_t **sops);
    msym_error_t MSYM_EXPORT msymGetEquivalenceSets(msym_context ctx, int *l, const msym_equivalence_set_t **es);
    msym_error_t MSYM_EXPORT msymGetEquivalenceSetByElement(msym_context ctx, msym_element_t *element, const msym_equivalence_set_t **es);
    msym_error_t MSYM_EXPORT msymGetSubrepresentationSpaces(msym_context ctx, int *l, const msym_subrepresentation_space_t **srs);
    msym_error_t MSYM_EXPORT msymGetCharacterTable(msym_context ctx, const msym_character_table_t **ct);
    
    msym_error_t MSYM_EXPORT msymFindEquivalenceSets(msym_context ctx);
    msym_error_t MSYM_EXPORT msymFindEquivalenceSetPermutations(msym_context ctx);
    msym_error_t MSYM_EXPORT msymFindSymmetry(msym_context ctx);
    msym_error_t MSYM_EXPORT msymSymmetrizeElements(msym_context context, double *err);
    msym_error_t MSYM_EXPORT msymApplyTranslation(msym_context ctx, msym_element_t *element, double v[3]);
#ifdef __cplusplus
    msym_error_t MSYM_EXPORT msymSymmetrizeWavefunctions(msym_context ctx, int l, void *c, int *species, msym_partner_function_t *pf);
    msym_error_t MSYM_EXPORT msymGetSALCs(msym_context ctx, int l, void *c, int *species, msym_partner_function_t *pf);
    
#else
    msym_error_t MSYM_EXPORT msymSymmetrizeWavefunctions(msym_context ctx, int l, double c[l][l], int species[l], msym_partner_function_t pf[l]);
    msym_error_t MSYM_EXPORT msymGetSALCs(msym_context ctx, int l, double c[l][l], int species[l], msym_partner_function_t pf[l]);
#endif
    msym_error_t MSYM_EXPORT msymSymmetrySpeciesComponents(msym_context ctx, int wfl, double *wf, int sl, double *s);
    msym_error_t MSYM_EXPORT msymGenerateElements(msym_context ctx, int length, msym_element_t *elements);
    msym_error_t MSYM_EXPORT msymGenerateSubrepresentationSpaces(msym_context ctx);
    msym_error_t MSYM_EXPORT msymAlignAxes(msym_context ctx);
    
    msym_error_t MSYM_EXPORT msymGetCenterOfMass(msym_context ctx, double v[3]);
    msym_error_t MSYM_EXPORT msymSetCenterOfMass(msym_context ctx, double v[3]);
    msym_error_t MSYM_EXPORT msymGetRadius(msym_context ctx, double *radius);
    msym_error_t MSYM_EXPORT msymGetGeometry(msym_context ctx, msym_geometry_t *geometry);
    msym_error_t MSYM_EXPORT msymGetPrincipalMoments(msym_context ctx, double eigval[3]);
    msym_error_t MSYM_EXPORT msymGetPrincipalAxes(msym_context ctx, double eigvec[3][3]);
    msym_error_t MSYM_EXPORT msymGetAlignmentAxes(msym_context ctx, double primary[3], double secondary[3]);
    msym_error_t MSYM_EXPORT msymSetAlignmentAxes(msym_context ctx, double primary[3], double secondary[3]);
    msym_error_t MSYM_EXPORT msymGetAlignmentTransform(msym_context ctx, double transform[3][3]);
    msym_error_t MSYM_EXPORT msymSetAlignmentTransform(msym_context ctx, double transform[3][3]);
    
#ifdef __cplusplus
}
#endif
    
#endif /* defined(__MSYM_H) */
