/**
* Copyright 2016 BitTorrent Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#include <scraps/config.h>

#include <scraps/Temp.h>
#include <scraps/type-traits.h>
#include <scraps/utility.h>

SCRAPS_IGNORE_WARNINGS_PUSH
#include <gsl.h>
SCRAPS_IGNORE_WARNINGS_POP

#include <array>

namespace scraps {

/**
 * Convenience functions to create an std::array from a gsl::span
 */
template <typename T, std::ptrdiff_t ArraySize, template <typename, std::ptrdiff_t...> class Span>
constexpr std::array<std::remove_cv_t<T>, ArraySize> ToArray(Span<T, ArraySize> span) {
    static_assert(ArraySize != gsl::dynamic_range, "Dynamic range spans are not allowed.");
    static_assert(ArraySize > 0, "ArraySize must be greater than 0");

    std::array<std::remove_cv_t<T>, ArraySize> ret{};
    std::copy(span.begin(), span.end(), ret.begin());
    return ret;
}

template <typename T, std::ptrdiff_t ArraySize, template <typename, std::ptrdiff_t...> class Span>
constexpr std::array<std::remove_cv_t<T>, ArraySize> ToArray(Temp<Span<T, ArraySize>> span) {
    return ToArray(static_cast<Span<T, ArraySize>>(span));
}

template <typename T, size_t N>
constexpr std::array<std::remove_cv_t<T>, N> ToArray(T (&arr)[N]) {
    std::array<std::remove_cv_t<T>, N> ret{};
    std::copy(arr, arr + N, ret.begin());
    return ret;
}

/**
 * Convenience type for defining a c-style array using the template parameters
 * of a std::array.
 */
template <typename ArrayT>
using CArray = typename RemoveCVRType<ArrayT>::value_type[std::tuple_size<RemoveCVRType<ArrayT>>::value];

/**
 * Hashing struct suitable for using an std::array as the key in stl
 * associative containers such as unordered_map.
 */
struct ArrayHasher {
    template <typename T, size_t N>
    size_t operator()(const std::array<T, N>& arr) const {
        return HashRange(arr.begin(), arr.end());
    }
};

} // namespace scraps

namespace std {
    template<typename T, size_t N>
    struct hash<std::array<T, N>> {
        size_t operator()(const std::array<T, N>& array) const {
            return scraps::ArrayHasher{}(array);
        }
    };
} // namespace std


#ifndef API_H_INCLUDED
#define API_H_INCLUDED

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <plplot/plplot.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "stack.h"
#include "utf8.h"
#include "gdate.h"
#include <setjmp.h>

#if defined(_MSC_VER) || defined(_WIN32)
#include <Windows.h>
#endif

#define RECORD_SEP '~'

// Parse errors
#define KEE_UNQU    0x01 // unmatched quotation marks
#define KEE_UNLP    0x02 // unmatched left parentheses
#define KEE_UNRP    0x04 // unmatched right parentheses
#define KEE_UNOP    0x08 // unknown operators
#define KEE_FUNC    0x10 // wrong function syntax
#define KEE_ARG     0x20
#define KEE_NUM     0x40 // fail to parse a number
//#define KEE_END     0x80 // fail to parse a number

// Evaluation errors
#define KEE_UNFUNC  0x40 // undefined function
#define KEE_UNVAR   0x80 // unassigned variable

// Return type
#define KEV_REAL 1
#define KEV_INT  2
#define KEV_STR  3
#define KEV_VEC  4
#define KEV_MAT  5
#define KEV_COMPLEX 6
#define KEV_FNC  7
#define KEV_REC  8
#define KEV_VEC_INT  9
#define KEV_DEF  10
#define KEV_IMAGE 11
#define KEV_FILE 12
#define KEV_BUFFER 13
#define KEV_DATE 14
#define KEV_PTR   15
#define KEV_FPOS  16

#define KEV_REAL_S "REAL"
#define KEV_INT_S "INT"
#define KEV_STR_S "STR"
#define KEV_VEC_S  "VEC"
#define KEV_MAT_S  "MAT"
#define KEV_COMPLEX_S "COMPLEX"
#define KEV_CMD_S  "CMD"
#define KEV_FNC_S  "FNC"
#define KEV_REC_S  "REC"
#define KEV_VEC_INT_S  "VEC_INT"
#define KEV_DEF_S  "DEF"
#define KEV_IMAGE_S "IMAGE"
#define KEV_FILE_S "FILE"
#define KEV_BUFFER_S "BUFFER"
#define KEV_DATE_S "DATE"
#define KEV_PTR_S  "PTR"
#define KEV_FPOS_S  "FPOS"

extern char * kev_to_str[17];

#define KEO_NULL  0
#define KEO_POS   1
#define KEO_NEG   2
#define KEO_BNOT  3
#define KEO_LNOT  4
#define KEO_POW   5
#define KEO_MUL   6
#define KEO_DIV   7
#define KEO_IDIV  8
#define KEO_MOD   9
#define KEO_ADD  10
#define KEO_SUB  11
#define KEO_LSH  12
#define KEO_RSH  13
#define KEO_LT   14
#define KEO_LE   15
#define KEO_GT   16
#define KEO_GE   17
#define KEO_EQ   18
#define KEO_NE   19
#define KEO_BAND 20
#define KEO_BXOR 21
#define KEO_BOR  22
#define KEO_LAND 23
#define KEO_LOR  24
#define KEO_LET  25
#define KEO_NOP  26

#define KEO_MAX  27

#define KET_NULL  0
#define KET_DEFNAME 1
#define KET_CMD   2
#define KET_VCMD  3
#define KET_OP    4
#define KET_FUNC  5
#define KET_REC   6
#define KET_XPROP 7
#define KET_PROP  8
#define KET_VAL_SEP 10
#define KET_CONST 11
#define KET_VAL   12
#define KET_FILE  13
#define KET_XNAME 14 //already set when it's VNAME
#define KET_VNAME 15

#define KEF_NULL  0
#define KEF_REAL  1
#define KEF_LET   2
#define KEF_GET   3

#include "kdq.h"
#include "khash.h"

struct token_s;
struct kexpr_s;
struct sml_s;

typedef char* strptr;
typedef struct token_s* ke1_p;
typedef int(*cmdp)(struct sml_s * sml, int);
typedef void(*fncp)(struct sml_s *);
typedef int(*vcmdp)(struct sml_s * sml, int);
typedef int(__cdecl *dllke_hash_add_t)(struct sml_s *, fncp, char *);
typedef struct token_s *(__cdecl *dllke_get_out_t)(struct sml_s *);

KHASH_MAP_INIT_STR(0, cmdp)
KHASH_MAP_INIT_STR(1, vcmdp)
KHASH_MAP_INIT_STR(2, int)
KHASH_MAP_INIT_STR(3, int)
KHASH_MAP_INIT_STR(5, fncp)
KHASH_MAP_INIT_STR(6, int)
KHASH_MAP_INIT_STR(7, int)

#pragma warning( push )
#pragma warning( disable : 273)
#pragma warning( disable : 4018)
#pragma warning( disable : 4334)
#pragma warning( disable : 4627)
#pragma warning( disable : 4267)
KDQ_INIT(int)
#pragma warning( pop )

struct sml_s;
typedef struct sml_s {
	int is_execute;
	int topGlobal;
	struct token_s *def_name;      // current def name to put the stack ref
	struct token_s **stack;        // stack for the evaluation of the program
	double *rstack;                    // stack for the evaluation of the program
	struct token_s *out;           // pointer to the array of out parameter to put into the stack
	struct kexpr_s *kexpr;
	struct token_s ** fields;		// array of all global fields of the program to exectue
	struct token_s ** tokens;     // array of pointers of all program tokens
	int top;
	int localtop;				// will be init with gfield_qte;
	int inittop;				// will be init with gfield_qte;
	struct token_s * tokp;
	int out_qte;
	int tok_idx;
	int val;
	jmp_buf env_buffer;
	// GLOBAL VARIABLE USED BY ALL FUNCTIONS
	int field_qte;				// number of local fields, reset for each def
	int gfield_qte;				// number of global fields
	int mem_count;				// current count of memory allocation
								// parser variables
	int isNextDefName;			// flag to indicate that the next token is the def name
	char currentDefName[100];	// current name of the current function
	int sourceCodeLine;			// use to keep the line number to show better error trapping
	int isFirstToken;			// use to remove separators at the beginning of the program
	int isLastTokenNop;			// use to manage the command seperator, the rule is to have one separator between each comand
	char lastErrorMessage[256]; //
	struct token_s * recp[100]; // local record fields
	struct token_s * grecp[100];// global record fields
	int rec_qte;        // number of local record fields
	int grec_qte;        // number of global record fields
	khash_t(5) *hfunction;
	khash_t(6) *hname;  // function name = negative stack
	khash_t(7) *gname;  // global name = positif stack
#if defined(_MSC_VER) || defined(_WIN32)
	HMODULE libhandle[64];
	int libhandle_qte;      // number of global fields
#endif
	kdq_t(int) *callstack;
	kdq_t(int) *hiforcommand;
	kdq_t(int) *hinextcommand;
	khash_t(3) *hidefcommand;
	khash_t(0) *hcommand;
	khash_t(1) *hvcommand;
	kstack_t *harg;
	int g_forstack[20];
	int g_fortop;
	int lastDef;
	dllke_hash_add_t dllke_hash_add;
	dllke_get_out_t dllke_get_out;
} sml_t;

struct kexpr_s;
typedef struct kexpr_s kexpr_t;
typedef int * reclistt;

struct token_s;
typedef struct token_s {
	int64_t i;
	double r;
	union {    //
		void(*builtin)(struct token_s *a, struct token_s *b, struct token_s *out); // execution function
		double(*real_func1)(double);
		double(*real_func2)(double, double);
		int(*defprop)(sml_t* sml, struct token_s* prop, int top);
		void(*deffunc)(sml_t* sml);
		int(*defcmd)(sml_t* sml, int itok);
		int(*defvcmd)(sml_t* sml, int itok);
		struct token_s * recp;
	} f;
	union {  //
		void * ptr;
		struct token_s * tokp;
		char *s;
		char* image;
		char ** ms;
		fpos_t * fpos;
		FILE *file;
		void *buffer;
		GDate_t * date;
		gsl_vector_int * vector_int;
		gsl_vector * vector;
		gsl_matrix * matrix;
		gsl_complex tcomplex;
		reclistt reclist;
		PLGraphicsIn * plgrin;
		PLPointer * plptr;
		PLTRANSFORM_callback pltrcb;
		PLMAPFORM_callback plmpcb;
		PLDEFINED_callback pldefcb;
		PLLABEL_FUNC_callback pllblcb;
		PLFILL_callback plfillcb;
	} obj;
	char *name; // variable name or function name
	int32_t ijmp;
	int32_t sourceLine; // fast jmp
	uint16_t ttype;
	uint16_t vtype;
	int16_t ifield;
	int8_t icmd;
	int8_t assigned;
	int8_t propget;
	int8_t propset;
	int8_t tofree;
	int8_t islocal;
	int8_t op;
	int8_t n_args;
	int8_t realToken;
	int8_t isfor;
} token_t;

struct kexpr_s {
	int n;
	token_t *e;
};

token_t * ke_get_out(sml_t *sml);
void ke_hash_add(sml_t *sml, fncp key, char * name);
void ke_inc_memory(sml_t *sml);
void ke_dec_memory(sml_t *sml);
void * ke_calloc_memory(sml_t *sml, size_t i, size_t x);
void * ke_malloc_memory(sml_t *sml, size_t i);
void ke_free_memory(sml_t *sml, void * m);
void ke_print_one(sml_t *sml, token_t* t);
gsl_matrix * sml_pop_set_matrix(sml_t * sml);
gsl_vector * sml_pop_set_vector(sml_t * sml);
gsl_vector_int * sml_pop_set_vector_int(sml_t * sml);
gsl_matrix * sml_peek_set_matrix(sml_t * sml, int n);
gsl_vector * sml_peek_set_vector(sml_t * sml, int n);
gsl_vector_int * sml_peek_set_vector_int(sml_t * sml, int n);
void * sml_peek_set_ptr(sml_t * sml, int n);

#define __GLOBAL "g_"
#define __GLOBAL_SEP "_"
#define __GLOBAL_DSEP "__"

#define sml_get_top(sml) sml->top
#define sml_dec_top(sml) --sml->top
#define sml_set_top(sml,top) sml->top=top
#define sml_get_stack(sml) sml->stack
#define sml_get_tokp(sml) sml->tokp
#define sml_get_args(sml) sml->tokp->n_args
#define sml_set_args(sml,a) sml->tokp->n_args = a
#define sml_get_ttype(sml) sml->tokp->ttype
#define sml_get_vtype(sml) sml->tokp->vtype
#define sml_get_ijmp(sml) sml->tokp->ijmp
#define sml_get_assigned(sml) sml->tokp->assigned
#define sml_set_assigned(sml,a) sml->tokp->assigned = a

#define sml_pop_token(sml) sml->stack[--sml->top]
#define sml_pop_int(sml) sml->stack[--sml->top]->i
#define sml_pop_real(sml) sml->stack[--sml->top]->r
#define sml_pop_ptr(sml) sml->stack[--sml->top]->obj.ptr
#define sml_pop_file(sml) sml->stack[--sml->top]->obj.file
#define sml_pop_fpos(sml) sml->stack[--sml->top]->obj.fpos
#define sml_pop_str(sml) sml->stack[--sml->top]->obj.s
#define sml_pop_date(sml) sml->stack[--sml->top]->obj.date
#define sml_pop_complex(sml) sml->stack[--sml->top]->obj.tcomplex
#define sml_pop_complex_adr(sml) &sml->stack[--sml->top]->obj.tcomplex
#define sml_pop_day(sml) sml->stack[--sml->top]->i
#define sml_pop_month(sml) sml->stack[--sml->top]->i
#define sml_pop_year(sml) sml->stack[--sml->top]->i
#define sml_pop_matrix(sml) sml->stack[--sml->top]->obj.matrix
#define sml_pop_vector(sml) sml->stack[--sml->top]->obj.vector
#define sml_pop_vector_int(sml) sml->stack[--sml->top]->obj.vector_int

#define sml_peek_token(sml,i) sml->stack[i]
#define sml_peek_int(sml,ii) sml->stack[ii]->i
#define sml_peek_real(sml,i) sml->stack[i]->r
#define sml_peek_ptr(sml,i) sml->stack[i]->obj.ptr
#define sml_peek_file(sml,i) sml->stack[i]->obj.file
#define sml_peek_fpos(sml,i) sml->stack[i]->obj.fpos
#define sml_peek_str(sml,i) sml->stack[i]->obj.s
#define sml_peek_date(sml,i) sml->stack[i]->obj.date
#define sml_peek_complex(sml,i) sml->stack[i]->obj.tcomplex
#define sml_peek_complex_adr(sml,i) &sml->stack[i]->obj.tcomplex
#define sml_peek_day(sml,i) (GDateDay)sml->stack[i]->i
#define sml_peek_month(sml,i) (GDateMonth)sml->stack[i]->i
#define sml_peek_year(sml,i) (GDateYear)sml->stack[i]->i
#define sml_peek_matrix(sml,i) sml->stack[i]->obj.matrix
#define sml_peek_vector(sml,i) sml->stack[i]->obj.vector
#define sml_peek_vector_int(sml,i) sml->stack[i]->obj.vector_int

#define sml_get_complex_adr  &out->obj.tcomplex

#define sml_get_type(t) t->vtype
#define sml_get_str(t) t->obj.s
#define sml_get_real(t) t->r
#define sml_get_int(t) t->i
#define sml_get_ifield(t) t->ifield

#define sml_set_int(sml,t,ii) { int i = t->ifield; \
	if (i < 0) { \
		i += sml->localtop; \
	} \
	t = sml->fields[i]; \
	t->i = ii; }

#define sml_get_ptr(t) t->obj.ptr
#define sml_get_file(t) t->obj.file
#define sml_get_buffer(t) t->obj.buffer
#define sml_get_fpos(t) t->obj.fpos

#define sml_set_fpos(sml,t,fposp) { int i = t->ifield; \
	if (i < 0) { \
		i += sml->localtop; \
	} \
	t = sml->fields[i]; \
	t->obj.fpos = fposp; }

#define sml_set_ptr_null(sml,t) { int i = t->ifield; \
	if (i < 0) { \
		i += sml->localtop; \
	} \
	t = sml->fields[i]; \
	t->obj.ptr = NULL; }

#define sml_get_len(t) t->i
#define sml_adr_str(t) &t->obj.s
#define sml_adr_ptr(t) &t->obj

#define sml_get_matrix(t) t->obj.matrix
#define sml_get_vector(t) t->obj.vector
#define sml_get_vector_int(t) t->obj.vector_int
#define sml_get_complex(t) t->obj.complex

#define sml_push_buffer(sml, b, ii) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.buffer = b; \
	out->i = ii; \
	out->r == (double)ii; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_BUFFER;

#define sml_push_file(sml, file) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.file = file; \
	out->i = 1; \
	out->r == 1.0; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_FILE;

#define sml_push_new_complex(sml) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_COMPLEX;

#define sml_push_complex(sml, z) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.tcomplex = z; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_COMPLEX;

#define sml_push_real(sml, r) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->r = r; \
	out->i = (int64_t)r; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_REAL;

#define sml_push_date(sml, dt) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.date = dt; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_DATE;

#define  sml_push_int(sml, ii) token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->i = ii; \
	out->r = (double)ii; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_INT;

#define  sml_push_ptr(sml, ptr) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.ptr = ptr; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_PTR;

#define  sml_push_str(sml, str) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.s = str; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_STR;

#define  sml_push_matrix(sml, m) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.matrix = m; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_MAT;

#define  sml_push_vector(sml, v) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.vector = v; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_VEC;

#define  sml_push_vector_int(sml, v) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.vector_int = v; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_VEC_INT;

#define  sml_push_image(sml, image) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.image = image; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_IMAGE;

#define  sml_push_fpos(sml, fposp) \
	token_t* out; \
	sml->stack[sml->top] = ke_get_out(sml); \
	out = sml->stack[sml->top++]; \
	out->obj.fpos = fposp; \
	out->ttype = KET_VAL; \
	out->vtype = KEV_FPOS;

#define sml_out_date out->obj.date

#define sml_free_ptr(sml,ptr) ke_free_memory(sml,ptr); ptr = NULL

#define sml_new_ptr(sml,size) ke_calloc_memory(sml, size,1)

#define sml_mem_inc ke_inc_memory

#define sml_save_str(sml, tokp, tmp) \
	int k = tokp->ifield; \
    if (k < 0) \
		k += sml->localtop; \
	tokp = sml->fields[k]; \
	if (tokp->vtype == KEV_STR && tokp->obj.s) { \
		ke_free_memory(sml, tokp->obj.s); \
	} \
	tokp->obj.s = tmp; \
	tokp->i = 0, tokp->r = 0 ; \
	tokp->vtype = KEV_STR;

#endif

#ifdef _DEBUG
#define sml_assert_args(sml,i,fnc) if (sml->tokp->n_args != i) { \
		printf("ASSERT ERROR : Invalid number of parameters, expected <%d> got <%d> at line <%d> for function <%s>", i, sml->tokp->n_args, sml->tokp->sourceLine,fnc);	\
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_args(sml,i,s)
#endif

#ifdef _DEBUG
#define sml_assert_args_min(sml,i,fnc) if (sml->tokp->n_args < i) { \
		printf("ASSERT ERROR : Invalid number of parameters, expected at least <%d> got <%d> at line <%d> for function <%s>", i, sml->tokp->n_args, sml->tokp->sourceLine,fnc);	\
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_args_min(sml,i,fnc)
#endif

#ifdef _DEBUG
#define sml_assert_type(sml,i,t,fnc) \
	if (sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->vtype != t) { \
		printf("ASSERT ERROR : Invalid type for parameter #%d, expected <%s> got <%s> at line <%d> for function <%s>", i, kev_to_str[t], kev_to_str[sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->vtype], sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_type(sml,i,t,s)
#endif

#ifdef _DEBUG
#define sml_assert_type_int_or_real(sml,i,fnc) \
	if (sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->vtype != KEV_INT && sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->vtype != KEV_REAL) { \
		printf("ASSERT ERROR : Invalid type for parameter #%d, expected <INT OR REAL> got <%s> at line <%d> for function <%s>", i, kev_to_str[sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->vtype], sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_type_int_or_real(sml,i,s)
#endif

#ifdef _DEBUG
#define sml_assert_str_in(sml,i,search,fnc) \
	if (!strstr(search, sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->obj.s)) { \
		printf("ASSERT ERROR : Invalid parameter value for parameter #%d, at line <%d> for function <%s>", i, sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_str_in(sml,i,f,s)
#endif

#ifdef _DEBUG
#define sml_assert_str(sml,i,fnc) \
	if (!strlen(sml->stack[sml->top - 1 - (sml->tokp->n_args - i)]->obj.s)) { \
		printf("ASSERT ERROR : Invalid parameter value (empty) for parameter #%d, at line <%d> for function <%s>", i, sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_str(sml,i,fnc)
#endif

#ifdef _DEBUG
#define sml_assert_size(sml,size,i,fnc) \
	if (size <= 0) { \
		printf("ASSERT ERROR : Invalid parameter value (<=0) for parameter #%d, at line <%d> for function <%s>", i, sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_size(sml,size,i,fnc)
#endif

#ifdef _DEBUG
#define sml_assert_gz(sml,size,fnc) \
	if (size < 0) { \
		printf("ASSERT ERROR : Invalid parameters (size validation < 0) at line <%d> for function <%s>", sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_gz(sml,size,fnc)
#endif

#ifdef _DEBUG
#define sml_assert_ptr(sml,p,i,fnc) \
	if (p == NULL) { \
		printf("ASSERT ERROR : Invalid parameter value (==NULL) for parameter #%d, at line <%d> for function <%s>", i, sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_ptr(sml,p,i,fnc)
#endif

#ifdef _DEBUG
#define sml_assert_range(sml,i,v,from,to,fnc) \
	if (v < from || v > to) { \
		printf("ASSERT ERROR : Invalid parameter value <%d> expected (%d - %d) for parameter #%d, at line <%d> for function <%s>", v, from, to, i, sml->tokp->sourceLine,fnc); \
		longjmp(sml->env_buffer, 1); \
	}
#else
#define sml_assert_range(sml,i,v,from,to,fnc)
#endif

#define sml_fatal_error(sml,c,fnc) \
		printf("ASSERT ERROR : Fatal error <%s> at line <%d> for function <%s>", c, sml->tokp->sourceLine , fnc); \
		longjmp(sml->env_buffer, 1);


#pragma once

#include "arcana/finally_scope.h"
#include "arcana/functional/inplace_function.h"
#include "arcana/sentry.h"
#include "arcana/threading/affinity.h"

#include <algorithm>
#include <tuple>
#include <vector>

#include <gsl/gsl>

namespace arcana
{
    using ticket_seed = int64_t;

    using ticket = gsl::final_action<
        stdext::inplace_function<void(), sizeof(std::aligned_storage_t<sizeof(ticket_seed) + sizeof(void*)>)>>;

    using ticket_scope = finally_scope<ticket>;

    /*
        An event routing class used to dispatch events to multiple listeners.
        Each router class can only handle a certain fixed set of event types defined by EventTs.
    */
    template<typename... EventTs>
    class router
    {
    public:
        static constexpr size_t LISTENER_SIZE = 4 * sizeof(int64_t);

        template<typename EventT>
        using listener_function = stdext::inplace_function<void(const EventT&), LISTENER_SIZE>;

        /*
            Sends an event synchronously to all listeners.
        */
        template<typename EventT>
        void fire(const EventT& evt)
        {
            GSL_CONTRACT_CHECK("thread affinity", m_affinity.check());

            using event = std::decay_t<EventT>;

            auto& listeners = std::get<listener_group<event>>(m_listeners);

            {
                auto guard = std::get<sentry<event>>(m_sentries).take();

                for (listener<event>& listener : listeners)
                {
                    if (listener.valid)
                    {
                        listener.callback(evt);
                    }
                }
            }

            // if we're no longer iterating the listeners list in this stack
            // remove all the unregistered listeners and add the pending ones
            if (!std::get<sentry<event>>(m_sentries).is_active())
            {
                listeners.erase(std::remove_if(listeners.begin(),
                                               listeners.end(),
                                               [](const listener<event>& l) { return !l.valid; }),
                                listeners.end());

                // move the pending listeners to the real list
                // and clear the pending list
                auto& pending = std::get<listener_group<event>>(m_pending);
                std::move(pending.begin(), pending.end(), std::back_inserter(listeners));
                pending.clear();
            }
        }

        /*
            Adds an event listener.
        */
        template<typename EventT, typename T>
        ticket add_listener(T&& listener)
        {
            auto id = internal_add_listener<EventT>(std::forward<T>(listener));

            return ticket{ [id, this] { internal_remove_listener<EventT>(id); } };
        }

        /*
            Sets the routers thread affinity. Once this is set the methods
            on this instance will need to be called by that thread.
        */
        void set_affinity(const affinity& aff)
        {
            m_affinity = aff;
        }

    private:
        /*
        Adds an event listener.
        */
        template<typename EventT, typename T>
        ticket_seed internal_add_listener(T&& listener)
        {
            GSL_CONTRACT_CHECK("thread affinity", m_affinity.check());

            using event = std::decay_t<EventT>;

            // if we're currently firing an event in that group we need to wait until we're done before
            // adding the listener to the list
            auto& listeners = std::get<sentry<event>>(m_sentries).is_active()
                                  ? std::get<listener_group<event>>(m_pending)
                                  : std::get<listener_group<event>>(m_listeners);

            auto id = m_nextId++;
            listeners.emplace_back(std::forward<T>(listener), id);
            return id;
        }

        /*
        Removes an event listener by id.
        */
        template<typename EventT>
        void internal_remove_listener(const ticket_seed& id)
        {
            GSL_CONTRACT_CHECK("thread affinity", m_affinity.check());

            using event = std::decay_t<EventT>;

            auto& listeners = std::get<listener_group<event>>(m_listeners);
            auto found = std::find_if(listeners.begin(), listeners.end(), [id](const listener<event>& listener) {
                return listener.id == id;
            });

            if (found == listeners.end())
            {
                assert(false && "removing item that isn't there");
                return;
            }

            // don't modify the collection while iterating, just disable the listener
            if (std::get<sentry<event>>(m_sentries).is_active())
            {
                found->valid = false;
            }
            else
            {
                listeners.erase(found);
            }
        }

        template<typename EventT>
        struct listener
        {
            using callback_t = listener_function<EventT>;

            callback_t callback;
            ticket_seed id;
            bool valid;

            listener(callback_t&& callback, const ticket_seed& id)
                : callback{ std::move(callback) }
                , id{ id }
                , valid{ true }
            {}

            listener(const callback_t& callback, const ticket_seed& id)
                : callback{ callback }
                , id{ id }
                , valid{ true }
            {}
        };

        template<typename EventT>
        using listener_group = std::vector<listener<EventT>>;

        std::tuple<listener_group<EventTs>...> m_listeners;
        std::tuple<sentry<EventTs>...> m_sentries;
        std::tuple<listener_group<EventTs>...> m_pending;

        affinity m_affinity;
        ticket_seed m_nextId;
    };
}


// Copyright © Matt Jones and Contributors. Licensed under the MIT Licence (MIT). See LICENCE.md in the repository root
// for more information.

#ifndef NOVELRT_UTILITIES_MISC_H
#define NOVELRT_UTILITIES_MISC_H

#include <filesystem>
#include <gsl/span>
#include <type_traits>

#if defined(NDEBUG)
#define unused(x) (void)(x)
#else
#define unused(x) (void)(0)
#endif

namespace NovelRT::Utilities
{
    class Misc
    {
    public:
        static inline const char* CONSOLE_LOG_GENERIC = "NovelRT";
        static inline const char* CONSOLE_LOG_APP = "Application";
        static inline const char* CONSOLE_LOG_DOTNET = ".NET";
        static inline const char* CONSOLE_LOG_GFX = "GFX";
        static inline const char* CONSOLE_LOG_STATS = "Statistics";
        static inline const char* CONSOLE_LOG_AUDIO = "Audio";
        static inline const char* CONSOLE_LOG_INPUT = "Input";
        static inline const char* CONSOLE_LOG_WINDOWING = "WindowManager";

        /**
         * @brief Gets the path to the executable.
         *
         * @return The path to the executable.
         */
        static std::filesystem::path getExecutablePath();

        /**
         * @brief Gets the path to the directory that contains the executable. <br/>
         * For example, `/home/stuff/game/best-game-executable` will return `/home/stuff/game`
         *
         * @return The path to the directory that contains the executable.
         */
        static std::filesystem::path getExecutableDirPath()
        {
            return getExecutablePath().parent_path();
        }

        [[nodiscard]] static std::vector<const char*> GetStringSpanAsCharPtrVector(
            const gsl::span<const std::string>& target) noexcept
        {
            size_t extensionLength = target.size();
            std::vector<const char*> targetPtrs{};
            targetPtrs.reserve(extensionLength);

            for (auto&& extension : target)
            {
                targetPtrs.emplace_back(extension.c_str());
            }

            return targetPtrs;
        }
    };
}

template<class T, class U = std::underlying_type_t<T>> constexpr T operator~(T a)
{
    return static_cast<T>(~static_cast<U>(a));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T operator|(T a, T b)
{
    return static_cast<T>((static_cast<U>(a) | static_cast<U>(b)));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T operator&(T a, T b)
{
    return static_cast<T>((static_cast<U>(a) & static_cast<U>(b)));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T operator^(T a, T b)
{
    return static_cast<T>((static_cast<U>(a) ^ static_cast<U>(b)));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T& operator|=(T& a, T b)
{
    return a = static_cast<T>((static_cast<U>(a) | static_cast<U>(b)));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T& operator&=(T& a, T b)
{
    return a = static_cast<T>((static_cast<U>(a) & static_cast<U>(b)));
}

template<class T, class U = std::underlying_type_t<T>> constexpr T& operator^=(T& a, T b)
{
    return a = static_cast<T>((static_cast<U>(a) ^ static_cast<U>(b)));
}

#endif //! NOVELRT_UTILITIES_MISC_H


#ifndef STDAFX_H
#define STDAFX_H
/*
 * Libs
 */
#include <qwt.h>
#include <qwt_legend.h>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_renderer.h>
#include <qwt_symbol.h>
#include <QtConcurrent/QtConcurrent>
#include <QtCore/QtCore>
#include <QtWidgets/QtWidgets>
#include <gsl/gsl>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
/*
 * STL
 */
#include <memory>
#include <vector>
/*
 * UTILITIES
 */
#include "macros.inl"
#endif  // !STDAFX_H


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef pool_81b70dbf_d70f_42d7_8770_678651c585c4_h
#define pool_81b70dbf_d70f_42d7_8770_678651c585c4_h

#include <assert.h>
#include <memory.h>
#include <stdlib.h>
#include <gslib/type.h>

__gslib_begin__

template<class _construct>
class factory
{
public:
    typedef _construct mycon;
    typedef factory<mycon> myref;

public:
    factory(void* ptr = nullptr) { _generated = (mycon*)ptr; }
    const mycon* get_ptr() const { return _generated; }
    mycon* get_ptr() { return _generated; }
    mycon* emerge()
    {
        if(!_generated)
            _generated = new mycon;
        assert(_generated);
        return _generated;
    }
    void destroy()
    {
        if(!_generated)
            return;
        delete _generated;
        _generated = nullptr;
    }

protected:
    mycon*              _generated;
};

class vessel
{
protected:
    void*               _buf;
    int                 _cap;
    int                 _cur;

public:
    vessel()
    {
        _buf = nullptr;
        _cap = 0;
        _cur = 0;
    }
    ~vessel()
    {
        destroy();
    }
    void destroy()
    {
        if(_buf) {
            free(_buf);
            _buf = nullptr;
        }
        _cap = _cur = 0;
    }
    int get_cap() const
    {
        return _cap;
    }
    int get_cur() const
    {
        return _cur;
    }
    void* get_ptr() const
    {
        return _buf;
    }
    int get_rest() const
    {
        return _cap - _cur;
    }
    void flex(int size)
    {
        _buf = realloc(_buf, size);
        _cap = size;
    }
    void expand(int size)
    {
        flex(get_cap() + size);
    }
    void fit()
    {
        _buf = realloc(_buf, _cur);
        _cap = _cur;
    }
    void occupy(int size)
    {
        _cur += size;
        assert(_cur <= get_cap());
    }
    void set_cur(int size)
    {
        _cur = size;
        assert(_cur <= get_cap());
    }
    int curaddr(int ofs = 0) const
    {
        return (int)_buf + _cur + ofs;
    }
    void store(const void* buf, int size)
    {
        if(_cur + size > get_cap())
            expand(get_cap() > size ? get_cap() : size);
        assert(get_cap() >= _cur + size);
        memcpy_s((void*)curaddr(), get_rest(), buf, size);
        occupy(size);
    }
    void attach(void* buf, int size)
    {
        flex(0);
        _buf = buf;
        _cap = size;
        _cur = size;
    }
    void attach(vessel* vsl)
    {
        assert(vsl);
        attach(vsl->get_ptr(), vsl->get_cap());
        _cur = vsl->get_cur();
        vsl->detach();
    }
    void detach()
    {
        _buf = nullptr;
        _cap = 0;
        _cur = 0;
    }
    template<class tpl>
    tpl& current(int ofs = 0)
    {
        return *((tpl*)((int)_buf + _cur + ofs));
    }
    template<class tpl>
    tpl& front(int ofs = 0)
    {
        return *((tpl*)((int)_buf + ofs));
    }
};

__gslib_end__

#endif


/* histogram/ntuple.h
 *
 * Copyright (C) 2000 Simone Piccardi
 *
 * This program 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 3 of the License, or (at
 * your option) any later version.
 *
 * This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

/* Jan/2001 Modified by Brian Gough. Minor changes for GSL */

#ifndef __GSL_NTUPLE_H__
#define __GSL_NTUPLE_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <stdio.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_histogram.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct {
    FILE * file;
    void * ntuple_data;
    size_t size;
} gsl_ntuple;

typedef struct {
  int (* function) (void * ntuple_data, void * params);
  void * params;
} gsl_ntuple_select_fn;

typedef struct {
  double (* function) (void * ntuple_data, void * params);
  void * params;
} gsl_ntuple_value_fn;

GSL_FUN gsl_ntuple *
gsl_ntuple_open (char * filename, void * ntuple_data, size_t size);

GSL_FUN gsl_ntuple *
gsl_ntuple_create (char * filename, void * ntuple_data, size_t size);

GSL_FUN int gsl_ntuple_write (gsl_ntuple * ntuple);
GSL_FUN int gsl_ntuple_read (gsl_ntuple * ntuple);

GSL_FUN int gsl_ntuple_bookdata (gsl_ntuple * ntuple);  /* synonym for write */

GSL_FUN int gsl_ntuple_project (gsl_histogram * h, gsl_ntuple * ntuple,
                        gsl_ntuple_value_fn *value_func,
                        gsl_ntuple_select_fn *select_func);

GSL_FUN int gsl_ntuple_close (gsl_ntuple * ntuple);

__END_DECLS

#endif /* __GSL_NTUPLE_H__ */


/* vector/gsl_vector_int.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program 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.
 *
 * This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_VECTOR_INT_H__
#define __GSL_VECTOR_INT_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_block_int.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size;
  size_t stride;
  int *data;
  gsl_block_int *block;
  int owner;
}
gsl_vector_int;

typedef struct
{
  gsl_vector_int vector;
} _gsl_vector_int_view;

typedef _gsl_vector_int_view gsl_vector_int_view;

typedef struct
{
  gsl_vector_int vector;
} _gsl_vector_int_const_view;

typedef const _gsl_vector_int_const_view gsl_vector_int_const_view;

/* Allocation */

GSL_EXPORT gsl_vector_int *gsl_vector_int_alloc (const size_t n);
GSL_EXPORT gsl_vector_int *gsl_vector_int_calloc (const size_t n);

GSL_EXPORT gsl_vector_int *gsl_vector_int_alloc_from_block (gsl_block_int * b,
                                                                const size_t offset,
                                                                const size_t n,
                                                                const size_t stride);

GSL_EXPORT gsl_vector_int *gsl_vector_int_alloc_from_vector (gsl_vector_int * v,
                                                                 const size_t offset,
                                                                 const size_t n,
                                                                 const size_t stride);

GSL_EXPORT void gsl_vector_int_free (gsl_vector_int * v);

/* Views */

GSL_EXPORT
_gsl_vector_int_view
gsl_vector_int_view_array (int *v, size_t n);

GSL_EXPORT
_gsl_vector_int_view
gsl_vector_int_view_array_with_stride (int *base,
                                         size_t stride,
                                         size_t n);

GSL_EXPORT
_gsl_vector_int_const_view
gsl_vector_int_const_view_array (const int *v, size_t n);

GSL_EXPORT
_gsl_vector_int_const_view
gsl_vector_int_const_view_array_with_stride (const int *base,
                                               size_t stride,
                                               size_t n);

GSL_EXPORT
_gsl_vector_int_view
gsl_vector_int_subvector (gsl_vector_int *v,
                            size_t i,
                            size_t n);

GSL_EXPORT
_gsl_vector_int_view
gsl_vector_int_subvector_with_stride (gsl_vector_int *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

GSL_EXPORT
_gsl_vector_int_const_view
gsl_vector_int_const_subvector (const gsl_vector_int *v,
                                  size_t i,
                                  size_t n);

GSL_EXPORT
_gsl_vector_int_const_view
gsl_vector_int_const_subvector_with_stride (const gsl_vector_int *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

GSL_EXPORT int gsl_vector_int_get (const gsl_vector_int * v, const size_t i);
GSL_EXPORT void gsl_vector_int_set (gsl_vector_int * v, const size_t i, int x);

GSL_EXPORT int *gsl_vector_int_ptr (gsl_vector_int * v, const size_t i);
GSL_EXPORT const int *gsl_vector_int_const_ptr (const gsl_vector_int * v, const size_t i);

GSL_EXPORT void gsl_vector_int_set_zero (gsl_vector_int * v);
GSL_EXPORT void gsl_vector_int_set_all (gsl_vector_int * v, int x);
GSL_EXPORT int gsl_vector_int_set_basis (gsl_vector_int * v, size_t i);

GSL_EXPORT int gsl_vector_int_fread (FILE * stream, gsl_vector_int * v);
GSL_EXPORT int gsl_vector_int_fwrite (FILE * stream, const gsl_vector_int * v);
GSL_EXPORT int gsl_vector_int_fscanf (FILE * stream, gsl_vector_int * v);
GSL_EXPORT int gsl_vector_int_fprintf (FILE * stream, const gsl_vector_int * v,
                                         const char *format);

GSL_EXPORT int gsl_vector_int_memcpy (gsl_vector_int * dest, const gsl_vector_int * src);

GSL_EXPORT int gsl_vector_int_reverse (gsl_vector_int * v);

GSL_EXPORT int gsl_vector_int_swap (gsl_vector_int * v, gsl_vector_int * w);
GSL_EXPORT int gsl_vector_int_swap_elements (gsl_vector_int * v, const size_t i, const size_t j);

GSL_EXPORT int gsl_vector_int_max (const gsl_vector_int * v);
GSL_EXPORT int gsl_vector_int_min (const gsl_vector_int * v);
GSL_EXPORT void gsl_vector_int_minmax (const gsl_vector_int * v, int * min_out, int * max_out);

GSL_EXPORT size_t gsl_vector_int_max_index (const gsl_vector_int * v);
GSL_EXPORT size_t gsl_vector_int_min_index (const gsl_vector_int * v);
GSL_EXPORT void gsl_vector_int_minmax_index (const gsl_vector_int * v, size_t * imin, size_t * imax);

GSL_EXPORT int gsl_vector_int_add (gsl_vector_int * a, const gsl_vector_int * b);
GSL_EXPORT int gsl_vector_int_sub (gsl_vector_int * a, const gsl_vector_int * b);
GSL_EXPORT int gsl_vector_int_mul (gsl_vector_int * a, const gsl_vector_int * b);
GSL_EXPORT int gsl_vector_int_div (gsl_vector_int * a, const gsl_vector_int * b);
GSL_EXPORT int gsl_vector_int_scale (gsl_vector_int * a, const double x);
GSL_EXPORT int gsl_vector_int_add_constant (gsl_vector_int * a, const double x);

GSL_EXPORT int gsl_vector_int_isnull (const gsl_vector_int * v);

#ifdef HAVE_INLINE

extern inline
int
gsl_vector_int_get (const gsl_vector_int * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

extern inline
void
gsl_vector_int_set (gsl_vector_int * v, const size_t i, int x)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

extern inline
int *
gsl_vector_int_ptr (gsl_vector_int * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (int *) (v->data + i * v->stride);
}

extern inline
const int *
gsl_vector_int_const_ptr (const gsl_vector_int * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (i >= v->size)
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const int *) (v->data + i * v->stride);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_INT_H__ */


/*
   Copyright [2020] [IBM Corporation]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

/*
 * Authors:
 *
 */

#ifndef _NUPM_OPENED_SPACE_H_
#define _NUPM_OPENED_SPACE_H_

#include <common/byte_span.h>
#include <common/fd_locked.h>
#include <common/logging.h> /* log_source */
#include <common/memory_mapped.h>
#include <common/moveable_ptr.h>
#include <common/string_view.h>
#include <common/types.h> /* addr_t */
#include <gsl/pointers>
#include <cassert>
#include <cstddef>
#include <string>
#include <vector>

namespace nupm
{
struct dax_manager;

struct range_use
{
private:
  common::moveable_ptr<dax_manager> _dm;
  /* Note: arena_fs used multiple ranges */
  std::vector<common::memory_mapped> _iovm;

  std::vector<common::memory_mapped> address_coverage_check(std::vector<common::memory_mapped> &&iovm);
  using byte = common::byte;
  using byte_span = common::byte_span;
public:
#if 0
  const auto & operator[](std::size_t i) const { return _iovm.at(i).iov(); }
#else
  byte_span operator[](std::size_t i) const { const auto &iov = _iovm.at(i).iov(); return common::make_byte_span(::base(iov), ::size(iov)); }
#endif
  range_use(dax_manager *dm_, std::vector<common::memory_mapped> &&);
  range_use(const range_use &) = delete;
  range_use &operator=(const range_use &) = delete;
  range_use(range_use &&) noexcept = default;
  void grow(std::vector<common::memory_mapped> &&);
  void shrink(std::size_t size);
  ~range_use();
  gsl::not_null<dax_manager *> dm() const { return _dm; }
  ::off_t size() const;
};

struct space_opened : private common::log_source
{
private:
  using byte_span = common::byte_span;
  common::fd_locked _fd_locked;
  /* Note: arena_fs may someday use multiple ranges */
  range_use _range;

  /* owns the file mapping */
  std::vector<common::memory_mapped> map_dev(int fd, const addr_t base_addr);
  std::vector<common::memory_mapped> map_fs(int fd, const std::vector<byte_span> &mapping, ::off_t offset);
public:
  space_opened(const common::log_source &, dax_manager * dm_, common::fd_locked && fd, const addr_t base_addr);
  space_opened(const common::log_source &, dax_manager * dm_, common::fd_locked && fd, const std::vector<byte_span> &mapping);
  space_opened(space_opened &&) noexcept = default;
  void shrink(std::size_t size);
  void grow(std::vector<byte_span> && iovv);
  int fd() const { return _fd_locked.fd(); }
  range_use &range() { return _range; }
};
}
#endif


#pragma once

#include <cstddef>
#include <gsl/string_span>
#include <map>
#include <memory>
#include <string_view>
#include <vector>

namespace multihash {

using string_span = gsl::span<char>;

class algorithm {
 public:
  /** Compute the hash of an input range */
  template <typename InputIterator>
  auto digest(InputIterator first, InputIterator last) -> std::string;

  /** Write the hash of an input range into output */
  template <typename InputIterator, typename OutputIterator>
  auto digest(InputIterator first, InputIterator last,
              OutputIterator output) -> OutputIterator;

  virtual ~algorithm() = default;

 private:
  virtual void reset() = 0;
  virtual std::size_t digest_size() const = 0;
  virtual std::size_t block_size() const = 0;
  virtual void update(std::string_view data) = 0;
  virtual std::size_t digest(string_span output) = 0;
  std::string digest();

};

template <typename InputIterator>
auto algorithm::digest(InputIterator first, InputIterator last) -> std::string {
  auto result = std::string{};
  result.reserve(digest_size());
  digest(first, last, std::back_inserter(result));
  return result;
}

template <typename InputIterator, typename OutputIterator>
auto algorithm::digest(InputIterator first, InputIterator last,
                                     OutputIterator output) -> OutputIterator {
  reset();
  auto buffer = std::vector<char>{};
  auto chunk_size = block_size();
  buffer.reserve(chunk_size);
  while (first != last) {
    for (auto i = 0u; first != last && i < chunk_size; ++i, ++first) {
      buffer.emplace_back(*first);
    }
    auto view = std::string_view(buffer.data(), buffer.size());
    update(view);
    buffer.clear();
  }
  auto result = digest();
  std::copy(result.begin(), result.end(), output);
  return output;
}

}  // namespace multihash


#include <config.h>
#include <math.h>
#include <gsl/gsl_statistics.h>

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "wskew_source.c"
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

#define BASE_DOUBLE
#include "templates_on.h"
#include "wskew_source.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "wskew_source.c"
#include "templates_off.h"
#undef  BASE_FLOAT


#include <bindings.cmacros.h>
#include <gsl/gsl_multiset.h>

BC_INLINE2(gsl_multiset_get,gsl_multiset*,size_t,size_t)


#pragma once

#include <halley/maths/vector2.h>
#include <halley/maths/rect.h>
#include <memory>
#include <halley/resources/resource.h>
#include <halley/text/halleystring.h>
#include <halley/data_structures/hash_map.h>
#include <gsl/span>
#include "halley/maths/vector4.h"

namespace Halley
{
	class Resources;
	class Serializer;
	class Deserializer;
	class ResourceDataStatic;
	class Texture;
	class ResourceLoader;

	class SpriteSheetEntry
	{
	public:
		Vector2f pivot;
		Vector2i origPivot;
		Vector2f size;
		Rect4f coords;
		Vector4s trimBorder;
		Vector4s slices;
		int duration = 0;
		bool rotated = false;
		bool sliced = false;

		void serialize(Serializer& s) const;
		void deserialize(Deserializer& s);
	};

	class SpriteSheetFrameTag
	{
	public:
		String name;
		int from = 0;
		int to = 0;

		void serialize(Serializer& s) const;
		void deserialize(Deserializer& s);
	};

	class SpriteSheet : public Resource
	{
	public:
		const std::shared_ptr<const Texture>& getTexture() const;
		const SpriteSheetEntry& getSprite(const String& name) const;
		const SpriteSheetEntry& getSprite(size_t idx) const;

		const std::vector<SpriteSheetFrameTag>& getFrameTags() const;
		std::vector<String> getSpriteNames() const;

		size_t getSpriteCount() const;
		size_t getIndex(const String& name) const;
		bool hasSprite(const String& name) const;

		void loadJson(gsl::span<const gsl::byte> data);

		void addSprite(String name, const SpriteSheetEntry& sprite);
		void setTextureName(String name);

		static std::unique_ptr<SpriteSheet> loadResource(ResourceLoader& loader);
		constexpr static AssetType getAssetType() { return AssetType::SpriteSheet; }
		void reload(Resource&& resource) override;

		void serialize(Serializer& s) const;
		void deserialize(Deserializer& s);

	private:
		Resources* resources = nullptr;

		mutable std::shared_ptr<const Texture> texture;
		std::vector<SpriteSheetEntry> sprites;
		HashMap<String, uint32_t> spriteIdx;
		std::vector<SpriteSheetFrameTag> frameTags;
		String textureName;

		void loadTexture(Resources& resources) const;
	};

	class SpriteResource : public Resource
	{
	public:
		SpriteResource(std::shared_ptr<const SpriteSheet> spriteSheet, size_t idx);

		const SpriteSheetEntry& getSprite() const;
		size_t getIdx() const;
		std::shared_ptr<const SpriteSheet> getSpriteSheet() const;

		constexpr static AssetType getAssetType() { return AssetType::Sprite; }
		static std::unique_ptr<SpriteResource> loadResource(ResourceLoader& loader);
		void reload(Resource&& resource) override;

	private:
		std::weak_ptr<const SpriteSheet> spriteSheet;
		size_t idx = -1;
	};
}


/*
 * Copyright <2012> <Vincent Le Guilloux,Peter Schmidtke, Pierre Tuffery>
 * Copyright <2013-2018> <Peter Schmidtke, Vincent Le Guilloux>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 */

#ifndef DH_UTILS
#define DH_UTILS

/* ------------------------------ INCLUDES ---------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <time.h>

#ifndef _WIN32
#include <unistd.h>
#endif

#ifdef MD_USE_GSL 	/* GSL */

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#endif 				/* /GSL */

#include "memhandler.h"

/* ------------------------------- PUBLIC MACROS ---------------------------- */

#define M_MAX_PDB_NAME_LEN 200  /**< maximum pdb filename length*/

#define M_SIGN 1
#define M_NO_SIGN 0

#ifdef MD_USE_GSL	/* GSL */

#define M_GEN_MTWISTER gsl_rng_mt19937
#define M_GEN_GFSR4 gsl_rng_gfsr4
#define M_GEN_TAUS gsl_rng_taus2
#define M_GEN_RANLXS0 gsl_rng_ranlxs0
#define M_GEN_RANLXS1 gsl_rng_ranlxs1

#endif				/* /GSL */

/* ------------------------------ PUBLIC STRUCTURES ------------------------- */

typedef struct tab_str
{
	char **t_str ;
	int nb_str ;

} tab_str ;

/* ------------------------------- PROTOTYPES ------------------------------- */

int str_is_number(const char *str, const int sign) ;
int str_is_float(const char *str, const int sign) ;
void str_trim(char *str) ;
tab_str* str_split(const char *str, const int sep) ;

short file_exists(const char * filename);

tab_str* f_readl(const char *fpath, int nchar_max) ;
void free_tab_str(tab_str *tstr) ;
void print_tab_str(tab_str* strings) ;

int in_tab(int *tab, int size, int val) ;
int index_of(int *tab, int size, int val)  ;

void remove_ext(char *str) ;
void remove_path(char *str) ;
void extract_ext(char *str, char *dest) ;
void extract_path(char *str, char *dest) ;

void start_rand_generator(void) ;
float rand_uniform(float min, float max) ;

FILE* fopen_pdb_check_case(char *name, const char *mode)  ;

float float_get_min_in_2D_array(float **t,size_t n,int col);
float float_get_max_in_2D_array(float **t,size_t n,int col);

#endif


#pragma once

#include <variant>

#include <gsl/gsl-lite.hpp>

#include <clrie/instruction_factory.h>

namespace appmap { namespace cil {

struct op {
    ILOrdinalOpcode op;
};

struct token_op {
    ILOrdinalOpcode op;
    mdToken token;
};

struct long_op {
    ILOrdinalOpcode op;
    uint64_t value;
};

template <ILOrdinalOpcode Op>
struct token_opcode: token_op {
    token_opcode(mdToken token): token_op{Op, token} {}
};

namespace ops {
    struct ldarg { int index; };
    struct ldloc { int index; };
    struct stloc { int index; };

    using ldfld = token_opcode<Cee_Ldfld>;
    using stfld = token_opcode<Cee_Stfld>;
    using ldftn = token_opcode<Cee_Ldftn>;
    using call = token_opcode<Cee_Call>;
    using calli = token_opcode<Cee_Calli>;
    using callvirt = token_opcode<Cee_Callvirt>;
    using newobj = token_opcode<Cee_Newobj>;

    constexpr inline auto ldnull = op{Cee_Ldnull};
    constexpr inline auto pop = op{Cee_Pop};
    constexpr inline auto dup = op{Cee_Dup};

    struct ldc: long_op {
        ldc(uint64_t val): long_op{Cee_Ldc_I8, val} {}

        template <typename Ret, typename... Args>
        ldc(Ret(*fn)(Args...)): ldc(reinterpret_cast<uint64_t>(fn)) {}
    };
}

using instruction = std::variant<ops::ldarg, ops::ldloc, ops::stloc, op, token_op, long_op>;

clrie::instruction_factory::instruction_sequence compile(const gsl::span<const instruction> code, const clrie::instruction_factory &factory);
clrie::instruction_factory::instruction_sequence compile(std::initializer_list<const instruction> code, const clrie::instruction_factory &factory);

}}


#ifndef __GSL_VECTOR_H__
#define __GSL_VECTOR_H__

#include <gsl/vector/gsl_vector_double.h>

#endif /* __GSL_VECTOR_H__ */


//
//  Revision.hpp
//  PretendToWork
//
//  Created by tqtifnypmb on 14/12/2017.
//  Copyright © 2017 tqtifnypmb. All rights reserved.
//

#pragma once

#include "../rope/Range.h"
#include "../types.h"

#include <gsl/gsl>
#include <vector>
#include <string>

namespace brick
{

class Rope;
class Revision {
public:
    enum class Operation {
        insert,
        erase,
    };

    Revision(size_t authorId, size_t revId, Operation op, const Range& range);
    Revision(size_t authorId, size_t revId, Operation op, const Range& range, const detail::CodePointList& text);
    Revision(const Revision&) = default;
    Revision() = default;

    void apply(gsl::not_null<Rope*> rope) const;
    bool canApply(gsl::not_null<const Rope*> rope) const;

    size_t authorId() const {
        return authorId_;
    }

    bool prior(const Revision& rev) const {
        return authorId() <= rev.authorId();
    }

    size_t revId() const {
        return revId_;
    }

    const Range& range() const {
        return range_;
    }

    Range& range() {
        return range_;
    }

    int affectLength() const {
        if (op() == Operation::insert) {
            return static_cast<int>(cplist().size());
        } else {
            return range().length;
        }
    }

    Operation op() const {
        return op_;
    }

    bool valid() const {
        return !range_.empty() && range_.location >= 0;
    }

    void setInvalid() {
        range_.length = 0;
    }

    const detail::CodePointList& cplist() const {
        return cplist_;
    }

private:

    size_t authorId_;

    // revId_ only identify individual revision
    // newer revision doesn't have to have greater revId_
    size_t revId_;
    detail::CodePointList cplist_;
    Operation op_;
    Range range_;
};

}   // namespace brick


#pragma once
#include <utility>
#include <algorithm>
#include <gsl/span>
#include <array>
#include <vector>
#include <memory>
#include <chrono>

struct patternbyte
{
    uint8_t value;
    uint8_t mask;

    patternbyte()
    {
        this->clear();
    }

    patternbyte(const uint8_t value, const uint8_t mask)
    {
        this->value = value;
        this->mask = mask;
    }

    bool opaque() const
    {
        return this->mask == 0xffui8;
    }

    void clear()
    {
        this->value = 0ui8;
        this->mask = 0xffui8;
    }

    friend inline bool operator==(
        const patternbyte& left,
        const uint8_t& right)
    {
        return (right & left.mask) == (left.value & left.mask); // value should already be masked
    }

    friend inline bool operator==(
        const uint8_t& left,
        const patternbyte& right)
    {
        return (left & right.mask) == (right.value & right.mask);
    }
};

inline int hexchtoint(char ch)
{
    if (ch >= '0' && ch <= '9')
        return ch - '0';
    else if (ch >= 'A' && ch <= 'F')
        return ch - 'A' + 10;
    else if (ch >= 'a' && ch <= 'f')
        return ch - 'a' + 10;
    return -1;
}

static auto compile_pattern(
    const char* pattern)
{
    auto compiled_pattern = std::make_unique<std::vector< patternbyte>>();
    patternbyte b;

    char c;
    auto shift = 0x4UL;
    while (c = *pattern++) {
        if (c == '?' || c == '.') {
            b.mask &= ~(0xf << shift);
        }
        else if (auto num = hexchtoint(c); num != -1) {
            b.value |= (num & 0xf) << shift;
        }
        else {
            continue;
        }

        if (!shift) {
            compiled_pattern->push_back(b);
            b.clear();
        }
        shift ^= 0x4UL;
    }
    if (!shift) {
        b.mask &= 0xf;
        compiled_pattern->push_back(b);
    }
    return compiled_pattern;
}

class pattern_searcher
{
    std::unique_ptr<std::vector< patternbyte>> _cp;

public:
    pattern_searcher(const char* pattern)
    {
        _cp = compile_pattern(pattern);
    }

    template <class Iterator>
    std::pair<Iterator, Iterator> operator()(Iterator first, Iterator last) const
    {
        if (first == last)
            return std::make_pair(last, last);
        if (_cp->empty())
            return std::make_pair(first, first);

        auto it = std::search(first, last, std::begin(*_cp), std::end(*_cp));

        if (it == last)
            return std::make_pair(last, last);
        return std::make_pair(it, it + std::size(*_cp));
    }
};


#ifndef _HistWidget_H
#define _HistWidget_H

#include <QWidget>
#include <gsl/gsl_histogram.h>

#include "src_config.h"

class QwtPlot;

class _HIST_EXPORT_ HistWidget : public QWidget
{
public:
    HistWidget (double xmin, double xmax, double ymin, double ymax, QWidget * parent=0, Qt::WindowFlags flags=0);
    virtual ~HistWidget (void);

    void setHistogram (const gsl_histogram * hist);

private:
    //
    // Variables
    //
    QwtPlot *plot;
    gsl_histogram * w_hist;
private:
    Q_OBJECT

};

#endif


#include <netlib-jni.h>
#include <cblas.h>

// these CBLAS get* helpers are really irritating because
// the first thing the cblas_ methods do is to do a reverse
// lookup for the char and then pass it to the fortran lib!

CBLAS_TRANSPOSE getCblasTrans(const char * fortranChar) {
	switch (fortranChar[0]) {
	    case 'N': return CblasNoTrans;
	    case 'n': return CblasNoTrans;
		case 'T': return CblasTrans;
		case 't': return CblasTrans;
		default: return -1;
	}
}

CBLAS_UPLO getCblasUpLo(const char * fortranChar) {
	switch (fortranChar[0]) {
	    case 'U': return CblasUpper;
	    case 'u': return CblasUpper;
		case 'L': return CblasLower;
		case 'l': return CblasLower;
		default: return -1;
	}
}

CBLAS_SIDE getCblasSide(const char * fortranChar) {
	switch (fortranChar[0]) {
	    case 'L': return CblasLeft;
	    case 'l': return CblasLeft;
		case 'R': return CblasRight;
		case 'r': return CblasRight;
		default: return -1;
	}
}

CBLAS_DIAG getCblasDiag(const char * fortranChar) {
	switch (fortranChar[0]) {
	    case 'N': return CblasNonUnit;
	    case 'n': return CblasNonUnit;
		case 'U': return CblasUnit;
		case 'u': return CblasUnit;
		default: return -1;
	}
}

inline void check_memory(JNIEnv * env, void * arg) {
	if (arg != NULL) {
		return;
	}
	/*
	 * WARNING: Memory leak
	 *
	 * This doesn't clean up successful allocations prior to throwing this exception.
	 * However, it's a pretty dire situation to be anyway and the client code is not
	 * expected to recover.
	 */
	(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/OutOfMemoryError"),
		"Out of memory transferring array to native code in F2J JNI");
}

inline int jboolean2int(jboolean b) {
    switch (b) {
        case JNI_TRUE: return 1;
        default: return 0;
    }
}

inline jboolean int2jboolean(int i) {
    switch (i) {
        case 1: return JNI_TRUE;
        default: return JNI_FALSE;
    }
}

int* jbooleanArray2intArray(JNIEnv * env, jboolean * a, jint size) {
	int * j = (int*) malloc(size * sizeof(int));
	check_memory(env, j);

	int i;
	for (i = 0 ; i < size ; i++) {
	    j[i] = jboolean2int(a[i]);
	}
	return j;
}

void intArray2jbooleanArray(int * a, jboolean * b, jint size) {
	int i;
	for (i = 0 ; i < size ; i++) {
	    b[i] = int2jboolean(a[i]);
	}
}


/*==================BEGIN ASF DOCUMENTATION==================*/
/*
ABOUT EDITING THIS DOCUMENTATION:
If you wish to edit the documentation for this program, you need to change the
following defines.
*/

#define ASF_NAME_STRING \
"asf_export"

#define ASF_USAGE_STRING \
"   "ASF_NAME_STRING" [-format <output_format>] [-byte <sample mapping option>]\n"\
"              [-rgb <red> <green> <blue>] [-band <band_id | all>]\n"\
"              [-lut <look up table file>] [-truecolor] [-falsecolor]\n"\
"              [-log <log_file>] [-quiet] [-license] [-version] [-help]\n"\
"              <in_base_name> <out_full_name>\n"

#define ASF_DESCRIPTION_STRING \
"   This program ingests ASF internal format data and exports said data to a\n"\
"   number of graphics file formats (TIFF/GEOTIFF, JPEG, PGM, PNG, POlSARPRO,\n"\
"   HDF5 and netCDF).\n"\
"   If the input data was geocoded and the ouput format supports geocoding,\n"\
"   that information will be included.  Optionally, you may apply look-up tables,\n"\
"   assign color bands (-rgb, -truecolor, -falsecolor).\n"

#define ASF_INPUT_STRING \
"   A file set in the ASF internal data format.\n"

#define ASF_OUTPUT_STRING \
"   The converted data in the output file, with the requested format.\n"

#define ASF_OPTIONS_STRING \
"   -format <format>\n"\
"        Format to export to. Must be one of the following:\n"\
"            tiff      - Tagged Image File Format, with byte valued pixels\n"\
"            geotiff   - GeoTIFF file, with floating point or byte valued pixels\n"\
"            jpeg      - Lossy compressed image, with byte valued pixels\n"\
"            pgm       - Portable graymap image, with byte valued pixels\n"\
"            png       - Portable network graphic, with byte valued pixels\n"\
"            polsarpro - Flat binary floating point files in PolSARPro format\n"\
"            hdf5      - HDF5 format, with floating point or byte valued pixels\n"\
"            netcdf    - netCDF format compliant to the CF conventions\n"\
"   NOTE: When exporting to a GeoTIFF format file, all map-projection\n"\
"         information is included in GeoKeys as specified in the GeoTIFF\n"\
"         standard.  The other graphics file formats do not support the\n"\
"         storing of map-projection parameters in the output file.  If you\n"\
"         wish to maintain the map-projection and/or georeference (corner\n"\
"         point) information in the output, you should choose the GeoTIFF\n"\
"         output format.\n\n"\
"   NOTE: When exporting to a GeoTIFF format file, the data format (floating\n"\
"         point, byte, 16-bit integer, etc) will be maintained.  Many viewers\n"\
"         cannot view non-integer data.  Leaving the data format the same as\n"\
"         the original produces the most accurate export, but remapping it to\n"\
"         byte range (0-255) with the -byte option will result in the greatest\n"\
"         compatibility with viewers and GIS software packages.\n\n"\
"   -byte <sample mapping option>\n"\
"        Converts output image to byte using the following options:\n"\
"            truncate\n"\
"                values less than 0 are mapped to 0, values greater than 255\n"\
"                are mapped to 255, and values in between are converted to\n"\
"                whole numbers (the fractional part of the values are truncated,\n"\
"                not rounded.)\n"\
"            minmax\n"\
"                determines the minimum and maximum values of the input image\n"\
"                and linearly maps those values to the byte range of 0 to 255.\n"\
"                The remapping is accomplished using real (floating point)\n"\
"                numbers then the result is converted to a whole number by\n"\
"                truncating the fractional part.\n"\
"            sigma\n"\
"                determines the mean and standard deviation of an image and\n"\
"                defines a range of +/- two standard deviations (2-sigma)\n"\
"                around the mean value, and maps this buffer to the byte range\n"\
"                0 to 255 as described for minmax above.  The range limits are\n"\
"                adjusted if the either of the 2-sigma range limits lie outside\n"\
"                the range of the original values.  As with the other remapping\n"\
"                methods, the calculation of values are made with real numbers\n"\
"                and the result is converted to a whole number by truncating\n"\
"                any fractional part.\n"\
"            histogram_equalize\n"\
"                develops a look-up table by integrating the (no-adaptation)\n"\
"                whole-image histogram and then normalizing the result to the\n"\
"                0-255 range.  The result is that areas of low contrast, i.e.\n"\
"                flat topography, have a stronger contrast expansion applied\n"\
"                while areas of high contrast, i.e. non-flat topography, will\n"\
"                receive less contrast expansion.  Histogram equalization\n"\
"                is a useful transform for making hard-to-see detail more\n"\
"                visible for the the viewer but is likewise a nonlinear\n"\
"                transform that results in minor (apparent) topography shifts\n"\
"                within the image.  Since shifts occur the most in areas where\n"\
"                the contrast is expanded the most, i.e. flat topography, and\n"\
"                less in areas of more interesting topography, the pragmatic\n"\
"                conclusion is that the nonlinear shifts are quite\n"\
"                insignificant for the majority of users ...only important\n"\
"                if performing precision geography measurements or overlays.\n"\
"   -rgb <red> <green> <blue>\n"\
"        Converts output image into a color RGB image.\n"\
"        <red>, <green>, and <blue> specify which band (channel)\n"\
"        is assigned to color planes red, green, or blue,\n"\
"        ex) '-rgb HH VH VV', or '-rgb 3 2 1'.  If the word 'ignore' is\n"\
"        provided as one or more bands, then the associated color plane\n"\
"        will not be exported, e.g. '-rgb ignore 2 1' will result in an\n"\
"        RGB file that has a zero in each RGB pixel's red component, band 2\n"\
"        assigned to the green channel, and band 1 assigned to the blue.\n"\
"        The result will be an image with only greens and blues in it.\n"\
"        Currently implemented for GeoTIFF, TIFF, JPEG and PNG.\n"\
"        Cannot be used together with the -band option.\n"\
"   -lut <look up table file>\n"\
"        Applies a color look up table to the image while exporting.\n"\
"        Only allowed for single-band images.  Images must contain byte (8-bit)\n"\
"        data (except for data deriving from PolSARpro classifications.) Some\n"\
"        look-up table files are in the look_up_tables subdirectory in\n"\
"        the asf_tools share directory.  The tool will look in\n"\
"        this directory for the specified file if it isn't found\n"\
"        in the current directory.\n"\
"   -truecolor\n"\
"        For 3 or 4 band optical satellite images where the first band is the\n"\
"        the blue band, the second green, and the third red.  This option will\n"\
"        export the third band as the red element, the second band as the green\n"\
"        element, and the first band as the blue element.  Performs a 2-sigma\n"\
"        constrast expansion on each individual band during the export (similar\n"\
"        to most GIS software packages.)  To export a true-color image WITHOUT\n"\
"        the contrast expansion associated with the truecolor option, use the\n"\
"        -rgb option instead.  The rgb option will directly assign the\n"\
"        available bands, unaltered, to the RGB color channels in the output\n"\
"        file, e.g.\n\n"\
"          'asf_export -rgb 03 02 01 <infile> <outfile>'.\n\n"\
"        Only allowed for multi-band images with 3 or more bands.\n"\
"        The truecolor option cannot be used together with any of the\n"\
"        following options: -rgb, -band, or -falsecolor.\n"\
"   -falsecolor\n"\
"        For 4 band optical satellite images where the second band is green,\n"\
"        the third red, and the fourth is the near-infrared band.  Exports\n"\
"        the fourth (IR) band as the red element, the third band as the green\n"\
"        element, and the second band as the blue element.  Performs a 2-sigma\n"\
"        constrast expansion on the individual bands during the export.  To\n"\
"        export a falsecolor image WITHOUT the contrast expansion, use the\n"\
"        -rgb flag to directly assign the available bands to the RGB color\n"\
"        channels, e.g.\n\n"\
"          'asf_export -rgb 04 03 02 <infile> <outfile>'.\n\n"\
"        Only allowed for multi-band images with 4 bands.\n"\
"        Cannot be used together with any of the following: -rgb, -band,\n"\
"        or -truecolor.\n"\
"   -band <band_id | all>\n"\
"        If the data contains multiple data files, one for each band (channel)\n"\
"        then export the band identified by 'band_id' (only).  If 'all' is\n"\
"        specified rather than a band_id, then export all available bands into\n"\
"        individual files, one for each band.  Default is '-band all'.\n"\
"        Cannot be chosen together with the -rgb option.\n"\
"   -log <logFile>\n"\
"        Output will be written to a specified log file.\n"\
"   -quiet\n"\
"        Supresses all non-essential output.\n"\
"   -license\n"\
"        Print copyright and license for this software then exit.\n"\
"   -version\n"\
"        Print version and copyright then exit.\n"\
"   -help\n"\
"        Print a help page and exit.\n"

#define ASF_EXAMPLES_STRING \
"   To export to the default GeoTIFF format from file1.img and file1.meta\n"\
"   to file1.tif:\n"\
"        example> "ASF_NAME_STRING" file1 file1\n\n"\
"   NOTE: When exporting to a GeoTIFF format file, all map-projection\n"\
"         information is included in GeoKeys as specified in the GeoTIFF\n"\
"         standard.\n\n"\
"   NOTE: When exporting to a GeoTIFF format file, the data format (floating\n"\
"         point, byte, 16-bit integer, etc) will be maintained.  Many viewers\n"\
"         cannot view non-integer data.  Leaving the data format the same as\n"\
"         the original produces the most accurate export, but remapping it to\n"\
"         byte range (0-255) with the -byte option will result in the greatest\n"\
"         compatibility with viewers and GIS software packages.\n"\
"\n"\
"   To export to file2.jpg in the jpeg format:\n"\
"        example> "ASF_NAME_STRING" -format jpeg file1 file2\n"\
"\n"

#define ASF_LIMITATIONS_STRING \
"   Currently supports ingest of ASF format floating point and byte data (only).\n"\
"\n"\
"   Floating-point image formats (i.e., geotiff) are not supported in many\n"\
"   image viewing programs.\n"

#define ASF_SEE_ALSO_STRING \
"   asf_mapready, asf_import\n"

/*===================END ASF DOCUMENTATION===================*/

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>

#include <cla.h>
#include <envi.h>
#include <esri.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_statistics.h>

#include <asf.h>
#include <asf_endian.h>
#include <asf_meta.h>
#include <asf_raster.h>
#include <asf_export.h>
#include <asf_contact.h>
#include <asf_license.h>

// Local prototypes
int is_numeric(char *str);

// Print minimalistic usage info & exit
static void print_usage(void)
{
  asfPrintStatus("\n"
      "Usage:\n"
      ASF_USAGE_STRING
      "\n");
  exit(EXIT_FAILURE);
}

// Print the help info & exit
static void print_help(void)
{
  asfPrintStatus(
      "\n"
      "Tool name:\n   " ASF_NAME_STRING "\n\n"
      "Usage:\n" ASF_USAGE_STRING "\n"
      "Description:\n" ASF_DESCRIPTION_STRING "\n"
      "Input:\n" ASF_INPUT_STRING "\n"
      "Output:\n"ASF_OUTPUT_STRING "\n"
      "Options:\n" ASF_OPTIONS_STRING "\n"
      "Examples:\n" ASF_EXAMPLES_STRING "\n"
      "Limitations:\n" ASF_LIMITATIONS_STRING "\n"
      "See also:\n" ASF_SEE_ALSO_STRING "\n"
      "Contact:\n" ASF_CONTACT_STRING "\n"
      "Version:\n   " SVN_REV " (part of " TOOL_SUITE_NAME " " MAPREADY_VERSION_STRING ")\n\n");
  exit(EXIT_FAILURE);
}

int
checkForOption (char *key, int argc, char *argv[])
{
  int ii = 0;
  while ( ii < argc ) {
    if ( strmatch (key, argv[ii]) )
      return ii;
    ++ii;
  }
  return FLAG_NOT_SET;
}

static const char *sigma_str(int with_sigma) {
    return with_sigma ? "w/sigma" : "";
}

// Main program body.
int
main (int argc, char *argv[])
{
  output_format_t format = 0;
  meta_parameters *md;
  char *in_base_name, *output_name;
  char **band_names=NULL;
  int rgb=0;
  int true_color;
  int false_color;
  int num_bands_found;
  int ignored[3] = {0, 0, 0};
  int num_ignored = 0;

  in_base_name = (char *) MALLOC(sizeof(char)*255);
  output_name = (char *) MALLOC(sizeof(char)*255);

/**********************BEGIN COMMAND LINE PARSING STUFF**********************/
  // Command line input goes in it's own structure.
  command_line_parameters_t command_line;
  strcpy (command_line.format, "");
  command_line.size = NO_MAXIMUM_OUTPUT_SIZE;
  strcpy (command_line.in_data_name, "");
  strcpy (command_line.in_meta_name, "");
  strcpy (command_line.output_name, "");
  command_line.verbose = FALSE;
  command_line.quiet = FALSE;
  strcpy (command_line.leader_name, "");
  strcpy (command_line.cal_params_file, "");
  strcpy (command_line.cal_comment, "");
  command_line.sample_mapping = 0;
  strcpy(command_line.red_channel, "");
  strcpy(command_line.green_channel, "");
  strcpy(command_line.blue_channel, "");
  strcpy(command_line.band, "");
  strcpy(command_line.look_up_table_name, "");

  int formatFlag, logFlag, quietFlag, byteFlag, rgbFlag, bandFlag, lutFlag;
  int truecolorFlag, falsecolorFlag;
  int needed_args = 3;  //command & argument & argument
  int ii;
  char sample_mapping_string[25];

  //Check to see which options were specified
  if (   (checkForOption("--help", argc, argv) != FLAG_NOT_SET)
      || (checkForOption("-h", argc, argv) != FLAG_NOT_SET)
      || (checkForOption("-help", argc, argv) != FLAG_NOT_SET) ) {
      print_help();
  }
  get_asf_share_dir_with_argv0(argv[0]);
  handle_license_and_version_args(argc, argv, ASF_NAME_STRING);

  formatFlag = checkForOption ("-format", argc, argv);
  logFlag = checkForOption ("-log", argc, argv);
  quietFlag = checkForOption ("-quiet", argc, argv);
  byteFlag = checkForOption ("-byte", argc, argv);
  rgbFlag = checkForOption ("-rgb", argc, argv);
  bandFlag = checkForOption ("-band", argc, argv);
  lutFlag = checkForOption ("-lut", argc, argv);
  truecolorFlag = checkForOption("-truecolor", argc, argv);
  falsecolorFlag = checkForOption("-falsecolor", argc, argv);

  if ( formatFlag != FLAG_NOT_SET ) {
    needed_args += 2;           // Option & parameter.
  }
  if ( quietFlag != FLAG_NOT_SET ) {
    needed_args += 1;           // Option & parameter.
  }
  if ( logFlag != FLAG_NOT_SET ) {
    needed_args += 2;           // Option & parameter.
  }
  if ( byteFlag != FLAG_NOT_SET ) {
    needed_args += 2;           // Option & parameter.
  }
  if ( rgbFlag != FLAG_NOT_SET ) {
    needed_args += 4;           // Option & 3 parameters.
  }
  if ( bandFlag != FLAG_NOT_SET ) {
    needed_args += 2;           // Option & parameter.
  }
  if ( lutFlag != FLAG_NOT_SET ) {
    needed_args += 2;           // Option & parameter.
  }
  if ( truecolorFlag != FLAG_NOT_SET ) {
    needed_args += 1;           // Option only
  }
  if ( falsecolorFlag != FLAG_NOT_SET ) {
    needed_args += 1;           // Option only
  }

  if ( argc != needed_args ) {
    print_usage ();                   // This exits with a failure.
  }

  // We also need to make sure the last three options are close to
  // what we expect.
  if ( argv[argc - 1][0] == '-' || argv[argc - 2][0] == '-' ) {
    print_usage (); // This exits with a failure.
  }

  // Make sure any options that have parameters are followed by
  // parameters (and not other options) Also make sure options'
  // parameters don't bleed into required arguments.
  if ( formatFlag != FLAG_NOT_SET ) {
    if ( argv[formatFlag + 1][0] == '-' || formatFlag >= argc - 3 ) {
      print_usage ();
    }
  }
  if ( byteFlag != FLAG_NOT_SET ) {
    if ( argv[byteFlag + 1][0] == '-' || byteFlag >= argc - 3 ) {
      print_usage ();
    }
  }
  if ( rgbFlag != FLAG_NOT_SET ) {
    if (( argv[rgbFlag + 1][0] == '-' && argv[rgbFlag + 2][0] == '-' &&
          argv[rgbFlag + 3][0] == '-' ) || rgbFlag >= argc - 5 ) {
      print_usage ();
    }
  }
  if ( bandFlag != FLAG_NOT_SET ) {
    if ( argv[bandFlag + 1][0] == '-' || bandFlag >= argc - 3 ) {
      print_usage ();
    }
  }
  if ( lutFlag != FLAG_NOT_SET ) {
    if ( argv[lutFlag + 1][0] == '-' || lutFlag >= argc - 3 ) {
      print_usage ();
    }
  }
  if ( logFlag != FLAG_NOT_SET ) {
    if ( argv[logFlag + 1][0] == '-' || logFlag >= argc - 3 ) {
      print_usage ();
    }
  }

  // Make sure there are no flag incompatibilities
  if ( (rgbFlag != FLAG_NOT_SET           &&
        (bandFlag != FLAG_NOT_SET         ||
         truecolorFlag != FLAG_NOT_SET    ||
         falsecolorFlag != FLAG_NOT_SET))
     ||
       (bandFlag != FLAG_NOT_SET         &&
        (rgbFlag != FLAG_NOT_SET         ||
         truecolorFlag != FLAG_NOT_SET   ||
         falsecolorFlag != FLAG_NOT_SET))
     ||
       (truecolorFlag != FLAG_NOT_SET    &&
        (bandFlag != FLAG_NOT_SET        ||
         rgbFlag != FLAG_NOT_SET         ||
         falsecolorFlag != FLAG_NOT_SET))
     ||
       (falsecolorFlag != FLAG_NOT_SET   &&
        (bandFlag != FLAG_NOT_SET        ||
         truecolorFlag != FLAG_NOT_SET   ||
         rgbFlag != FLAG_NOT_SET))
     )
  {
    asfPrintWarning("The following options may only be used one at a time:\n"
        "    %s\n    %s\n    %s\n    %s\n    %s\n    %s\n",
        "-rgb", "-truecolor", "-falsecolor", "-band");
    print_help();
  }
  if ( (rgbFlag != FLAG_NOT_SET         ||
        truecolorFlag != FLAG_NOT_SET   ||
        falsecolorFlag != FLAG_NOT_SET) &&
        lutFlag != FLAG_NOT_SET
     )
    asfPrintError("Look up table option can only be used on single-band "
          "images.\n");

  if( logFlag != FLAG_NOT_SET ) {
    strcpy(logFile, argv[logFlag+1]);
  }
  else {
    sprintf(logFile, "tmp%i.log", (int)getpid());
  }
  logflag = TRUE; // Since we always log, set the old school logflag to true
  fLog = FOPEN (logFile, "a");

  // Set old school quiet flag (for use in our libraries)
  quietflag = ( quietFlag != FLAG_NOT_SET ) ? TRUE : FALSE;

  // We're good enough at this point... print the splash screen.
  asfSplashScreen (argc, argv);

  // Grab the input and output name
  strcpy (in_base_name, argv[argc - 2]);
  strcpy (output_name, argv[argc - 1]);
  strcpy (command_line.output_name, output_name);

  // If user added ".img", strip it.
  char *ext = findExt(in_base_name);
  if (ext && strcmp(ext, ".img") == 0) *ext = '\0';

  // Set default output type
  if( formatFlag != FLAG_NOT_SET ) {
    strcpy (command_line.format, argv[formatFlag + 1]);
  }
  else {
    // Default behavior: produce a geotiff.
    strcpy (command_line.format, "geotiff");
  }

  // Compose input metadata name
  strcpy (command_line.in_meta_name, in_base_name);
  strcat (command_line.in_meta_name, ".meta");

  // for some validation, need the metadata
  md = meta_read (command_line.in_meta_name);

  // Convert the string to upper case.
  for ( ii = 0 ; ii < strlen (command_line.format) ; ++ii ) {
    command_line.format[ii] = toupper (command_line.format[ii]);
  }
  if (strcmp (command_line.format, "PGM") == 0 &&
      (rgbFlag != FLAG_NOT_SET      ||
      truecolorFlag != FLAG_NOT_SET ||
      falsecolorFlag != FLAG_NOT_SET)
     )
  {
    asfPrintWarning("Greyscale PGM output is not compatible with color options:\n"
                    "(RGB, True Color, False Color, color look-up tables, etc\n)"
                    "...Defaulting to producing separate greyscale PGM files for available band.\n");
    rgbFlag = FLAG_NOT_SET;
    truecolorFlag = FLAG_NOT_SET;
    falsecolorFlag = FLAG_NOT_SET;
  }

  // Set the default byte scaling mechanisms
  if (md->optical) {
     // for optical data, default sample mapping is NONE
      command_line.sample_mapping = NONE;
  }
  // for other data, default is based on the output type
  else if (strcmp (command_line.format, "TIFF") == 0 ||
           strcmp (command_line.format, "TIF")  == 0 ||
           strcmp (command_line.format, "JPEG") == 0 ||
           strcmp (command_line.format, "JPG")  == 0 ||
           strcmp (command_line.format, "PNG")  == 0 ||
           strcmp (command_line.format, "PGM")  == 0 ||
	   strcmp (command_line.format, "PNG_ALPHA") == 0 ||
	   strcmp (command_line.format, "PNG_GE") == 0)
  {
    command_line.sample_mapping = SIGMA;
  }
  else if (strcmp (command_line.format, "GEOTIFF") == 0) {
    command_line.sample_mapping = NONE;
  }

  if ( quietFlag != FLAG_NOT_SET )
    command_line.quiet = TRUE;
  else
    command_line.quiet = FALSE;

  // Set rgb combination
  if ( rgbFlag != FLAG_NOT_SET ) {
    int i;

    for (i=0, num_ignored = 0; i<3; i++) {
      ignored[i] = strncmp("IGNORE", uc(argv[rgbFlag + i + 1]), 6) == 0 ? 1 : 0;
      num_ignored += ignored[i] ? 1 : 0;
    }
    asfRequire(num_ignored < 3,
               "Cannot ignore all bands.  Exported image would be blank.\n");

    strcpy (command_line.red_channel, ignored[0] ? "Ignored" : argv[rgbFlag + 1]);
    strcpy (command_line.green_channel, ignored[1] ? "Ignored" : argv[rgbFlag + 2]);
    strcpy (command_line.blue_channel, ignored[2] ? "Ignored" : argv[rgbFlag + 3]);

    // Check to see if the bands are numeric and in range
    int r_channel = atoi(command_line.red_channel);
    int g_channel = atoi(command_line.green_channel);
    int b_channel = atoi(command_line.blue_channel);

    /////////// Numeric channel case ////////////
    // Remove trailing non-numeric characters from the channel number
    // string and pad front end nicely with a zero
    if (!ignored[0] && is_numeric(command_line.red_channel) &&
        r_channel >= 1 && r_channel <= MAX_BANDS) {
      sprintf(command_line.red_channel, "%02d", atoi(command_line.red_channel));
    }
    if (!ignored[1] && is_numeric(command_line.green_channel) &&
        g_channel >= 1 && g_channel <= MAX_BANDS) {
      sprintf(command_line.green_channel, "%02d", atoi(command_line.green_channel));
    }
    if (!ignored[2] && is_numeric(command_line.blue_channel) &&
        b_channel >= 1 && b_channel <= MAX_BANDS) {
      sprintf(command_line.blue_channel, "%02d", atoi(command_line.blue_channel));
    }
  }

  // Set up the bands for true or false color optical data
  true_color = false_color = 0;
  int with_sigma = FALSE;
  if (truecolorFlag != FLAG_NOT_SET || falsecolorFlag != FLAG_NOT_SET) {
    int ALOS_optical = (md->optical && strncmp(md->general->sensor, "ALOS", 4) == 0) ? 1 : 0;
    if (md->optical && truecolorFlag != FLAG_NOT_SET) {
      if (ALOS_optical) {
        with_sigma = TRUE;
        strcpy(command_line.red_channel,   "03");
        strcpy(command_line.green_channel, "02");
        strcpy(command_line.blue_channel,  "01");
        true_color = 1;
        asfPrintStatus("Applying True Color contrast expansion to following channels:");
      }
      else {
        char **bands = extract_band_names(md->general->bands, 3);
        asfRequire(bands != NULL,
                   "-truecolor option specified for non-true color optical image.\n");

        asfPrintWarning("Attempting to use the -truecolor option with non-ALOS\n"
            "optical data.\n");
        strcpy(command_line.red_channel, bands[2]);
        strcpy(command_line.green_channel, bands[1]);
        strcpy(command_line.blue_channel, bands[0]);
        int i;
        for (i=0; i<3; i++) {
          FREE(bands[i]);
        }
        FREE(bands);
      }
    }
    if (md->optical && falsecolorFlag != FLAG_NOT_SET) {
      if (ALOS_optical) {
        with_sigma = TRUE;
        strcpy(command_line.red_channel,   "04");
        strcpy(command_line.green_channel, "03");
        strcpy(command_line.blue_channel,  "02");
        false_color = 1;
        asfPrintStatus("Applying False Color contrast expansion to the following channels:");
      }
      else {
        char **bands = extract_band_names(md->general->bands, 4);
        asfRequire(bands != NULL,
                   "-falsecolor option specified for an optical image with fewer than 4 bands.\n");

        asfPrintWarning("Attempting to use the -falsecolor option with non-ALOS\n"
            "optical data.\n");
        strcpy(command_line.red_channel, bands[3]);
        strcpy(command_line.green_channel, bands[2]);
        strcpy(command_line.blue_channel, bands[1]);
        int i;
        for (i=0; i<3; i++) {
          FREE(bands[i]);
        }
        FREE(bands);
      }
    }
    if (!ALOS_optical && !md->optical) {
      asfPrintError("-truecolor or -falsecolor option selected with non-optical data\n");
    }
  }

  if (rgbFlag != FLAG_NOT_SET ||
     truecolorFlag != FLAG_NOT_SET ||
     falsecolorFlag != FLAG_NOT_SET)
  {
    char red_band[16], green_band[16], blue_band[16];

    asfPrintStatus("\nRed channel  : %s %s\n", command_line.red_channel, sigma_str(with_sigma));
    asfPrintStatus("Green channel: %s %s\n", command_line.green_channel, sigma_str(with_sigma));
    asfPrintStatus("Blue channel : %s %s\n\n", command_line.blue_channel, sigma_str(with_sigma));

    if (is_numeric(command_line.red_channel) &&
        is_numeric(command_line.green_channel) &&
        is_numeric(command_line.blue_channel))
    {
      sprintf(red_band, "%02d", atoi(command_line.red_channel));
      sprintf(green_band, "%02d", atoi(command_line.green_channel));
      sprintf(blue_band, "%02d", atoi(command_line.blue_channel));
      band_names = find_bands(in_base_name, rgbFlag,
                              red_band,
                              green_band,
                              blue_band,
                              &num_bands_found);
    }
    else {
      band_names = find_bands(in_base_name, rgbFlag,
                              command_line.red_channel,
                              command_line.green_channel,
                              command_line.blue_channel,
                              &num_bands_found);
    }
  }

  // Set band
  if ( bandFlag != FLAG_NOT_SET) {
    strcpy (command_line.band, argv[bandFlag + 1]);
    band_names = find_single_band(in_base_name, command_line.band,
                  &num_bands_found);
  }
  else if (rgbFlag == FLAG_NOT_SET &&
          truecolorFlag == FLAG_NOT_SET &&
          falsecolorFlag == FLAG_NOT_SET &&
          bandFlag == FLAG_NOT_SET) {
    bandFlag=1; // For proper messaging to the user
    strcpy (command_line.band, "all");
    band_names = find_single_band(in_base_name, command_line.band,
                                  &num_bands_found);
  }

  // Read look up table name
  if ( lutFlag != FLAG_NOT_SET) {
    strcpy(command_line.look_up_table_name, argv[lutFlag + 1]);
    rgb = 1;
  }

  // Set scaling mechanism
  if ( byteFlag != FLAG_NOT_SET ) {
    strcpy (sample_mapping_string, argv[byteFlag + 1]);
    for ( ii = 0; ii < strlen(sample_mapping_string); ii++) {
      sample_mapping_string[ii] = toupper (sample_mapping_string[ii]);
    }
    if ( strcmp (sample_mapping_string, "TRUNCATE") == 0 )
      command_line.sample_mapping = TRUNCATE;
    else if ( strcmp(sample_mapping_string, "MINMAX") == 0 )
      command_line.sample_mapping = MINMAX;
    else if ( strcmp(sample_mapping_string, "SIGMA") == 0 )
      command_line.sample_mapping = SIGMA;
    else if ( strcmp(sample_mapping_string, "HISTOGRAM_EQUALIZE") == 0 )
      command_line.sample_mapping = HISTOGRAM_EQUALIZE;
    else if ( strcmp(sample_mapping_string, "NONE") == 0 ) {
        asfPrintWarning("Sample remapping method (-byte option) is set to NONE\n"
                "which doesn't make sense.  Defaulting to TRUNCATE...\n");
        command_line.sample_mapping = TRUNCATE;
    }
    else
      asfPrintError("Unrecognized byte scaling method '%s'.\n",
                    sample_mapping_string);
  }

  int is_polsarpro = (md->general->bands && strstr(md->general->bands, "POLSARPRO") != NULL) ? 1 : 0;
  if ( !is_polsarpro               &&
       lutFlag != FLAG_NOT_SET     &&
       bandFlag == FLAG_NOT_SET    &&
       md->general->band_count > 1)
  {
    asfPrintError("Look up tables can only be applied to single band"
          " images\n");
  }

  if ( !is_polsarpro                       &&
       lutFlag != FLAG_NOT_SET             &&
       command_line.sample_mapping == NONE &&
       md->general->data_type != BYTE      &&
       md->general->band_count == 1)
  {
    asfPrintError("Look up tables can only be applied to byte output"
          " images\n");
  }

  // Report what is going to happen
  if (rgbFlag != FLAG_NOT_SET ||
     truecolorFlag != FLAG_NOT_SET ||
     falsecolorFlag != FLAG_NOT_SET)
  {
    if (num_bands_found >= 3) {
      asfPrintStatus("Exporting multiband image ...\n\n");
      rgb = 1;
    }
    else {
      asfPrintError("Not all RGB channels found.\n");
    }
  }
  else if (bandFlag != FLAG_NOT_SET) {
    if (strcmp_case(command_line.band, "ALL") == 0) {
      if (multiband(command_line.format,
		    extract_band_names(md->general->bands, md->general->band_count),
		    md->general->band_count))
	asfPrintStatus("Exporting multiband image ...\n\n");
      else  if (num_bands_found > 1)
        asfPrintStatus("Exporting each band into individual greyscale files ...\n\n");
    }
    else if (num_bands_found == 1) {
      if (lutFlag != FLAG_NOT_SET)
    asfPrintStatus("Exporting band '%s' applying look up table ...\n\n",
               command_line.band);
      else
    asfPrintStatus("Exporting band '%s' as greyscale ...\n\n",
               command_line.band);
    }
    else
      asfPrintError("Band could not be found in the image.\n");
  }
  else if (lutFlag != FLAG_NOT_SET)
    asfPrintStatus("Exporting applying look up table.\n\n");
  else
    asfPrintStatus("Exporting as greyscale.\n\n");

  //If user added ".img", strip it.
  ext = findExt(in_base_name);
  if (ext && strcmp(ext, ".img") == 0) *ext = '\0';
  meta_free(md);

/***********************END COMMAND LINE PARSING STUFF***********************/

  if ( strcmp (command_line.format, "ENVI") == 0 ) {
    format = ENVI;
  }
  else if ( strcmp (command_line.format, "ESRI") == 0 ) {
    format = ESRI;
  }
  else if ( strcmp (command_line.format, "GEOTIFF") == 0 ||
            strcmp (command_line.format, "GEOTIF") == 0) {
    format = GEOTIFF;
  }
  else if ( strcmp (command_line.format, "TIFF") == 0 ||
            strcmp (command_line.format, "TIF") == 0) {
    format = TIF;
  }
  else if ( strcmp (command_line.format, "JPEG") == 0 ||
            strcmp (command_line.format, "JPG") == 0) {
    format = JPEG;
  }
  else if ( strcmp (command_line.format, "PGM") == 0 ) {
    format = PGM;
  }
  else if ( strcmp (command_line.format, "PNG") == 0 ) {
    format = PNG;
  }
  else if ( strcmp (command_line.format, "PNG_ALPHA") == 0 ) {
    format = PNG_ALPHA;
  }
  else if ( strcmp (command_line.format, "PNG_GE") == 0 ) {
    format = PNG_GE;
  }
  else if ( strcmp (command_line.format, "KML") == 0 ) {
    format = KML;
  }
  else if ( strcmp (command_line.format, "POLSARPRO") == 0 ) {
    format = POLSARPRO_HDR;
  }
  else if ( strcmp (command_line.format, "HDF5") == 0 ) {
    format = HDF;
  }
  else if ( strcmp (command_line.format, "NETCDF") == 0 ) {
    format = NC;
  }
  else {
    asfPrintError("Unrecognized output format specified\n");
  }

  /* Complex data generally can't be output into meaningful images, so
     we refuse to deal with it.  */
  /*
  md = meta_read (command_line.in_meta_name);
  asfRequire (   md->general->data_type == BYTE
              || md->general->data_type == INTEGER16
              || md->general->data_type == INTEGER32
              || md->general->data_type == REAL32
              || md->general->data_type == REAL64,
              "Cannot cope with complex data, exiting...\n");

  meta_free (md);
  */

  // Do that exporting magic!
  asf_export_bands(format, command_line.sample_mapping, rgb,
                   true_color, false_color,
                   command_line.look_up_table_name,
                   in_base_name, command_line.output_name, band_names,
                   NULL, NULL);

  // If the user didn't ask for a log file then nuke the one that's been kept
  // since everything has finished successfully
  if (logFlag == FLAG_NOT_SET) {
      fclose (fLog);
      remove(logFile);
  }

  for (ii = 0; ii<num_bands_found; ii++) {
    FREE(band_names[ii]);
  }
  FREE(band_names);
  FREE(in_base_name);
  FREE(output_name);
  exit (EXIT_SUCCESS);
}

int is_numeric(char *str)
{
  char *s = str;
  int numericness = 1;

  while (*s != '\0') {
    if (!isdigit(*s)) numericness = 0;
    s++;
  }

  return numericness;
}