/*
 *  Copyright (c) 2011-2012, Los Alamos National Security, LLC.
 *  All rights Reserved.
 *
 *  Copyright 2011-2012. Los Alamos National Security, LLC. This software was produced 
 *  under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National 
 *  Laboratory (LANL), which is operated by Los Alamos National Security, LLC 
 *  for the U.S. Department of Energy. The U.S. Government has rights to use, 
 *  reproduce, and distribute this software.  NEITHER THE GOVERNMENT NOR LOS 
 *  ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR 
 *  ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is modified
 *  to produce derivative works, such modified software should be clearly marked,
 *  so as not to confuse it with the version available from LANL.
 *
 *  Additionally, redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Los Alamos National Security, LLC, Los Alamos 
 *       National Laboratory, LANL, the U.S. Government, nor the names of its 
 *       contributors may be used to endorse or promote products derived from 
 *       this software without specific prior written permission.
 *  
 *  THIS SOFTWARE IS PROVIDED BY THE LOS ALAMOS NATIONAL SECURITY, LLC AND 
 *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 
 *  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LOS ALAMOS NATIONAL
 *  SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 *  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *  
 *  CLAMR -- LA-CC-11-094
 *  This research code is being developed as part of the 
 *  2011 X Division Summer Workshop for the express purpose
 *  of a collaborative code for development of ideas in
 *  the implementation of AMR codes for Exascale platforms
 *  
 *  AMR implementation of the Wave code previously developed
 *  as a demonstration code for regular grids on Exascale platforms
 *  as part of the Supercomputing Challenge and Los Alamos 
 *  National Laboratory
 *  
 *  Authors: Bob Robey       XCP-2   brobey@lanl.gov
 *           Neal Davis              davis68@lanl.gov, davis68@illinois.edu
 *           David Nicholaeff        dnic@lanl.gov, mtrxknight@aol.com
 *           Dennis Trujillo         dptrujillo@lanl.gov, dptru10@gmail.com
 * 
 *  
 *  This file and the associated header is based on a file from the capablanca
 *  project available under the MIT open-source license.  As author of that code,
 *  I, Neal Davis, permit repurposing and redistribution for CLAMR under the New
 *  BSD License used above.
 *      http://code.google.com/p/capablanca/
 */

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

#include "state.h"
#include "partition.h"
#include "mesh.h"
#include "hash.h"
#include "crux.h"
//#include "graphics/display.h"
#include "graphics.h"

#include <fstream>
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include <limits.h>

#define OUTPUT_INTERVAL 100
#define COARSE_GRID_RES 128
#define MAX_TIME_STEP 3000

using namespace std;

//  Global variables.
char progName[12];      //  Program name.
char progVers[8];       //  Program version.

//  External global variables.
extern bool verbose,
            localStencil,
            outline,
            face_based,
            dynamic_load_balance_on,
            h5_spoutput,
            neighbor_remap,
            restart;
extern int  outputInterval,
            crux_type,
            enhanced_precision_sum,
            tmax,
            levmx,
            nx,
            ny,
            niter,
            measure_type,
            lttrace_on,
            do_quo_setup,
            calc_neighbor_type,
	    choose_hash_method,
            initial_order,
            graphic_outputInterval,
            graphics_type,
            checkpoint_outputInterval,
            num_of_rollback_states,
            cycle_reorder;
extern float
            mem_opt_factor;
extern double
            upper_mass_diff_percentage;

extern char* restart_file;

void outputHelp()
{   cout << "CLAMR is an experimental adaptive mesh refinement code for the GPU." << endl
         #ifdef PACKAGE_VERSION 
         << "Version is " << PACKAGE_VERSION << endl << endl 
         #endif
         << "Usage:  " << progName << " [options]..." << endl
         << "  -b <B>            Number of rollback images, disk or in memory (default 2);" << endl
         << "  -c <C>            Checkpoint to disk at interval specified;" << endl
         << "  -C <C>            Checkpoint to memory at interval specified;" << endl
         << "  -d                turn on LTTRACE;" << endl
         << "  -D                turn on dynamic load balancing using LTTRACE;" << endl
         << "  -e <E>            force hash_method, ie linear, quadratic..." <<endl          
         << "      \"perfect\"" << endl
         << "      \"linear\"" << endl
         << "      \"quadratic\"" << endl
         << "      \"prime_jump\"" << endl
         << "  -f                face-based finite difference;" << endl
         << "  -g <g>            specify I step between saving graphics information for post processing;" << endl
         << "  -G <G>            specify graphics file type for post processing;" << endl
         << "      \"bmp\"" << endl
         << "      \"gif\"" << endl
         << "      \"jpeg\"" << endl
         << "      \"mpeg\"" << endl
         << "      \"pdf\"" << endl
         << "      \"png\"" << endl
         << "      \"svg\"" << endl
         << "      \"data\"" << endl
         << "  -h                display this help message;" << endl
         << "  -i <I>            specify I steps between output files;" << endl
         << "  -l <l>            max number of levels;" << endl
         << "  -M <M>            memory optimization factor 1.0 <= M <=100.0 (default 1.0 -- represents 1/20 perfect hash);" << endl
         << "  -m <m>            specify partition measure type;" << endl
         << "      \"with_duplicates\"" << endl
         << "      \"without_duplicates\"" << endl
         << "  -N <n>            specify calc neighbor type;" << endl
         << "      \"hash_table\"" << endl
         << "      \"kdtree\"" << endl
         << "  -n <N>            specify coarse grid resolution of NxN;" << endl
         << "  -o                turn off outlines;" << endl
         << "  -P <P>            specify initial order P;" << endl
         << "      \"original_order\"" << endl
         << "      \"hilbert_sort\"" << endl
         << "      \"hilbert_partition\"" << endl
         << "      \"z_order\"" << endl
         << "  -p <p>            specify ordering P every cycle;" << endl
         << "      \"original_order\"" << endl
         << "      \"hilbert_sort\"" << endl
         << "      \"hilbert_partition\"" << endl
         << "      \"local_hilbert\"" << endl
         << "      \"local_fixed\"" << endl
         << "      \"z_order\"" << endl
         << "  -q                turn on quo;" << endl
         << "  -r                regular sum instead of enhanced precision sum (Kahan sum);" << endl
         << "  -R                restart simulation from the backup file specified;" << endl
         << "  -s <s>            specify space-filling curve method S;" << endl
         << "  -S                write out double precision data as single precision;" << endl
         << "  -T                execute with TVD;" << endl
         << "  -t <t>            specify T time steps to run;" << endl
         << "  -u                allowed percentage of difference between total mass between iterations." << endl
         << "                    the default value for this parameter is 2.6e-13;" << endl
         << "  -V                use verbose output;" << endl
         << "  -v                display version information." << endl
         << "  -z                force recalculation of neighbors." << endl; }

void outputVersion()
{   cout << progName << " " << progVers << endl; }

/*  parseInput(const int argc, char** argv)
 *  
 *  Interpret the command line input.
 */
void parseInput(const int argc, char** argv)
{   strcpy(progName, "clamr");
    #ifdef PACKAGE_VERSION
    strcpy(progVers, PACKAGE_VERSION);
   #endif
    //	Reconstruct command line argument as a string.
    char progCL[256];       //  Complete program command line.
    strcpy(progCL, argv[0]);
    for (int i = 1; i < argc; i++)
    {   strcat(progCL, " ");
        strcat(progCL, argv[i]); }
    
    //  Set variables to defaults, which may be overridden by CLI.
    verbose                 = false;
    localStencil            = true;
    outline                 = true;
#ifdef HAVE_LTTRACE
    lttrace_on              = 0;
#endif
#ifdef HAVE_QUO
    do_quo_setup            = 0;
#endif
    dynamic_load_balance_on = false;
    crux_type               = CRUX_NONE;
    face_based              = false;
    restart                 = false;
    restart_file            = NULL;
    outputInterval          = OUTPUT_INTERVAL;
    nx                      = COARSE_GRID_RES;
    ny                      = COARSE_GRID_RES;
    niter                   = MAX_TIME_STEP;
    neighbor_remap          = true;
    //measure_type            = CSTARVALUE;
    measure_type            = NO_PARTITION_MEASURE;
    calc_neighbor_type      = HASH_TABLE;
    choose_hash_method      = METHOD_UNSET;
    initial_order           = HILBERT_SORT;
    cycle_reorder           = ORIGINAL_ORDER;
    graphic_outputInterval  = INT_MAX;
    graphics_type           = GRAPHICS_NONE;
    checkpoint_outputInterval = INT_MAX;
    num_of_rollback_states  = 2;
    levmx                   = 1;
    mem_opt_factor          = 1.0;
    upper_mass_diff_percentage = -1.0;
    enhanced_precision_sum  = SUM_KAHAN;
    
    char   *val;
    if (argc > 1)
    {   int i = 1;
        val = strtok(argv[i++], " ,.-");
        while (val != NULL){
            switch (val[0]){
               case 'b':     //  Number of rollback images, disk or in memory (default 2)
                    sprintf(val,"0");
                    if (i < argc) val = strtok(argv[i++], " ,");
                    if(atoi(val) < 1){
                        printf("backup number must be at least 1, setting to default value 2\n");
                    }
                    else{
                        num_of_rollback_states = atoi(val);
                    }
                    break;
                case 'c':   //  Checkpoint to disk at interval specified
                    val = strtok(argv[i++], " ,.-");
                    checkpoint_outputInterval = atoi(val);
                    crux_type = CRUX_DISK;
                    break;

                case 'C':   //  Checkpoint to memory at interval specified
                    val = strtok(argv[i++], " ,.-");
                    checkpoint_outputInterval = atoi(val);
                    crux_type = CRUX_IN_MEMORY;
                    break;

                case 'd':   //  Turn on lttrace.
                            //  This is provided as a separate option to measure
                            //  the overhead of having lttrace on.
#ifdef HAVE_LTTRACE
                    lttrace_on = 1;
#endif
                    break;

                case 'D':   //  Turn on dynamic load balancing.
                            //  This forces on lttrace.
#ifdef HAVE_LTTRACE
                    lttrace_on = true;
                    dynamic_load_balance_on = true;
#endif
                    break;

                case 'e':   //  hash method specified.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"perfect") ) {
                       choose_hash_method = PERFECT_HASH;
                    } else if (! strcmp(val,"linear") ) {
                       choose_hash_method = LINEAR;
                    } else if (! strcmp(val,"quadratic") ) {
                       choose_hash_method = QUADRATIC;
                    } else if (! strcmp(val,"prime_jump") ) {
                       choose_hash_method = PRIME_JUMP;
                    }
                    break;

                case 'f':   // Use face-based finite difference
                    face_based = true;
                    break;
                
                case 'g':   //  Save graphics data to files during simulation.
                    val = strtok(argv[i++], " ,.-");
                    graphic_outputInterval = atoi(val);
                    if (graphics_type == GRAPHICS_NONE) graphics_type = GRAPHICS_DATA;
                    break;

                case 'G':   //  Graphics data file type.
                    val = strtok(argv[i++], " ,.-");
                    if (! strcmp(val,"none") ) {
                       graphics_type = GRAPHICS_NONE;
                       graphic_outputInterval  = INT_MAX;
                    } else if (! strcmp(val,"data") ) {
                       graphics_type = GRAPHICS_DATA;
#ifdef HAVE_MAGICKWAND
                    } else if (! strcmp(val,"bmp") ) {
                       graphics_type = GRAPHICS_BMP;
                    } else if (! strcmp(val,"gif") ) {
                       graphics_type = GRAPHICS_GIF;
                    } else if (! strcmp(val,"jpeg") ) {
                       graphics_type = GRAPHICS_JPEG;
                    } else if (! strcmp(val,"mpeg") ) {
                       graphics_type = GRAPHICS_MPEG;
                    } else if (! strcmp(val,"pdf") ) {
                       graphics_type = GRAPHICS_PDF;
                    } else if (! strcmp(val,"png") ) {
                       graphics_type = GRAPHICS_PNG;
                    } else if (! strcmp(val,"svg") ) {
                       graphics_type = GRAPHICS_SVG;
#endif
                    } else {
                       printf("Unrecognized option for graphics file type %s\n",val);
                       exit(-1);
                    }
                    break;

                case 'h':   //  Output help.
                    outputHelp();
                    cout.flush();
                    exit(EXIT_SUCCESS);
                    break;
                    
                case 'i':   //  Output interval specified.
                    val = strtok(argv[i++], " ,.-");
                    outputInterval = atoi(val);
                    break;
                    
                case 'l':   //  max level specified.
                    val = strtok(argv[i++], " ,");
                    levmx = atoi(val);
                    break;
                    
                case 'M':   //  memory optimization factor
                    val = strtok(argv[i++], " ,");
                    mem_opt_factor = atof(val);
                    break;
                    
                case 'm':   //  partition measure specified.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"no_partition_measure") ) {
                       measure_type = NO_PARTITION_MEASURE;
                    } else if (! strcmp(val,"with_duplicates") ) {
                       measure_type = WITH_DUPLICATES;
                    } else if (! strcmp(val,"without_duplicates") ) {
                       measure_type = WITHOUT_DUPLICATES;
                    } else if (! strcmp(val,"cvalue") ) {
                       measure_type = CVALUE;
                    } else if (! strcmp(val,"cstarvalue") ) {
                       measure_type = CSTARVALUE;
                    }
                    break;
                    
                case 'N':   //  calc neighbor type specified.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"hash_table") ) {
                       calc_neighbor_type = HASH_TABLE;
                    } else if (! strcmp(val,"kdtree") ) {
                       calc_neighbor_type = KDTREE;
                    }
                    break;
                    
                case 'n':   //  Domain grid resolution specified.
                    val = strtok(argv[i++], " ,");
                    nx = atoi(val);
                    ny = nx;
                    break;
                    
                case 'o':   //  Turn off outlines on mesh drawing.
                    outline = false;
                    break;
                    
                case 'P':   //  Initial order specified.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"original_order") ) {
                       initial_order = ORIGINAL_ORDER;
                    } else if (! strcmp(val,"hilbert_sort") ) {
                       initial_order = HILBERT_SORT;
                    } else if (! strcmp(val,"hilbert_partition") ) {
                       initial_order = HILBERT_PARTITION;
                    } else if (! strcmp(val,"z_order") ) {
                       initial_order = ZORDER;
                    }
                    break;
                    
                case 'p':   //  Initial order specified.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"original_order") ) {
                       cycle_reorder = ORIGINAL_ORDER;
                       localStencil = false;
                    } else if (! strcmp(val,"hilbert_sort") ) {
                       cycle_reorder = HILBERT_SORT;
                       localStencil = false;
                    } else if (! strcmp(val,"hilbert_partition") ) {
                       cycle_reorder = HILBERT_PARTITION;
                       localStencil = false;
                    } else if (! strcmp(val,"local_hilbert") ) {
                       cycle_reorder = ORIGINAL_ORDER;
                       localStencil = true;
                    } else if (! strcmp(val,"local_fixed") ) {
                       cycle_reorder = ORIGINAL_ORDER;
                       localStencil = false;
                    } else if (! strcmp(val,"z_order") ) {
                       cycle_reorder = ZORDER;
                       localStencil = false;
                    }
                    break;
   
                case 'q':   //  turn on quo package.
#ifdef HAVE_QUO
                    do_quo_setup = 1;
#endif
                    break;
                    
                case 'r':   //  Regular sum instead of enhanced precision sum.
                    val = strtok(argv[i++], " ,");
                    if (! strcmp(val,"regular_sum") ) {
                       enhanced_precision_sum = SUM_REGULAR;
                    } else if (! strcmp(val,"kahan_sum") ) {
                       enhanced_precision_sum = SUM_KAHAN;
                    } else {
                       printf("Error with sum argument %s\n",val);
                       exit(0);
                    }
                    break;

                case 'R':  //  Restart application from last checkpoint
                    restart = true;
                    restart_file = strtok(argv[i++], " ,");

#ifndef HDF5_FF
                    struct stat stat_descriptor;
                    if (stat(restart_file,&stat_descriptor) == -1){
                       printf("Error -- restart file %s does not exist\n",restart_file);
                       exit(0);
                    }
#endif
                    break;

                case 's':   //  Space-filling curve method specified (default HILBERT_SORT).
                //  Add different problem setups such as sloped wave in x, y and diagonal directions to help check algorithm
                    //  HILBERT_SORT
                    break;
                    
                case 'T':   //  TVD inclusion specified.
                    break;
                    
                case 't':   //  Number of time steps specified.
                    val = strtok(argv[i++], " ,.-");
                    niter = atoi(val);
                    break;

                case 'u':   //  Allowed percentage of difference in mass per iteration
                    val = strtok(argv[i++], " ,");
                    upper_mass_diff_percentage = atof(val);
                    break;
                    
                case 'V':   //  Verbose output desired.
                    verbose = true;
                    break;
                    
                case 'v':   //  Version.
                    outputVersion();
                    cout.flush();
                    exit(EXIT_SUCCESS);
                    break;

                case 'z':  // Neighbor remap -- default is true, -z sets to false
                    neighbor_remap = false;
                    break;
                    
                default:    //  Unknown parameter encountered.
                    cout << "⚠ Unknown input parameter " << val << endl;
                    outputHelp();
                    cout.flush();
                    exit(EXIT_FAILURE);
                    break; }
            
            val = strtok(argv[i++], " ,.-");
        }
     }

/*
    if(upper_mass_diff_percentage < 0){
             upper_mass_diff_percentage = 1.0e-12;
    }
*/
}
