#ifdef WIN
#pragma warning (disable: 4786) // disable warning for STL maps
#endif /* WIN */

#include "dpuser.h"
#include "functions.h"
#include "dpuser_utils.h"
#include "dpuserType.h"
#include <fits.h>
#include <dpstring.h>
#include <sstream>

#ifdef DPQT
#include "qtdpuser.h"
#endif /* DPQT */

#ifdef HAS_PGPLOT
#include <cpgplot.h>
#ifdef HAS_DPPGPLOT
#include <cpg3d.h>
#endif /* HAS_DPPGPLOT */
#endif /* HAS_PGPLOT */

#include "procedures.h"

char outstring[1024];

#ifdef HAS_PGPLOT
#ifdef HAS_DPPGPLOT
void surface(Fits *input, float angle, long skip) {
	Fits *data = CreateFits();
	int x, y, scale;

	if (skip > 1) {
		scale = skip;
		if (scale < 1) {
			dp_output("surface: Silently ignoring skip value of %i.\n", scale);
			if (!data->copy(*input)) return;
			if (!data->setType(R4)) return;
		} else {
			if (!data->create(input->Naxis(1) / scale, input->Naxis(2) / scale)) return;
			for (x = 1; x <= data->Naxis(1); x++) {
				for (y = 1; y <= data->Naxis(2); y++) {
					data->r4data[data->F_I(x, y)] = input->ValueAt(input->F_I(x*scale, y*scale));
				}
			}
		}
	} else {
		if (!data->copy(*input)) return;
		if (!data->setType(R4)) return;
	}
    if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) != 1) return;
	cpgenv(0, 1, 0, 1, 1, -2);
	cfreddy(data->r4data, data->Naxis(1), data->Naxis(2), 1.0, angle);
	cpgend();
}
#endif /* HAS_DPPGPLOT */
#endif /* HAS_PGPLOT */

void print(dpuserType &v, int options) {
	switch(v.type) {
		case typeCon: dp_output("%li\n", v.lvalue); return;
		case typeDbl: dp_output("%6g\n", v.dvalue); return;
                case typeStr: dp_output_string("%s\n", v.svalue->c_str()); return;
        case typeCom: dp_output("%s\n", FormatComplexdpString(*v.cvalue).c_str()); return;
		case typeStrarr: {
			dpint64 i;

            for (i = 0; i < v.arrvalue->count(); i++) {
                                dp_output_string("%s\n", (*v.arrvalue)[i].c_str());
			}
		}
			return;
		case typeFitsFile:
			v.type = typeFits;
			v.fvalue = CreateFits();
            if (!v.fvalue->ReadFITS(v.ffvalue->c_str())) break;
		case typeFits: {
			double min, max, flux, avg, fx, fy, fz;
			int x, y, z;
			Fits *_tmp = v.fvalue;

			if (_tmp == NULL) return;
			if (options == 0) {
			dp_output("*******************************************************\n");
			dp_output("             Statistics on full array:\n");
			dp_output("number of axes    : %li\n", _tmp->Naxis(0));
			if (_tmp->Naxis(0) == 3) {
				dp_output("array size        : %li x %li x %li\n", _tmp->Naxis(1), _tmp->Naxis(2), _tmp->Naxis(3));
			} else if (_tmp->Naxis(0) == 2) {
                dp_output("array size        : %lli x %lli\n", _tmp->Naxis(1), _tmp->Naxis(2));
			} else {
				dp_output("array size        : %li\n", _tmp->Naxis(1));
			}
			dp_output("bitpix            : %i\n", (int)_tmp->membits);
                        if ((int)_tmp->membits != -128) {
			dp_stat(*_tmp, &min, &max, &flux, &avg);
//			_tmp->makeStat(&min, &max, &flux, &avg);
			_tmp->min_pos(&x, &y, &z);
			if (_tmp->Naxis(0) == 3) {
				dp_output("minimum value     :%16g     at:%4i%5i%5i\n", min, x, y, z);
			} else if (_tmp->Naxis(0) == 2) {
				dp_output("minimum value     :%16g     at:%4i%5i\n", min, x, y);
			} else {
				dp_output("minimum value     :%16g     at:%4i\n", min, x);
			}
			_tmp->max_pos(&x, &y, &z);
			if (_tmp->Naxis(0) == 3) {
				dp_output("maximum value     :%16g     at:%4i%5i%5i\n", max, x, y, z);
			} else if (_tmp->Naxis(0) == 2) {
				dp_output("maximum value     :%16g     at:%4i%5i\n", max, x, y);
			} else {
				dp_output("maximum value     :%16g     at:%4i\n", max, x);
			}
			_tmp->centroid(&fx, &fy, &fz);
			if (_tmp->Naxis(0) == 3) {
				dp_output("centroid at       : %2.2f %2.2f %2.2f\n", fx, fy, fz);
			} else if (_tmp->Naxis(0) == 2) {
				dp_output("centroid at       : %2.2f %2.2f\n", fx, fy);
			} else {
				dp_output("centroid at       : %2.2f\n", fx);
			}
			dp_output("total   value     :%16g\n", flux);
                    } else {
                        dp_output("Array of complex numbers\n");
                    }
			dp_output("number of pixels  :%16li\n", _tmp->Nelements());
                        if ((int)_tmp->membits != -128) dp_output("mean    value     :%16g\n", avg);
			dp_output("*******************************************************\n");
			} else if (options & 1) {
				unsigned long n = 0;

				for (z = 1; z <= _tmp->Naxis(3); z++) {
					for (y = 1; y <= _tmp->Naxis(2); y++) {
            if ((int)_tmp->membits == -128) {
                /* typeCom */
                for (x = 1; x <= _tmp->Naxis(1); x++) {
                    dp_output("%s\t", FormatComplexdpString(dpComplex(_tmp->cdata[n].r, _tmp->cdata[n].i)).c_str());
                    n++;
                }
            } else {
                for (x = 1; x <= _tmp->Naxis(1); x++) {
                    /* typeCon, typeDbl etc. */
                    dp_output("%6g\t", _tmp->ValueAt(n));
                    n++;
                }
						}
						dp_output("\n");
					}
					dp_output("\n");
				}
			}
		}
		return;
        case typeDpArr:
                dp_output("List with %i elements\n", v.dparrvalue->size());
                return;
		default: dp_output("Don't know how to print this type.\n");
			return;
	}
}

void mem(void) {
	int i;
	dpint64 n;
	dpString vars;
	
    for (auto var:dpuser_vars) {
        switch(var.second.type) {
            case typeCon: dp_output("%s:\tinteger\t= %li\n", var.first.c_str(), var.second.toInt());
                break;
            case typeDbl: dp_output("%s:\tdouble\t= %f\n", var.first.c_str(), var.second.toDouble());
                break;
            case typeStr: dp_output("%s:\tstring\t= %s\n", var.first.c_str(), var.second.toString().c_str());
                break;
            case typeCom: dp_output("%s:\tcomplex\t= %s\n", var.first.c_str(), FormatComplexdpString(*var.second.cvalue).c_str());
                break;
            case typeFits:
                if (var.second.fvalue->Naxis(0) == 1) {
                    dp_output("%s:\tfits\t= %li, %li bytes\n", var.first.c_str(), var.second.fvalue->Naxis(1), var.second.fvalue->bytesAllocated);
                } else if (var.second.fvalue->Naxis(0) == 2) {
                    dp_output("%s:\tfits\t= %li x %li, %li bytes\n", var.first.c_str(), var.second.fvalue->Naxis(1), var.second.fvalue->Naxis(2), var.second.fvalue->bytesAllocated);
                } else {
                    dp_output("%s:\tfits\t= %li x %li x %li, %li bytes\n", var.first.c_str(), var.second.fvalue->Naxis(1), var.second.fvalue->Naxis(2), var.second.fvalue->Naxis(3), var.second.fvalue->bytesAllocated);
                }
                break;
            case typeStrarr: dp_output("%s:\tstring array with %li elements\n", var.first.c_str(), var.second.arrvalue->count());
                break;
            case typeDpArr:
                dp_output("%s:\tList with %i elements\n", var.first.c_str(), var.second.dparrvalue->size());
                break;

            default: dp_output("%s:\tunknown type\n", var.first.c_str());
                break;
		}
	}

	dp_output("\nUser defined functions:\n");
    for (auto fnc:userfunctions) {
        vars.clear();
        for (n = 1; n < userfunction_arguments[fnc.first].size(); n++) {
            vars += userfunction_arguments[fnc.first][n];
            if (n != userfunction_arguments[fnc.first].size() - 1) vars += ", ";
        }
        dp_output("%s(%s)\n", fnc.first.c_str(), vars.c_str());
    }
	for (n = 0; n < userfncnames.count(); n++) {
		if (userfncnames[n] != "") {
            vars = userfncvars[n].c_str();
			vars.replace(dpRegExp(" "), ",");
            dp_output("%s(%s)\n", userfncnames[n].c_str(), vars.c_str());
		}
	}

	dp_output("\nUser defined procedures:\n");
    for (auto proc:userprocedures) {
        vars.clear();
        for (n = 1; n < userprocedure_arguments[proc.first].size(); n++) {
            vars += userprocedure_arguments[proc.first][n];
            if (n != userprocedure_arguments[proc.first].size() - 1) vars += ", ";
        }
        dp_output("%s %s\n", proc.first.c_str(), vars.c_str());
    }
    for (n = 0; n < userpronames.count(); n++) {
		if (userpronames[n] != "") {
            vars = userprovars[n].c_str();
			vars.replace(dpRegExp(" "), ",");
            dp_output("%s %s\n", userpronames[n].c_str(), vars.c_str());
		}
	}
	
//	dp_output("\nCompiled functions:\n");
//	std::vector<compiled_function>::iterator tempIterFunc = compiledfunctions.begin();
//	while (tempIterFunc != compiledfunctions.end()) {
//        dp_output("%s\n", (*tempIterFunc).displaySyntax.c_str());
//		tempIterFunc++;
//	}
	
//	dp_output("\nCompiled procedures:\n");
//	std::vector<compiled_procedure>::iterator tempIterProc = compiledprocedures.begin();
//	while (tempIterProc != compiledprocedures.end()) {
//      dp_output("%s\n", (*tempIterProc).displaySyntax.c_str());
//		tempIterProc++;
//	}
	dp_output("\n");
}

#ifdef HAS_PGPLOT
void plot(Fits *vv, Fits *v, float min, float max, long method, int closeorcontinue) {
	int k;
	int i;
	dpint64 ii;
	float *w = NULL, *x = NULL, xmin, xmax;
	double crval[3], crpix[3], cdelt[3];
	dpString labels[3], _tmp;
	char *title = "", *xtitle = "", *ytitle = "";
    int symbol = -100;

	if (vv) if (vv->Naxis(0) != 1) {
		dp_output("plot: not a 1-dimensional vector.\n");
		return;
	}
	if (v->Naxis(0) != 1) {
		dp_output("plot: not a 1-dimensional vector.\n");
		return;
	}
	if (vv) if (v->Nelements() != vv->Nelements()) {
		dp_output("plot: x and y do not match in size.\n");
		return;
	}

	k = 0;
	w = (float *)malloc(v->Nelements() * sizeof(float));
	if (!w) {
		dp_output("plot: Could not allocate memory.\n");
		return;
	}
	if (vv) for (ii = 0; ii < v->Nelements(); ii++) w[ii] = vv->ValueAt(ii);

	x = (float *)malloc(v->Nelements() * sizeof(float));
	if (!x) {
		free(w);
		dp_output("plot: Could not allocate memory.\n");
		return;
	}
	for (ii = 0; ii < v->Nelements(); ii++) x[ii] = v->ValueAt(ii);

// Check for title and symbol
    if (dpuser_vars.count("title") == 1) {
        if (dpuser_vars["title"].type == typeStr) {
            title = (char *)dpuser_vars["title"].svalue->c_str();
        }
	}
    if (dpuser_vars.count("xtitle") == 1) {
        if (dpuser_vars["xtitle"].type == typeStr) {
            xtitle = (char *)dpuser_vars["xtitle"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("ytitle") == 1) {
        if (dpuser_vars["ytitle"].type == typeStr) {
            ytitle = (char *)dpuser_vars["ytitle"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("symbol") == 1) {
        if (dpuser_vars["symbol"].type == typeCon) {
            symbol = dpuser_vars["symbol"].toInt();
        }
    }

// create x-axes information
	if (!vv) {
		if (v->hasRefPix()) {
			v->GetFloatKeys("CRPIX", crpix, 3);
			v->GetFloatKeys("CRVAL", crval, 3);
			v->GetFloatKeys("CDELT", cdelt, 3);
			for (ii = 0; ii < v->Nelements(); ii++) {
				w[ii] = ((float)(ii+1) - crpix[0]) * cdelt[0] + crval[0];
			}
		} else {
			for (ii = 0; ii < v->Nelements(); ii++) {
				w[ii] = (float)(ii + 1);
			}
		}
	}

	if ((min == 0.0) && (max == 0.0)) {
		min = max = v->ValueAt(0);
		for (ii = 0; ii < v->Nelements(); ii++) {
			if (x[ii] < min) min = x[ii];
			else if (x[ii] > max) max = x[ii];
		}
	}
	if (min == max) {
		min--;
		max++;
	}

	xmin = xmax = w[0];
	for (ii = 0; ii < v->Nelements(); ii++) {
		if (w[ii] < xmin) xmin = w[ii];
		else if (w[ii] > xmax) xmax = w[ii];
	}
	if (xmin == xmax) {
		xmin = 0.0;
		xmax = 1.0;
	}

	cpgqid(&i);
	if (i == 0) {
        if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) != 1) return;

		if (method == 10) {
			if (xmin > 0.0) xmin = log10(xmin); else xmin = 0.0;
			if (xmax > 0.0) xmax = log10(xmax); else xmax = 0.0;
			for (ii = 0; ii < v->Nelements(); ii++) if (w[ii] > 0.0) w[ii] = log10(w[ii]); else w[ii] = 0.0;
		} else if (method == 20) {
			if (min > 0.0) min = log10(min); else min = 0.0;
			if (max > 0.0) max = log10(max); else max = 0.0;
			for (ii = 0; ii < v->Nelements(); ii++) if (x[ii] > 0.0) x[ii] = log10(x[ii]); else x[ii] = 0.0;
		} else if (method == 30) {
			if (xmin > 0.0) xmin = log10(xmin); else xmin = 0.0;
			if (xmax > 0.0) xmax = log10(xmax); else xmax = 0.0;
			if (min > 0.0) min = log10(min); else min = 0.0;
			if (max > 0.0) max = log10(max); else max = 0.0;
			for (ii = 0; ii < v->Nelements(); ii++) if (w[ii] > 0.0) w[ii] = log10(w[ii]); else w[ii] = 0.0;
			for (ii = 0; ii < v->Nelements(); ii++) if (x[ii] > 0.0) x[ii] = log10(x[ii]); else x[ii] = 0.0;
		}	
		cpgenv(xmin, xmax, min, max, 0, method);
		cpglab(xtitle, ytitle, title);
	}
	if (symbol > -32) cpgpt(v->Nelements(), w, x, symbol);
	else cpgline(v->Nelements(), w, x);

	if (closeorcontinue == 0) cpgend();

	free(w);
	free(x);

}
#endif /* HAS_PGPLOT */

#ifdef HAS_PGPLOT
void radialplot(Fits *v, long x, long y, long radius, long center) {
	Fits rdata;
	float *xdata, *ydata;
	float min, max;
	int i;
	char *title = "", *xtitle = "", *ytitle = "";

	v->radial_avg(rdata, x, y, radius, center);
	xdata = (float *)malloc(rdata.Naxis(2) * sizeof(float));
	ydata = (float *)malloc(rdata.Naxis(2) * sizeof(float));
	for (i = 0; i < rdata.Naxis(2); i++) {
		xdata[i] = rdata.ValueAt(rdata.C_I(0, i));
		ydata[i] = rdata.ValueAt(rdata.C_I(1, i));
	}
	min = max = ydata[0];
	for (i = 0; i < rdata.Naxis(2); i++) {
		if (ydata[i] < min) min = ydata[i];
		else if (ydata[i] > max) max = ydata[i];
	}
    if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) == 1) {

        cpgenv(0.0, (float)radius, min, max, 0, 0);

        // Check for title
        if (dpuser_vars.count("title") == 1) {
            if (dpuser_vars["title"].type == typeStr) {
                title = (char *)dpuser_vars["title"].svalue->c_str();
            }
        }
        if (dpuser_vars.count("xtitle") == 1) {
            if (dpuser_vars["xtitle"].type == typeStr) {
                xtitle = (char *)dpuser_vars["xtitle"].svalue->c_str();
            }
        }
        if (dpuser_vars.count("ytitle") == 1) {
            if (dpuser_vars["ytitle"].type == typeStr) {
                ytitle = (char *)dpuser_vars["ytitle"].svalue->c_str();
            }
        }
        cpglab(xtitle, ytitle, title);

        cpgpt(rdata.Nelements() / 2, xdata, ydata, 3);
        cpgend();
    }
	free(xdata);
	free(ydata);
}
#endif /* HAS_PGPLOT */

#ifdef HAS_PGPLOT
void contour(Fits *v, Fits *levels) {
	double dmin, dmax;
	float min, max, xmin, ymin, xmax, ymax;
//	double crpix[2], crval[2], cdelt[2];
	float tr[6] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
	Fits *lev;
	dpString labels[3], _tmp;
	float *data1;
	char *title = "", *xtitle = "", *ytitle = "";
	
    if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) != 1) return;
	
	v->get_minmax(&dmin, &dmax);
	min = (float)dmin;
	max = (float)dmax;
//	min = v->get_min();
//	max = v->get_max();
	lev = CreateFits();
	if (!lev->copy(*levels)) return;
	if (!lev->setType(R4)) return;
	lev->div(100.0);
	lev->mul(max - min);
	lev->add(min);
//	if (v->hasRefPix()) {
//		v->GetFloatKeys("CRPIX", crpix, 2);
//		v->GetFloatKeys("CRVAL", crval, 2);
//		v->GetFloatKeys("CDELT", cdelt, 2);
//		xmin = ((float)0 - crpix[0]) * cdelt[0] + crval[0];
//		xmax = ((float)v->Naxis(1) - 1 - crpix[0]) * cdelt[0] + crval[0];
//		tr[1] *= cdelt[0];
//		ymin = ((float)0 - crpix[1]) * cdelt[1] + crval[1];
//		ymax = ((float)v->Naxis(2) - 1 - crpix[1]) * cdelt[1] + crval[1];
//		tr[5] *= cdelt[1];
//	} else {
		xmin = 1.0;
		xmax = (float)v->Naxis(1);
		ymin = 1.0;
		ymax = (float)v->Naxis(2);
//	}
	cpgenv(xmin, xmax, ymin, ymax, 1, 0);

    // Check for title
    if (dpuser_vars.count("title") == 1) {
        if (dpuser_vars["title"].type == typeStr) {
            title = (char *)dpuser_vars["title"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("xtitle") == 1) {
        if (dpuser_vars["xtitle"].type == typeStr) {
            xtitle = (char *)dpuser_vars["xtitle"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("ytitle") == 1) {
        if (dpuser_vars["ytitle"].type == typeStr) {
            ytitle = (char *)dpuser_vars["ytitle"].svalue->c_str();
        }
    }
    cpglab(xtitle, ytitle, title);

	data1 = floatdata(*v);
	if (data1)
		cpgcont(data1, v->Naxis(1), v->Naxis(2), 1, v->Naxis(1), 1, v->Naxis(2), lev->r4data, lev->Nelements(), tr);
	if (data1 && (data1 != v->r4data)) free(data1);

	cpgend();
}
#endif /* HAS_PGPLOT */

#ifdef HAS_PGPLOT
void graymap(Fits *v, float min, float max, long itf) {
	float xmin, ymin, xmax, ymax;
	double crpix[2], crval[2], cdelt[2];
	float tr[6] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
	dpString labels[3], _tmp;
	float *data1;
	char *title = "", *xtitle = "", *ytitle = "";
	
    if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) != 1) return;

	if ((min == 0.0) && (max == 0.0)) {
		min = v->get_min();
		max = v->get_max();
	}
	if (v->hasRefPix()) {
		v->GetFloatKeys("CRPIX", crpix, 2);
		v->GetFloatKeys("CRVAL", crval, 2);
		v->GetFloatKeys("CDELT", cdelt, 2);
		xmin = ((float)0 - crpix[0]) * cdelt[0] + crval[0];
		xmax = ((float)v->Naxis(1) - 1 - crpix[0]) * cdelt[0] + crval[0];
		tr[1] *= cdelt[0];
		tr[0] = xmin;
		ymin = ((float)0 - crpix[1]) * cdelt[1] + crval[1];
		ymax = ((float)v->Naxis(2) - 1 - crpix[1]) * cdelt[1] + crval[1];
		tr[5] *= cdelt[1];
		tr[3] = ymin;
	} else {
		xmin = 0.0;
        xmax = (float)v->Naxis(1) - 1.0;
		ymin = 0.0;
        ymax = (float)v->Naxis(2) - 1.0;
	}

	cpgenv(xmin, xmax, ymin, ymax, 1, 0);

    // Check for title
    if (dpuser_vars.count("title") == 1) {
        if (dpuser_vars["title"].type == typeStr) {
            title = (char *)dpuser_vars["title"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("xtitle") == 1) {
        if (dpuser_vars["xtitle"].type == typeStr) {
            xtitle = (char *)dpuser_vars["xtitle"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("ytitle") == 1) {
        if (dpuser_vars["ytitle"].type == typeStr) {
            ytitle = (char *)dpuser_vars["ytitle"].svalue->c_str();
        }
    }
    cpglab(xtitle, ytitle, title);

    cpgsitf(itf);
	data1 = floatdata(*v);
	if (data1)
        cpggray(data1, v->Naxis(1), v->Naxis(2), 1, v->Naxis(1), 1, v->Naxis(2), max, min, tr);
	if (data1 && (data1 != v->r4data)) free(data1);

	cpgwedg("R", 1.0, 3.0, max, min, " ");
	cpgend();
}
#endif /* HAS_PGPLOT */

#ifdef HAS_PGPLOT
#ifdef HAS_DPPGPLOT
void shade(Fits *v) {
	double crpix[2], crval[2], cdelt[2];
	Fits *x, *y;
	dpString labels[3], _tmp;
	float *data1;
	char *title = "", *xtitle = "", *ytitle = "";
	int i;

    if (cpgbeg(0, dpuser_vars["plotdevice"].toString().c_str(), 1, 1) != 1) return;

	x = CreateFits();
	y = CreateFits();
	if (!x->create(v->Naxis(1), 1)) return;
	if (!y->create(v->Naxis(2), 1)) return;

	if (v->hasRefPix()) {
		v->GetFloatKeys("CRPIX", crpix, 2);
		v->GetFloatKeys("CRVAL", crval, 2);
		v->GetFloatKeys("CDELT", cdelt, 2);
	} else {
		crval[0] = 1.0;
		crval[1] = 1.0;
		cdelt[0] = 1.0;
		cdelt[1] = 1.0;
	}
	for (i = 0; i < x->Naxis(1); i++) {
		x->r4data[i] = crval[0] + cdelt[0] * i;
	}
	for (i = 0; i < y->Naxis(1); i++) {
		y->r4data[i] = crval[1] + cdelt[1] * i;
	}

    // Check for title
    if (dpuser_vars.count("title") == 1) {
        if (dpuser_vars["title"].type == typeStr) {
            title = (char *)dpuser_vars["title"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("xtitle") == 1) {
        if (dpuser_vars["xtitle"].type == typeStr) {
            xtitle = (char *)dpuser_vars["xtitle"].svalue->c_str();
        }
    }
    if (dpuser_vars.count("ytitle") == 1) {
        if (dpuser_vars["ytitle"].type == typeStr) {
            ytitle = (char *)dpuser_vars["ytitle"].svalue->c_str();
        }
    }

    data1 = floatdata(*v);
	if (data1) {
		cxtal(x->r4data, x->Naxis(1), y->r4data, y->Naxis(1), data1, v->Naxis(1), v->Naxis(2), 0.0, 1.5, 2, xtitle, ytitle, title);
	}
	if (data1 && (data1 != v->r4data)) free(data1);
	cpgend();
}
#endif /* HAS_DPPGPLOT */
#endif /* HAS_PGPLOT */

char *help_summary[] = {
"Introduction to DPUSER:",
"This is an interactive program to handle real numbers, strings,",
"and fits files. Normal operators act as you would expect them.",
"Expressions are evaluated from left to right, first \"^\", then \"*/\",",
"and finally \"+-\". Parentesis can be used.",
" ",
"Strings are generated using double quotes (\"), FITS files can be",
"read in and written to disk by specifying their file name in single",
"quotes (').",
" ",
"Type \"help procedures\" to see a list of available procedures.",
#ifdef HAS_PGPLOT
"Type \"help ppgplot\"    to see a list of available pgplot procedures.",
#endif /* HAS_PGPLOT */
"Type \"help functions\"  to see a list of available functions.",
"Type \"help <name>\" to see help on a specific function/procedure.",
""
};

char *help_help[] = {
"help",
"If no argument is given, a general introduction to dpuser is given,",
"else help on the specified topic is printed.",
""
};

/*
 * Import the help for functions, procedures, and pgplot.
 * The file "Documentation/help.h" is generated using
 * the "makec.dpuser" script in the Documentation directory.
 */
//#include "doc/helpmap.h"

extern std::map<std::string, std::vector<std::string> > functionHelp;

extern std::map<std::string, std::vector<std::string> > procedureHelp;

void CreateHelpMaps(void) {
/*
    int i = 0;
    bool ende = false;
    std::string func;

    while (!ende) {
        func = help_funcs_map[i];
        i++;
        while (strlen(help_funcs_map[i]) > 0) {
            functionHelp[func].push_back(help_funcs_map[i]);
            i++;
        }
        i++;
        if (strlen(help_funcs_map[i]) == 0) ende = true;
    }

    ende = false;

    std::string proc;
    std::vector<std::string> pentry;

    i = 0;
    while (!ende) {
        proc = help_pg_map[i];
        i++;
        while (strlen(help_pg_map[i]) > 0) {
            procedureHelp[proc].push_back(help_pg_map[i]);
            i++;
        }
        i++;
        if (strlen(help_pg_map[i]) == 0) ende = true;
    }
*/
}

/* sorting function for char* */

int stringcmp(const void *a, const void *b)
{
	return strcmp((char *)a, (char *)b);
}

void printHelp(char *msg[]) {
	dp_output("\n\033[4m%s\033[24m\n", msg[0]);

    unsigned long i = 1;
	while (strlen(msg[i]) != 0) {
		dp_output("%s\n", msg[i]);
		i++;
	}
	dp_output("\n");
}

void printHelpNew(char *what) {
    for (int i = 0; i < procedureHelp[what].size(); i++) {
        dp_output("%s", procedureHelp[what].at(i).c_str());
    }
}

void help(char *what) {
	int i, j, k, l, n, count = 0, success = 0;
	char *f;

	if (strlen(what) == 0) {
#ifdef DPQT
		dpuserthread.sendHelp("");
#else
		printHelp(help_summary);
#endif /* DPQT */
		return;
	}
	
/* find out what we want help on */
	if (strcmp(what, "functions") == 0) {
/* list all functions */
#ifdef DPQT
		dpuserthread.sendHelp("functions");
#else
/* find out longest name */
		l = 0;
        for (i = 0; i < funcs.size(); i++) {
 			if ((int)funcs[i].name.length() > l) {
				l = funcs[i].name.length();
			}
		}
		l++;
 		n = 80 / l - 1;
        f = (char *)malloc(sizeof(char) * l * funcs.size());
        for (i = 0, k = 0; k < funcs.size(); i += l, k++) {
			count = funcs[k].name.length();
			for (j = 0; j < count; j++) {
                f[i + j] = funcs[k].name.c_str()[j];
			}
			for (j = count; j < l - 1; j++) {
				f[i + j] = ' ';
			}
			f[i + l - 1] = (char)0;
		}

/* sort functions */
        qsort(f, funcs.size(), sizeof(char)*l, &stringcmp);

/* print out summary */
		j = 0;
		dp_output("\n\033[4mFunctions:\033[24m\n");
        for (i = 0; i < funcs.size(); i++) {
			if (j > n) {
				j = 0;
				dp_output("\n");
			}
			dp_output("%s ", &f[i*l]);
//			dp_output("%s ", (char *)((int)f + i * l));
			j++;
		}
		dp_output("\n\n");
		free(f);
#endif /* DPQT */

		return;
	}
	if (strcmp(what, "procedures") == 0) {
/* list all procedures */
#ifdef DPQT
		dpuserthread.sendHelp("procedures");
//		dpDisplayHelp("procedures");
#else
/* find out longest name */
		l = 0;
		for (i = 126; i < npg; i++) {
 			if ((int)procs[i].name.length() > l) {
				l = procs[i].name.length();
			}
		}
		l++;
 		n = 80 / l - 1;

		f = (char *)malloc(sizeof(char) * l * npg);
		for (i = 0, k = 126; k < npg; i += l, k++) {
			count = procs[k].name.length();
			for (j = 0; j < count; j++) {
                f[i + j] = procs[k].name.c_str()[j];
			}
			for (j = count; j < l - 1; j++) {
				f[i + j] = ' ';
			}
			f[i + l - 1] = (char)0;
		}

/* sort functions */
		qsort(f, npg - 126, sizeof(char)*l, &stringcmp);

/* print out summary */
		j = 0;
		dp_output("\n\033[4mProcedures:\033[24m\n");
		for (i = 0; i < npg - 126; i++) {
			if (j > n) {
				j = 0;
				dp_output("\n");
			}
			dp_output("%s ", &f[i*l]);
//			dp_output("%s ", (char *)((int)f + i * l));
			j++;
		}
		free(f);
		dp_output("\n\n");
#endif /* DPQT */

		return;
	}
#ifdef HAS_PGPLOT
	if (strcmp(what, "pgplot") == 0) {
/* list all pgplot procedures */
#ifdef DPQT
        dpuserthread.sendHelp("pgplot.html");
//		dpDisplayHelp("pgplot");
#else
/* find out longest name */
		l = 0;
		for (i = 0; i < 126; i++) {
 			if ((int)procs[i].name.length() > l) {
				l = procs[i].name.length();
			}
		}
		l++;
 		n = 80 / l - 1;

		f = (char *)malloc(sizeof(char) * l * npg);
		for (i = 0, k = 0; k < 126; i += l, k++) {
			count = procs[k].name.length();
			for (j = 0; j < count; j++) {
                f[i + j] = procs[k].name.c_str()[j];
			}
			for (j = count; j < l - 1; j++) {
				f[i + j] = ' ';
			}
			f[i + l - 1] = (char)0;
		}

/* sort functions */
		qsort(f, 126, sizeof(char)*l, &stringcmp);

/* print out summary */
		j = 0;
		dp_output("\n\033[4mPgplot procedures:\033[24m\n");
		for (i = 0; i < 126; i++) {
			if (j > n) {
				j = 0;
				dp_output("\n");
			}
			dp_output("%s ", &f[i*l]);
//			dp_output("%s ", (char *)((int)f + i * l));
			j++;
		}
		free(f);
		dp_output("\n\n");
#endif /* DPQT */

		return;
	}
#endif /* HAS_PGPLOT */

	if ((i = funcWord(what)) != 0) {
/* it's help on a function */
#ifdef DPQT
		dpuserthread.sendHelp(QString("function_") + QString(what) + ".html");
//		dpDisplayHelp(dpString("function_") + dpString(what) + ".html");
		success = 1;
#else
        if (functionHelp.count(what) == 1) {
            dp_output("\n\033[4mfunction %s\033[24m\n", what);
            for (int i = 0; i < functionHelp.at(what).size(); i++) dp_output("%s\n", functionHelp.at(what).at(i).c_str());
        } else if (functionHelp.count(std::string(what) + " (obsolete)") == 1) {
            std::string o = std::string(what) + " (obsolete)";
            dp_output("\n\033[4mfunction %s\033[24m\n", o.c_str());
            for (int i = 0; i < functionHelp.at(o).size(); i++) dp_output("%s\n", functionHelp.at(o).at(i).c_str());
        } else {
            dp_output("No help available\n");
        }
        success = 1;
#endif /* DPQT */
	}
	if (success != 1) if ((i = pgWord(what)) != -10000) {
/* it's help on a procedure */
#ifdef DPQT
		dpuserthread.sendHelp(QString("procedure_") + QString(what) + ".html");
//        printHelpNew(what);
//		dpDisplayHelp(dpString("procedure_") + dpString(what) + ".html");
		success = 1;
#else
        if (procedureHelp.count(what) == 1) {
            dp_output("\n\033[4mprocedure %s\033[24m\n", what);
            for (int i = 0; i < procedureHelp.at(what).size(); i++) dp_output("%s\n", procedureHelp.at(what).at(i).c_str());
        } else if (procedureHelp.count(std::string(what) + " (obsolete)") == 1) {
            std::string o = std::string(what) + " (obsolete)";
            dp_output("\n\033[4mprocedure %s\033[24m\n", o.c_str());
            for (int i = 0; i < procedureHelp.at(o).size(); i++) dp_output("%s\n", procedureHelp.at(o).at(i).c_str());
        } else {
            dp_output("No help available\n");
        }
        success = 1;
#endif /* DPQT */
	}
    if (dpuser_vars.count(what) == 1) {
        dp_output("variable %s\t", what);
        switch(dpuser_vars[what].type) {
            case typeCon:
                dp_output("integer\t= %li\n", dpuser_vars[what].lvalue);
                break;
            case typeDbl:
                dp_output("double\t= %6g\n", dpuser_vars[what].dvalue); return;
                break;
            case typeStr:
                dp_output("string\t= %s\n", dpuser_vars[what].svalue->c_str());
                break;
            case typeCom:
                dp_output("complex\t= %s\n", FormatComplexdpString(*dpuser_vars[what].cvalue).c_str());
                break;
            case typeFits: dp_output("fits\t = ");
                if (dpuser_vars[what].fvalue->Naxis(0) != 3) {
                    dp_output("%li x %li, %li bytes\n", dpuser_vars[what].fvalue->Naxis(1), dpuser_vars[what].fvalue->Naxis(2), dpuser_vars[what].fvalue->bytesAllocated);
                } else {
                    dp_output("%li x %li x %li, %li bytes\n", dpuser_vars[what].fvalue->Naxis(1), dpuser_vars[what].fvalue->Naxis(2), dpuser_vars[what].fvalue->Naxis(3), dpuser_vars[what].fvalue->bytesAllocated);
                }
                break;
            default: dp_output("unknown type\n");
                break;
        }
        success = 1;
    }
	if (!success) {
		dp_output("No help available for %s\n", what);
	}
}
