#ifdef AHF
/***********************************************************************
 *    Includes                                                         *
 ***********************************************************************/
/* Standard includes */
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "ahf_io.h"
#include "ahf_halos.h"

/** Okay, we need those to catch all the defines etc. */
#include "../define.h"
#include "../param.h"
#include "../common.h"

/** This is required for the HALO structure */
#include "../tdef.h"

/** Get the needed utility functions */
#include "../libutility/cosmology.h"
#include "../libutility/utility.h"


/***********************************************************************
 *    Local variables                                                  *
 ***********************************************************************/


/***********************************************************************
 *    Definitions of local functions                                   *
 ***********************************************************************/
#ifndef AHFbinary
static void
WriteProfilesLegacy(FILE          *fout,
                    HALO          *halos,
                    unsigned long *idx,
                    int           numHalos);

static void
WriteHalosLegacy(FILE          *fout,
                 HALO          *halos,
                 unsigned long *idx,
                 int           numHalos);

static void
WriteParticlesLegacy(FILE          *fout,
                     HALO          *halos,
                     unsigned long *idx,
                     int           numHalos);

#endif


/***********************************************************************
 *    Implemenation of exported functions                              *
 ***********************************************************************/
extern void
ahf_io_WriteProfiles(const char    *fprefix,
                     HALO          *halos,
                     unsigned long *idx,
                     int           numHalos)
{
	char filename[MAXSTRING];
	FILE *fout;

	/* Generate the filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_profiles");
#ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#endif

#ifndef AHFbinary
	/* Open file*/
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}
	WriteProfilesLegacy(fout, halos, idx, numHalos);

	/* Clean up */
	fclose(fout);
	return;
#endif

	/* Clean up */
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteProfiles */

extern void
ahf_io_WriteHalos(const char    *fprefix,
                  HALO          *halos,
                  unsigned long *idx,
                  int           numHalos)
{
	char filename[MAXSTRING];
	FILE *fout;

	/* Generate the filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_halos");
#ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#endif

#ifndef AHFbinary
	/* Open file*/
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}
	WriteHalosLegacy(fout, halos, idx, numHalos);

	/* Clean up */
	fclose(fout);
	return;
#endif

	/* Clean up */
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteHalos */

extern void
ahf_io_WriteParticles(const char    *fprefix,
                      HALO          *halos,
                      unsigned long *idx,
                      int           numHalos)
{
	char filename[MAXSTRING];
	FILE *fout;

	/* Generate the filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_particles");
#ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#endif

#ifndef AHFbinary
	/* Open file*/
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}
	WriteParticlesLegacy(fout, halos, idx, numHalos);

	/* Clean up */
	fclose(fout);
	return;
#endif

	/* Clean up */
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteParticles */

#ifdef AHFcentrefile
extern void
ahf_io_WriteCenterfile(const char *fprefix,
                       HALO       *halos,
                       int        numHalos)
{
	char filename[MAXSTRING];
	FILE *fout;
	int  j;

	/* Generate the filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_centres");
#  ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#  endif

	/* Open file */
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}

	/* Write centres */
	for (j = 0; j < numHalos; j++) {
		fprintf(fout, "P %g %g %g 0 1 0 6\n",
		        halos[j].pos.x * simu.boxsize,
		        halos[j].pos.y * simu.boxsize,
		        halos[j].pos.z * simu.boxsize);
	}

	/* Clean up */
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteCenterfile */

#endif /* AHFcentrefile */

#if ((defined AHFsubstructure || defined AHFgridsubstructure) \
    && !defined WITH_MPI && !defined AHFrestart && !defined AHFbinary)
extern void
ahf_io_WriteSubstructure(const char    *fprefix,
                         HALO          *halos,
                         unsigned long *idx,
                         int           numHalos)
{
	FILE          *fout;
	char          filename[MAXSTRING];
	unsigned long i;
	int           j, k, l;

	/* Generate filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_substructure");
#  ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#  endif

	/* Open file */
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}

	/* Write the substructure file */
	fprintf(fout, "%d\n", numHalos);
	for (j = 0; j < numHalos; j++) {
		i = idx[j];

#  ifdef AHFgridsubstructure
		if ((halos[i].numSubStruct > 0)
		    && (halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
			/* does *NOT* check if substructure lies within virial radius of
			 *host */
			fprintf(fout, "%10d %12d\n", j, halos[i].numSubStruct);

			for (k = 0; k < halos[i].numSubStruct; k++) {
				l = idx_inv(idx, numHalos, halos[i].subStruct[k]);
				fprintf(fout, "%10d ", l);
			}


			fprintf(fout, "\n");
		}
#  else   /* AHFgridsubstructure */
		if ((halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
			/* check if substructure lies within virial radius of host */
			Xhost  = halos[i].pos.x;
			Yhost  = halos[i].pos.y;
			Zhost  = halos[i].pos.z;
			Rhost2 = pow2(halos[i].R_vir);

			nsat   = 0;
			isat   = NULL;

			for (k = 0; k < numHalos; k++) {
				/* make sure to use un-ordered id */
				l = idx[k];

				if (l != i) {
					Xsat = halos[l].pos.x;
					Ysat = halos[l].pos.y;
					Zsat = halos[l].pos.z;

					/* distance to host centre */
					dX = Xsat - Xhost;
					dY = Ysat - Yhost;
					dZ = Zsat - Zhost;

					/* take care of periodic boundaries */
					if (dX > 0.5)
						dX -= 1.0;
					if (dY > 0.5)
						dY -= 1.0;
					if (dZ > 0.5)
						dZ -= 1.0;
					if (dX < -0.5)
						dX += 1.0;
					if (dY < -0.5)
						dY += 1.0;
					if (dZ < -0.5)
						dZ += 1.0;

					Dsat2 = pow2(dX) + pow2(dY) + pow2(dZ);

					/* within host's virial radius */
					if ((Dsat2 < Rhost2)
					    && (halos[l].npart >= AHF_MINPART)
					    && ((double)halos[l].npart * simu.min_weight
					        / (double)halos[l].M_vir > AHF_MASSMIX)) {
						nsat++;
						isat           = (long *)realloc(isat,
						                                 nsat * sizeof(long));
						isat[nsat - 1] = idx_inv(idx, numHalos, l);
					}
				}
			}

			/* write total number of satellites found */
			fprintf(fout, "%10d %12ld\n", j, nsat);

			/* write satellite id's */
			for (k = 0; k < nsat; k++) {
				l = isat[k];
				fprintf(fout, "%10d ", l);
			}
			free(isat);

			fprintf(fout, "\n");
		}
#  endif   /* AHFgridsubstructure */
	}

	/* Clean up*/
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteSubstructure */

#endif /* Substructure selection bla */

#ifdef AHFgeom
extern void
ahf_io_WriteHalosGeom(const char    *fprefix,
                      HALO          *halos,
                      unsigned long *idx,
                      int           numHalos)
{
	char          filename[MAXSTRING];
	FILE          *fout;
	unsigned long ifrac;
	unsigned long ipart;
	unsigned long j;
	partptr       cur_part, current;
	int           i;
	double        r, g, b;


	/* Generate filename */
	strcpy(filename, fprefix);
	strcat(filename, ".AHF_halos.geom");
#  ifdef VERBOSE
	fprintf(stderr, "%s\n", filename);
#  endif

	/* Open file */
	if ((fout = fopen(filename, "w")) == NULL) {
		fprintf(stderr, "could not open %s\n", filename);
		exit(1);
	}

#  ifdef AHFgeom_SIMUPART
	/* 1. write about 500000 simulation particles to AHF_geom file */
#    ifdef WITH_MPI
	if (global_info.no_part > 500000)
		ifrac = (long unsigned)((double)global_info.no_part
		                        / ((double)(500000.)));
#    else
	if (global.no_part > 500000)
		ifrac = (long unsigned)((double)global.no_part / (double)(500000.));
#    endif
	else
		ifrac = (long unsigned)1;

#    if (defined WITH_MPI || defined AHFrestart)
	for (ipart = 0; ipart < global_info.no_part; ipart += ifrac)
#    else
	for (ipart = 0; ipart < global.no_part; ipart += ifrac)
#    endif
	{
		cur_part = global.fst_part + ipart;
#    ifdef MULTIMASS
		if (cur_part->weight < simu.max_weight)
			fprintf(fout,
			        "p %12.6g %12.6g %12.6g 0 0 1\n",
			        cur_part->pos[X] * x_fac,
			        cur_part->pos[Y] * x_fac,
			        cur_part->pos[Z] * x_fac);
		else
			fprintf(fout,
			        "p %12.6g %12.6g %12.6g 0.3 0 0\n",
			        cur_part->pos[X] * x_fac,
			        cur_part->pos[Y] * x_fac,
			        cur_part->pos[Z] * x_fac);
#    else
		fprintf(fout,
		        "p %12.6g %12.6g %12.6g 0 0 1\n",
		        cur_part->pos[X] * x_fac,
		        cur_part->pos[Y] * x_fac,
		        cur_part->pos[Z] * x_fac);
#    endif
	}
#  endif     /* AHFgeom_SIMUPART */

	/* 2. write halos */
	for (j = 0; j < numHalos; j++) {
		i = idx[j];

		r = 1.0 - (double)i / (double)numHalos;
		g = (double)i / (double)numHalos;
		b = 0.0;

		r = 1.0;
		g = 0.0;
		b = 0.0;

		if ((halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
#  ifdef AHFgeom_HALOPART
			current = halos[i].ll;
			while (current != NULL) {
				fprintf(fout,
				        "p %12.6g %12.6g %12.6g  %g %g %g\n",
				        current->pos[X] * x_fac,
				        current->pos[Y] * x_fac,
				        current->pos[Z] * x_fac,
				        r,
				        g,
				        b);
				current = current->ll;
			}
#  else
			fprintf(fout,
			        "s %12.6g %12.6g %12.6g %12.6g  %g %g %g\n",
			        halos[i].pos.x * x_fac,
			        halos[i].pos.y * x_fac,
			        halos[i].pos.z * x_fac,
			        halos[i].R_vir * x_fac,
			        r,
			        g,
			        b);
#  endif
		}
	}

	/* Clean */
	fclose(fout);

	/* Done */
	return;
} /* ahf_io_WriteHalosGeom */

#endif /* AHFgeom */


/***********************************************************************
 *    Implementation of local functions                                *
 ***********************************************************************/
#ifndef AHFbinary

/* This is used to print the helpful notes about which columns holds what */
#  define COLUMN_INFO(f, info, counter) \
    fprintf(f, info "(%i)\t", counter++);

static void
WriteProfilesLegacy(FILE          *fout,
                    HALO          *halos,
                    unsigned long *idx,
                    int           numHalos)
{
	int           column = 1;
	unsigned long i, j;
	int           r_conv_i, ibin;
	double        rad, t_relax, age;

#  if (WITH_MPI || AHFrestart)
#    ifdef WITH_MPI
	if (global_mpi.rank == 0)
#    else
	if (global_info.rank == 0)
#    endif
#  endif
	{
		fprintf(fout, "#");
		COLUMN_INFO(fout, "r", column);
		COLUMN_INFO(fout, "npart", column);
		COLUMN_INFO(fout, "nvpart", column);
		COLUMN_INFO(fout, "ovdens", column);
		COLUMN_INFO(fout, "dens", column);
		COLUMN_INFO(fout, "vcirc", column);
		COLUMN_INFO(fout, "sigv", column);
		COLUMN_INFO(fout, "Lx", column);
		COLUMN_INFO(fout, "Ly", column);
		COLUMN_INFO(fout, "Lz", column);
		COLUMN_INFO(fout, "a", column);
		COLUMN_INFO(fout, "Eax", column);
		COLUMN_INFO(fout, "Eay", column);
		COLUMN_INFO(fout, "Eaz", column);
		COLUMN_INFO(fout, "b", column);
		COLUMN_INFO(fout, "Ebx", column);
		COLUMN_INFO(fout, "Eby", column);
		COLUMN_INFO(fout, "Ebz", column);
		COLUMN_INFO(fout, "c", column);
		COLUMN_INFO(fout, "Ecx", column);
		COLUMN_INFO(fout, "Ecy", column);
		COLUMN_INFO(fout, "Ecz", column);
		COLUMN_INFO(fout, "Ekin", column);
		COLUMN_INFO(fout, "Epot", column);
		COLUMN_INFO(fout, "vesc", column);
#  ifdef GAS_PARTICLES
		COLUMN_INFO(fout, "M_gas", column);
		COLUMN_INFO(fout, "M_star", column);
		COLUMN_INFO(fout, "u_gas", column);
#  endif
#  ifdef AHFphspdens
		COLUMN_INFO(fout, "sigma2_vx_sh", column);
		COLUMN_INFO(fout, "sigma2_vy_sh", column);
		COLUMN_INFO(fout, "sigma2_vz_sh", column);
		COLUMN_INFO(fout, "sigma2_vr_sh", column);
		COLUMN_INFO(fout, "sigma2_vtheta_sh", column);
		COLUMN_INFO(fout, "sigma2_vphi_sh", column);
#    ifdef AHFmeanvelocities
		COLUMN_INFO(fout, "mean_vx_sh", column);
		COLUMN_INFO(fout, "mean_vy_sh", column);
		COLUMN_INFO(fout, "mean_vx_sh", column);
		COLUMN_INFO(fout, "mean_vr_sh", column);
		COLUMN_INFO(fout, "mean_vtheta_sh", column);
		COLUMN_INFO(fout, "mean_vphi_sh", column);
		COLUMN_INFO(fout, "mean_vx_sp", column);
		COLUMN_INFO(fout, "mean_vy_sp", column);
		COLUMN_INFO(fout, "mean_vx_sp", column);
		COLUMN_INFO(fout, "mean_vr_sp", column);
		COLUMN_INFO(fout, "mean_vtheta_sp", column);
		COLUMN_INFO(fout, "mean_vphi_sp", column);
#    endif
#  endif
#  ifdef METALHACK
		COLUMN_INFO(fout, "Z_gas_sh", column);
		COLUMN_INFO(fout, "Z_star_sh", column);
#  endif
		fprintf(fout, "\n");
	}

	for (j = 0; j < numHalos; j++) {
		i = idx[j];

		if ((halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
			for (r_conv_i = 0, ibin = 0; ibin < halos[i].prof.nbins;
			     ibin++) {
				/* check for converged radius (Power et al. 2003) */
				rad     = halos[i].prof.r[ibin];
				t_relax = halos[i].prof.npart[ibin] / log(
				    rad / halos[i].spaRes)
				          * rad / sqrt(halos[i].prof.v2_circ[ibin]);

				/* convert to (Mpc/h) / (km/sec) ~ (Mpc/h) / (kpc/Gyr) */
				t_relax *= r_fac / sqrt(phi_fac);

				/* convert to Gyr/h */
				t_relax *= 1E3;

				/* age of the Universe in Gyr/h */
				age = calc_t(global.a) * simu.t_unit * Mpc / Gyr;

				/* if not converged, write negative radius into
				 *.AHF_profiles */
				if (t_relax < 0.9 * age)
					/* The +1 is merely to keep the name consistent:
					 * r_conv_i should give the smallest converged radius,
					 * not the bin before it. Hence to check for converged
					 * bin or not, i<r_conv_i is the thing to do. */
					r_conv_i = ibin + 1;
			}

			for (ibin = 0; ibin < halos[i].prof.nbins; ibin++) {
				/* If the radius is not converged, print the radius negative
				 * to the .AHF_profiles */
				rad = (ibin < r_conv_i) ? -halos[i].prof.r[ibin]
					  : halos[i].prof.r[ibin];
				fprintf(fout, "%8.4f",
				        rad * x_fac * 1000.);
				fprintf(fout, "\t%10ld",
				        halos[i].prof.npart[ibin]);
				fprintf(fout, "\t%e",
				        halos[i].prof.nvpart[ibin]);
				fprintf(fout, "\t%12.2f",
				        halos[i].prof.ovdens[ibin] * rho_fac / global.rho_b);
				fprintf(fout, "\t%12.2f",
				        halos[i].prof.dens[ibin] * rho_fac / global.rho_b);
				fprintf(fout, "\t%8.2f",
				        sqrt(halos[i].prof.v2_circ[ibin] * phi_fac));
				fprintf(fout, "\t%8.2f",
				        halos[i].prof.sig_v[ibin] * v_fac);
				fprintf(fout, "\t%12.4g",
				        halos[i].prof.Lx[ibin] * m_fac * r_fac * v_fac);
				fprintf(fout, "\t%12.4g",
				        halos[i].prof.Ly[ibin] * m_fac * r_fac * v_fac);
				fprintf(fout, "\t%12.4g",
				        halos[i].prof.Lz[ibin] * m_fac * r_fac * v_fac);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.axis1[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E1x[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E1y[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E1z[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.axis2[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E2x[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E2y[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E2z[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.axis3[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E3x[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E3y[ibin]);
				fprintf(fout, "\t%10.6f",
				        halos[i].prof.E3z[ibin]);
				fprintf(fout, "\t%e",
				        halos[i].prof.Ekin[ibin] * m_fac * pow2(v_fac));
				fprintf(fout, "\t%e",
				        halos[i].prof.Epot[ibin] * m_fac * phi_fac);
				fprintf(fout, "\t%10.6f",
				        sqrt(halos[i].prof.v_esc2[ibin] * phi_fac));
#  ifdef GAS_PARTICLES
				fprintf(fout, "\t%e",
				        halos[i].prof.M_gas[ibin]);
				fprintf(fout, "\t%e",
				        halos[i].prof.M_star[ibin]);
				fprintf(fout, "\t%e",
				        halos[i].prof.u_gas[ibin]);
#  endif
#  ifdef AHFphspdens
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vx_sh[ibin] * v_fac * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vy_sh[ibin] * v_fac * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vz_sh[ibin] * v_fac * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vr_sh[ibin] * v_fac * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vtheta_sh[ibin] * v_fac
				        * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.sigma2_vphi_sh[ibin] * v_fac * v_fac);
#    ifdef AHFmeanvelocities
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vx_sh[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vy_sh[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vz_sh[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vr_sh[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vtheta_sh[ibin]);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vphi_sh[ibin]);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vx_sp[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vy_sp[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vz_sp[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vr_sp[ibin] * v_fac);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vtheta_sp[ibin]);
				fprintf(fout, "\t%g",
				        halos[i].prof.mean_vphi_sp[ibin]);
#    endif
#  endif
#  ifdef METALHACK
				fprintf(fout, "\t%e",
				        halos[i].prof.z_gas[ibin]);
				fprintf(fout, "\t%e",
				        halos[i].prof.z_star[ibin]);
#  endif
				fprintf(fout, "\n");
			}
		}
	}

	/* Done */
	return;
} /* WriteProfilesLegacy */

static void
WriteHalosLegacy(FILE          *fout,
                 HALO          *halos,
                 unsigned long *idx,
                 int           numHalos)
{
	int           column = 1;
	unsigned long i, j;


#  if (WITH_MPI || AHFrestart)
#    ifdef WITH_MPI
	if (global_mpi.rank == 0)
#    else
	if (global_info.rank == 0)
#    endif
#  endif
	{
		fprintf(fout, "#");
		COLUMN_INFO(fout, "npart", column);
		COLUMN_INFO(fout, "nvpart", column);
		COLUMN_INFO(fout, "Xc", column);
		COLUMN_INFO(fout, "Yc", column);
		COLUMN_INFO(fout, "Zc", column);
		COLUMN_INFO(fout, "VXc", column);
		COLUMN_INFO(fout, "VYc", column);
		COLUMN_INFO(fout, "VZc", column);
		COLUMN_INFO(fout, "Mvir", column);
		COLUMN_INFO(fout, "Rvir", column);
		COLUMN_INFO(fout, "Vmax", column);
		COLUMN_INFO(fout, "Rmax", column);
		COLUMN_INFO(fout, "sigV", column);
		COLUMN_INFO(fout, "lambda", column);
		COLUMN_INFO(fout, "Lx", column);
		COLUMN_INFO(fout, "Ly", column);
		COLUMN_INFO(fout, "Lz", column);
		COLUMN_INFO(fout, "a", column);
		COLUMN_INFO(fout, "Eax", column);
		COLUMN_INFO(fout, "Eay", column);
		COLUMN_INFO(fout, "Eaz", column);
		COLUMN_INFO(fout, "b", column);
		COLUMN_INFO(fout, "Ebx", column);
		COLUMN_INFO(fout, "Eby", column);
		COLUMN_INFO(fout, "Ebz", column);
		COLUMN_INFO(fout, "c", column);
		COLUMN_INFO(fout, "Ecx", column);
		COLUMN_INFO(fout, "Ecy", column);
		COLUMN_INFO(fout, "Ecz", column);
		COLUMN_INFO(fout, "ovdens", column);
		COLUMN_INFO(fout, "Redge", column);
		COLUMN_INFO(fout, "nbins", column);
		COLUMN_INFO(fout, "Ekin", column);
		COLUMN_INFO(fout, "Epot", column);
		COLUMN_INFO(fout, "mbp_offset", column);
		COLUMN_INFO(fout, "com_offset", column);
		COLUMN_INFO(fout, "r2", column);
		COLUMN_INFO(fout, "lambdaE", column);
		COLUMN_INFO(fout, "v_esc", column);
		COLUMN_INFO(fout, "Phi0", column);
#  ifdef GAS_PARTICLES
		COLUMN_INFO(fout, "n_gas", column);
		COLUMN_INFO(fout, "M_gas", column);
		COLUMN_INFO(fout, "lambda_gas", column);
		COLUMN_INFO(fout, "Lx_gas", column);
		COLUMN_INFO(fout, "Ly_gas", column);
		COLUMN_INFO(fout, "Lz_gas", column);
		COLUMN_INFO(fout, "a_gas", column);
		COLUMN_INFO(fout, "Eax_gas", column);
		COLUMN_INFO(fout, "Eay_gas", column);
		COLUMN_INFO(fout, "Eaz_gas", column);
		COLUMN_INFO(fout, "b_gas", column);
		COLUMN_INFO(fout, "Ebx_gas", column);
		COLUMN_INFO(fout, "Eby_gas", column);
		COLUMN_INFO(fout, "Ebz_gas", column);
		COLUMN_INFO(fout, "c_gas", column);
		COLUMN_INFO(fout, "Ecx_gas", column);
		COLUMN_INFO(fout, "Ecy_gas", column);
		COLUMN_INFO(fout, "Ecz_gas", column);
		COLUMN_INFO(fout, "Ekin_gas", column);
		COLUMN_INFO(fout, "Epot_gas", column);
		COLUMN_INFO(fout, "lambdaE_gas", column);
		COLUMN_INFO(fout, "n_star", column);
		COLUMN_INFO(fout, "M_star", column);
		COLUMN_INFO(fout, "lambda_star", column);
		COLUMN_INFO(fout, "Lx_star", column);
		COLUMN_INFO(fout, "Ly_star", column);
		COLUMN_INFO(fout, "Lz_star", column);
		COLUMN_INFO(fout, "a_star", column);
		COLUMN_INFO(fout, "Eax_star", column);
		COLUMN_INFO(fout, "Eay_star", column);
		COLUMN_INFO(fout, "Eaz_star", column);
		COLUMN_INFO(fout, "b_star", column);
		COLUMN_INFO(fout, "Ebx_star", column);
		COLUMN_INFO(fout, "Eby_star", column);
		COLUMN_INFO(fout, "Ebz_star", column);
		COLUMN_INFO(fout, "c_star", column);
		COLUMN_INFO(fout, "Ecx_star", column);
		COLUMN_INFO(fout, "Ecy_star", column);
		COLUMN_INFO(fout, "Ecz_star", column);
		COLUMN_INFO(fout, "Ekin_star", column);
		COLUMN_INFO(fout, "Epot_star", column);
		COLUMN_INFO(fout, "lambdaE_star", column);
#    ifdef METALHACK
		COLUMN_INFO(fout, "mean_z_gas", column);
		COLUMN_INFO(fout, "mean_z_star", column);
#    endif
#  endif /* GAS_PARTICLES */
		fprintf(fout, "\n");
	}

	for (j = 0; j < numHalos; j++) {
		i = idx[j];

#  ifdef AHFmaxhalo
		if (halos[i].npart > AHF_MAXHALO) {
#    ifdef VERBOSELOG
			fprintf(
			    io.logfile,
			    "ahf_halos: found halo with more particles than %d -> terminating AMIGA!\n",
			    AHF_MAXHALO);
#    endif
			global.terminate = TRUE;
		}
#  endif

      //fprintf(stderr,"%d %ld\n",i,halos[i].npart);
      
      
		if ((halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
			fprintf(fout, "%10ld",
			        halos[i].npart);
			fprintf(fout, "\t%e",
			        halos[i].prof.nvpart[halos[i].prof.nbins - 1]);
			fprintf(fout, "\t%14.8f",
			        halos[i].pos.x * x_fac);
			fprintf(fout, "\t%14.8f",
			        halos[i].pos.y * x_fac);
			fprintf(fout, "\t%14.8f",
			        halos[i].pos.z * x_fac);
			fprintf(fout, "\t%8.2f",
			        halos[i].vel.x * v_fac);
			fprintf(fout, "\t%8.2f",
			        halos[i].vel.y * v_fac);
			fprintf(fout, "\t%8.2f",
			        halos[i].vel.z * v_fac);
			fprintf(fout, "\t%12.6g",
			        halos[i].M_vir * m_fac);
			fprintf(fout, "\t%10.2f",
			        halos[i].R_vir * x_fac * 1000.);
			fprintf(fout, "\t%8.2f",
			        sqrt(halos[i].V2_max * phi_fac));
			fprintf(fout, "\t%10.2f",
			        halos[i].R_max * x_fac * 1000.);
			fprintf(fout, "\t%8.2f",
			        halos[i].sigV * v_fac);
			fprintf(fout, "\t%10.6f",
			        halos[i].lambda);
			fprintf(fout, "\t%12.4g",
			        halos[i].AngMom.x);
			fprintf(fout, "\t%12.4g",
			        halos[i].AngMom.y);
			fprintf(fout, "\t%12.4g",
			        halos[i].AngMom.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].axis.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].E1.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].E1.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].E1.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].axis.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].E2.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].E2.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].E2.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].axis.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].E3.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].E3.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].E3.z);
			fprintf(fout, "\t%8.2f",
			        halos[i].ovdens);
			fprintf(fout, "\t%12.4g",
			        halos[i].R_edge * x_fac * 1000.);
			fprintf(fout, "\t%6d",
			        halos[i].prof.nbins);
			fprintf(fout, "\t%12.6g",
			        halos[i].Ekin * m_fac * pow2(v_fac));
			fprintf(fout, "\t%12.6g",
			        halos[i].Epot * m_fac * phi_fac);
			fprintf(fout, "\t%10.5f",
			        halos[i].mbp_offset * x_fac * 1000.);
			fprintf(fout, "\t%10.5f",
			        halos[i].com_offset * x_fac * 1000.);
			fprintf(fout, "\t%10.5f",
			        halos[i].r2 * x_fac * 1000.);
			fprintf(fout, "\t%12.6f",
			        halos[i].lambdaE);
			fprintf(fout, "\t%12.6f",
			        sqrt(halos[i].v_esc2 * phi_fac));
			fprintf(fout, "\t%12.6g",
			        halos[i].Phi0 * m_fac * phi_fac);
#  ifdef GAS_PARTICLES
			fprintf(fout, "\t%10ld",
			        halos[i].gas_only.npart);
			fprintf(fout, "\t%12.6g",
			        halos[i].gas_only.Mass * m_fac);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.lambda);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.AngMom.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.AngMom.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.AngMom.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.axis.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E1.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E1.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E1.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.axis.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E2.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E2.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E2.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.axis.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E3.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E3.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.E3.z);
			fprintf(fout, "\t%12.6g",
			        halos[i].gas_only.Ekin * m_fac * pow2(v_fac));
			fprintf(fout, "\t%12.6g",
			        halos[i].gas_only.Epot * m_fac * phi_fac);
			fprintf(fout, "\t%10.6f",
			        halos[i].gas_only.lambdaE);
			fprintf(fout, "\t%10ld",
			        halos[i].stars_only.npart);
			fprintf(fout, "\t%12.6g",
			        halos[i].stars_only.Mass * m_fac);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.lambda);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.AngMom.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.AngMom.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.AngMom.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.axis.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E1.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E1.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E1.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.axis.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E2.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E2.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E2.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.axis.z);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E3.x);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E3.y);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.E3.z);
			fprintf(fout, "\t%12.6g",
			        halos[i].stars_only.Ekin * m_fac * pow2(v_fac));
			fprintf(fout, "\t%12.6g",
			        halos[i].stars_only.Epot * m_fac * phi_fac);
			fprintf(fout, "\t%10.6f",
			        halos[i].stars_only.lambdaE);
#  endif

#  ifdef METALHACK
			fprintf(fout, "\t%e",
			        halos[i].mean_z_gas);
			fprintf(fout, "\t%e",
			        halos[i].mean_z_star);
#  endif
			fprintf(fout, "\n");
		}
	}

	/* Done */
	return;
} /* WriteHalosLegacy */

static void
WriteParticlesLegacy(FILE          *fout,
                     HALO          *halos,
                     unsigned long *idx,
                     int           numHalos)
{
	unsigned long i, j;
	unsigned long numGoodHalos = 0;
	int           ipart;
	partptr       cur_part, current;

	/* This is the number of halos, currently 0, will be filled
	 * correctly at the end */
	fprintf(fout, "%15lu\n", numGoodHalos);

	for (j = 0; j < numHalos; j++) {
		i = idx[j];

		if ((halos[i].npart >= AHF_MINPART)
		    && ((double)halos[i].npart * simu.min_weight
		        / (double)halos[i].M_vir > AHF_MASSMIX)) {
			/* Count this halo as a good one */
			numGoodHalos++;

			/* start with dumping the actual number of particle IDs to
			 * follow... */
			fprintf(fout, "%lu\n", (unsigned long)(halos[i].npart));

#  ifdef PARTICLES_INFO       // STANDARD
#    if (defined NEWSTARTRUN) // STANDARD
          
      /* THIS IS THE STANDARD FORMAT */
			for (ipart = 0; ipart < halos[i].npart; ipart++) {
				current = global.fst_part + halos[i].ipart[ipart];
#      if (defined GAS_PARTICLES)
        // this is the latest format and should always be used!
				fprintf(fout, "%" PRIpartid "\t%d\n",
				        current->id,
				        (current->u >= PGAS) ? (int)PGAS
						: (int)(-current->u));
        // this is an ancient format only kept for compatibility
//				fprintf(fout, "%d\t%d\t%f\n",
//				        (int)current->id,
//				        (current->u >= PGAS) ? (int)PGAS
//                : (int)(-current->u),current->weight*simu.pmass);
#      else
				fprintf(fout, "%" PRIpartid "\n", current->id);
#      endif     /* GAS_PARTICLES */
			}
          
          
#    else     /* NEWSTARTRUN */
          
			for (ipart = 0; ipart < halos[i].npart; ipart++) {
				cur_part = global.fst_part + halos[i].ipart[ipart];

				/* gas particle */
				if ((cur_part - global.fst_part - global.offset_gas <
				     global.no_gas)
				    && (cur_part - global.fst_part - global.offset_gas >= 0))
					fprintf(fout, "%d  %d\n", cur_part->ID, (int)PGAS);

				/* DM particle */
				else if (cur_part - global.fst_part - global.offset_gas < 0)
					fprintf(fout, "%d  %d\n", cur_part->ID, (int)(-PDM));

				/* star particle */
				else
					fprintf(fout, "%d  %d\n", cur_part->ID, (int)(-PSTAR));
			}
#    endif     /* WITH_MPI */


#  else     /* PARTICLES_INFO */
#    if (defined NEWSTARTRUN)
			for (ipart = 0; ipart < halos[i].npart; ipart++) {
				current = global.fst_part + halos[i].ipart[ipart];
				fprintf(fout, "%" PRIpartid "\n", current->id);
			}

#    else     /* NEWSTARTRUN */
#      ifdef GADGET_IDS
			/* this will simply dump the GADGET IDs to file ... irrespective of
			 *their meaning */
			for (ipart = 0; ipart < halos[i].npart; ipart++) {
				current = global.fst_part + halos[i].ipart[ipart];
				fprintf(fout, "%ld\n", current->ID);
			}

#      else     /* GADGET_IDS
				 * here we dump the AMIGA-assigned IDs to be used with
				 *amigaExtractHalos.c */
			for (ipart = 0; ipart < halos[i].npart; ipart++) {
				fprintf(fout, "%ld\n", halos[i].ipart[ipart]);
			}
#      endif     /* GADGET_IDS */
#    endif     /* WITH_MPI */
#  endif     /* PARTICLES_INFO */
		}
	}

	/* Rewriting the number of halos */
	rewind(fout);
	fprintf(fout, "%15lu", numGoodHalos);
	fseek(fout, 0L, SEEK_END);

	/* Done */
	return;
} /* WriteParticlesLegacy */

#endif /* AHFbinary */

#endif /* AHF */
