/*--------------------------------------------------------------------------
 * pgraph.ox - graphics functions mapped from Gauss to Ox
 *--------------------------------------------------------------------------*/
#ifndef PGRAPH_INCLUDED
#define PGRAPH_INCLUDED

#include <oxstd.h>
#ifdef GNUDRAW
  #include <packages/gnudraw/gnudraw.h>
#else
  #include <oxdraw.h>
#endif

#pragma array_base(1)

namespace gauss {

decl _pagedim;
decl _pageshf;
decl _pagesiz;
decl _pappend;
decl _parrow3;
decl _parrow;
decl _pascx;
decl _pascy;
decl _paxes = 1;
decl _paxht;
decl _paxmarx;
decl _pbartyp;
decl _pbarwid;
decl _pbox3d;
decl _pbox;
decl _pboxctl;
decl _pboxlim;
decl _pcmdlin;
decl _pcolor;
decl _pcrop = 1;
decl _pcross;
decl _pcsel;
decl _pcvfile;
decl _pcwin;
decl _pdate;
decl _perrbar;
decl _pfirstw;
decl _pfonts;
decl _pframe;
decl _pgrid;
decl _phard;
decl _plctrl = 0;
decl _plegctl = 0;
decl _plegstr = 0;
decl _plev;
decl _pline3d;
decl _pline = 0;
decl _plotshf;
decl _plotsiz;
decl _plsel;
decl _pltype;
decl _plwidth;
decl _pmargin;
decl _pmcolor;
decl _pmjr;
decl _pmnr;
decl _pmsgctl;
decl _pmsgstr = 0;
decl _pmtic;
decl _pncwin;
decl _pnotify;
decl _pnum;
decl _pnumht;
decl _poptic;
decl _pqg_ver;
decl _pqgedit;
decl _pqgplay;
decl _pqgtype;
decl _protate;
decl _prtfile;
decl _pscreen;
decl _psilent;
decl _pssel;
decl _pstype;
decl _psurf;
decl _psym3d;
decl _psym = 0;
decl _psymsiz;
decl _ptek = "";
decl _pticout;
decl _ptitle = 0;
decl _ptitlht;
decl _pversno;
decl _pview;
decl _pvolume;
decl _pwindmx = <>;
decl _pwindno;
decl _pworld;
decl _pxfmt;
decl _pxlabel = 0;
decl _pxmem;
decl _pxpmax;
decl _pxscale;
decl _pxsci;
decl _pyfmt;
decl _pylabel = 0;
decl _pypmax;
decl _pyscale;
decl _pysci;
decl _pzclr;
decl _pzfmt;
decl _pzlabel = 0;
decl _pzoom;
decl _pzpmax;
decl _pzscale;
decl _pzsci;

static decl s_iArea_pgraph = 0;
static decl s_bWindowing = 0;
static decl s_cWindow = 0;
static decl s_iGreekFont = 4;
static decl s_dYmin, s_dYmax, s_dXmin, s_dXmax;

static decl s_asGreekMap =
{
	"$\\epsilon$",	   // 33
	"$($",
	"$\\equiv$",
	"$\\approx$",
	"$\\uparrow$",
	"$\\surd$",
	"$\'$",
	"$\\subset$",	   // 40
	"$\\supset$",
	"$\\times$",
	"$\\pm$",
	"$\\int$",
	"$\\mp$",
	"$\\cdot$",
	"$\\div$",
	"$\\nabla$",
	"$\\surd$",
	"$\\oint$",		   // 50
	"$<$",
	"$>$",
	"$/$",
	"$\\exists$",
	"$\\|$",
	"$\\infty$",
	"$\\odot$",
	"$\\rightarrow$",
	"$\\leftarrow$",   
	"$\\leq$",		   // 60
	"$\\neq$",
	"$\\geq$",
	"$>\\sim$",
	"$\\cup$",
	"$1/2$",
	"$1/3$",
	"${H}$",
	"$\\Delta$",	   
	"$1/8$",		   
	"$\\emptyset$",	   // 70
	"$\\lceil$",
	"${X}$",
	"$2/3$",
	"$\\bot$",
	"$3/8$",
	"$\\wedge$",
	"$5/8$",
	"$7/8$",		   
	"$1/4$",		   
	"$\\Pi$",		   // 80
	"$\\Theta$",
	"$\\P$",
	"$\\Sigma$",
	"$<\\sim$",
	"$\\Upsilon$",
	"$\\leftrightarrow$",
	"$\\Omega$",
	"$\\Xi$",		   
	"$\\Psi$",
	"$>\\sim$",		   // 90
	"$[$",
	"$\\partial$",
	"$]$",
	"$\\cap$",
	"$\\downarrow$",
	"$\"$",
	"$\\alpha$",
	"$\\beta$",		   
	"$\\eta$",
	"$\\delta$",	   // 100
	"$\\varepsilon$",
	"$\\varphi$",
	"$\\gamma$",
	"$\\chi$",
	"$\\iota$",
	"$\\iota$",
	"$\\kappa$",
	"$\\lambda$",
	"$\\mu$",		   
	"$\\nu$",		   // 110
	"${o}$",			   
	"$\\pi$",
	"$\\vartheta$",
	"$\\rho$",
	"$\\sigma$",
	"$\\tau$",
	"$\\upsilon$",
	"$)$",
	"$\\omega$",	   
	"$\\xi$",		   // 120
	"$\\psi$",
	"$\\zeta$",
	"$\{$",
	"$\\int$",
	"$\}$",
	"$\\propto$"	   // 126
};

static pgString2tex(const s)
{
	decl i, j, sn, ch, sa, fgreek, fsubscript, fsuperscript;
	decl slen = ::sizeof(s);
	fgreek = fsuperscript = fsubscript = FALSE;

	sn = new string[2 * slen + 2000];
	for (i = j = 1; i <= slen; ++i)
	{
		ch = s[i];
		if (ch == ']')					 // subscript
		{
			if (fsuperscript)
			{
				fsuperscript = FALSE;
				sn[j : j+1] = "}$", j += 2;
			}
			else if (!fsubscript)
			{
				sa = "$_{";
				if (j > 1 && sn[j - 1] == '$')
					--j, sa = sa[2:];
				sn[j:] = sa;
				j += sizeof(sa);
				fsubscript = TRUE;
			}
		}
		else if (ch == '[')					 // superscript
		{
			if (fsubscript)
			{
				fsubscript = FALSE;
				sn[j : j+1] = "}$", j += 2;
			}
			else if (!fsuperscript)
			{
				sa = "$^{";
				if (j > 1 && sn[j - 1] == '$')
					--j, sa = sa[2:];
				sn[j:] = sa;
				j += sizeof(sa);
				fsuperscript = TRUE;
			}
		}
		else if (ch == '@')					 // superscript
		{	sn[j++] = s[i + 1];
			++i;
		}
		else if (ch == '$' || ch == '\\')	 // must be escaped
		{
			sn[j++] = '\\';
			sn[j++] = ch;
		}
		else if (ch <= 127)					 // normal character
		{
			if (fgreek && ch > 32)
			{
				sa = s_asGreekMap[ch - 32];
				if (j > 1 && sn[j - 1] == '$')
					--j, sa = sa[2:];
				sn[j:] = sa;
				j += sizeof(sa);
			}
			else
				sn[j++] = ch;
		}
		else if (ch >= 201)					 // fonts 1-4
		{
			fgreek = (ch - 200 == s_iGreekFont);
		}
	}
	return sn[:j];
}
static pgSplitString(const s, const chAt, const bToTex)
{
	decl as = {}, i, j, len1 = ::sizeof(s) + 1;

	for (i = j = 1; j <= len1; ++j)
	{
		if (s[j] == chAt || j == len1)
		{	if (j > i)
			{
				if (bToTex)
					as ~= pgString2tex(s[i : j - 1]);
				else
					as ~= s[i : j - 1];
			}
			i = j + 1;
		}
	}
	return as;
}
static pgInch2px(const i)
{
	decl x;
	if (s_bWindowing && ::sizerc(_pwindmx))
		x = max(_pwindmx[][1]);
	else
		x = 9.0;
	return i / x * 15000;
}
static pgInch2py(const i)
{
	decl y;
	if (s_bWindowing && ::sizerc(_pwindmx))
		y = max(_pwindmx[][2]);
	else
		y = 6.855;
	return i / y * 10000;
}
static pgPixl2px(const i)
{
	return i / 4096 * 15000;
}
static pgPixl2py(const i)
{
	return i / 3120 * 10000;
}
static pgCheckAxesHide(const bIs3d)
{
	if (_paxes == 1)
		return;
	decl fnone = ::sizerc(_paxes) == 1 && _paxes == 0;
	if (fnone || ::sizerc(_paxes) >= 1 && _paxes[1] == 0)
		::DrawAxisAuto(s_iArea_pgraph, 1, FALSE);
	if (fnone || ::sizerc(_paxes) >= 2 && _paxes[2] == 0)
		::DrawAxisAuto(s_iArea_pgraph, 0, FALSE);
	if (bIs3d && (fnone || ::sizerc(_paxes) >= 3 && _paxes[3] == 0) )
		::DrawAxisAuto(s_iArea_pgraph, 2, FALSE);
}
pgDrawLine(const v)
{
	if (columns(v) < 8)
		return;
	decl color = 1 + v[8], r;

	if (v[1] == 1)
	{
		decl x1 = v[3], y1 = v[4], x2 = v[5], y2 = v[6];
		if (_pcrop == 1 || (rows(_pcrop) == 5 && _pcrop[2]) )
		{
			if (s_dYmin < s_dYmax)
				y1 = max(s_dYmin, y1), y2 = min(s_dYmax, y2);
			if (s_dXmin < s_dXmax)
				x1 = max(s_dXmin, x1), x2 = min(s_dXmax, x2);
		}
		::DrawLine(s_iArea_pgraph, x1, y1, x2, y2, color);
	}
	else if (v[1] == 2)
		::DrawPLine(s_iArea_pgraph, pgInch2px(v[3]), pgInch2py(v[4]), pgInch2px(v[5]), pgInch2py(v[6]), color);
	else if (v[1] == 3)
		::DrawPLine(s_iArea_pgraph, pgPixl2px(v[3]), pgPixl2py(v[4]), pgPixl2px(v[5]), pgPixl2py(v[6]), color);
	else if (v[1] == 4)
	{
		r = v[5];
		::DrawSymbol(s_iArea_pgraph, v[3] - r, v[4] - r, v[3] + r, v[4] + r, PL_CIRCLE, color);
	}
	else if (v[1] == 5)
	{
		r = v[5];
		::DrawPSymbol(s_iArea_pgraph, pgInch2px(v[3] - r), pgInch2py(v[4] - r), pgInch2px(v[3] + r), pgInch2py(v[4] + r), PL_CIRCLE, color);
	}
}
pgDrawSymbol(const v)
{
	if (columns(v) < 7)
		return;
	decl color = 1 + v[5], r;
	decl symbols =
	<PL_CIRCLE, PL_BOX, PL_TRIANGLE, PL_PLUS, PL_DIAMOND, PL_DASH,	PL_CROSS,
	 PL_FILLCIRCLE, PL_FILLBOX, PL_FILLTRIANGLE, PL_PLUS, PL_FILLDIAMOND, PL_DASH,	PL_CROSS>;
	decl sym = symbols[v[3]];

	if (v[6] == 1)
	{
		// seems height is in pixels?
		::DrawSymbol(s_iArea_pgraph, v[1] - 0.01, v[2] - 0.01, v[1] + 0.01, v[2] + 0.01, sym, color);
	}
	else
	{
		r = v[4] / 2;
		::DrawPSymbol(s_iArea_pgraph, pgInch2px(v[1] - r), pgInch2py(v[2] - r), pgInch2px(v[1] + r), pgInch2py(v[2] + r), sym, color);
	}
}
static pgSetDrawWindow(const i)
{
	s_dYmin = s_dXmin = +.Inf;
	s_dYmax = s_dXmax = -.Inf;
	s_iArea_pgraph = i;
}
static pgShowDrawWindow()
{
	for (decl i = 1; i <= ::rows(_pline); ++i)
		pgDrawLine(_pline[i][]);
	_pline = 0;
	for (decl i = 1; i <= ::rows(_psym); ++i)
		pgDrawSymbol(_psym[i][]);
	_psym = 0;

	if (::isstring(_ptitle))
		::DrawTitle(s_iArea_pgraph, _ptitle);
	if (::isstring(_pxlabel))
		::DrawText(s_iArea_pgraph, _pxlabel, 0, 0, -1, -1, TEXT_XLABEL);
	if (::isstring(_pylabel))
		::DrawText(s_iArea_pgraph, _pylabel, 0, 0, -1, -1, TEXT_YLABEL);
	if (::isstring(_pzlabel))
		::DrawText(s_iArea_pgraph, _pzlabel, 0, 0, -1, -1, TEXT_ZLABEL);

	decl as = pgSplitString(_pmsgstr, 0, TRUE);

	for (decl i = 1; i <= ::sizeof(as) && i <= ::rows(_pmsgctl); ++i)
	{
		if (_pmsgctl[i][5] == 1)
			::DrawText(s_iArea_pgraph, as[i], _pmsgctl[i][1], _pmsgctl[i][2],
				-1, 1.1 * pgInch2py(_pmsgctl[i][3]), 0, _pmsgctl[i][4]);
		else
			::DrawPText(s_iArea_pgraph, as[i], pgInch2px(_pmsgctl[i][1]), pgInch2py(_pmsgctl[i][2]),
				-1, 1.1 * pgInch2py(_pmsgctl[i][3]), 0, _pmsgctl[i][4]);
	}
	if (!s_bWindowing)
	{
		++s_cWindow;
		::SetDrawWindow(sprint("OxGauss", "%2d", s_cWindow));
		::ShowDrawWindow();
	    if (_ptek != "" && _ptek != "notek")
    		::SaveDrawWindow(_ptek);
		pgSetDrawWindow(0);
	}
}
static pgLegends()
{
	if (_plegctl == 0)
		return {};
	return pgSplitString(_plegstr, 0, TRUE);
}
static pgXY(const x, const y, const fLogx, const fLogy)
{
	decl isym = _plctrl < 0 ? 1 : _plctrl > 0 ? 2 : 0;
	decl aslegends = pgLegends();

	pgCheckAxesHide(FALSE);
	s_dYmin = min(s_dYmin, y);
	s_dYmax = max(s_dYmax, y);
	s_dXmin = min(s_dXmin, x);
	s_dXmax = max(s_dXmax, x);
	
	if (::sizec(y) == 1)
		::DrawXMatrix(s_iArea_pgraph, y', aslegends, x', "", isym, 2);
	else if (::sizec(x) == 1)
	{
		for (decl i = 1; i <= ::sizec(y); ++i)
			::DrawXMatrix(s_iArea_pgraph, y[][i]',
				i <= ::sizeof(aslegends) ? aslegends[i] : "", x', "", isym, 1 + i);
	}
	else
	{
		for (decl i = 1; i <= ::sizec(y); ++i)
			::DrawXMatrix(s_iArea_pgraph, y[][i]',
				i <= ::sizeof(aslegends) ? aslegends[i] : "", x[][i]', "", isym, 1 + i);
	}
	if (fLogx)
	{
		::DrawAxisAuto(s_iArea_pgraph, 1);
		::DrawAdjust(ADJ_AXISSCALE, AXIS_LOG10);
	}
	if (fLogy)
	{
		::DrawAxisAuto(s_iArea_pgraph, 0);
		::DrawAdjust(ADJ_AXISSCALE, AXIS_LOG10);
	}
	pgShowDrawWindow();
	return {};
}

asclabel(const asx, const asy)
{
    return __printunsup__("asclabel");
}
axmargin(const left, const right, const top, const bottom)
{
    return {};
}
bar(const vx, const heights)
{
	pgCheckAxesHide(FALSE);
	
	s_dYmin = min(s_dYmin, heights);
	s_dYmax = max(s_dYmax, heights);

	if (::sizerc(vx) < 2)
	{	s_dXmin = min(s_dXmin, 1);
		s_dXmax = max(s_dXmax, sizer(heights));

		for (decl i = 1; i <= ::sizec(heights); ++i)
				::DrawHistogram(s_iArea_pgraph, heights[][i]');
	}
	else
	{
		decl d = vx[2] - vx[1], x0 = vx[1] - d / 2;
		s_dXmin = min(s_dXmin, x0);
		s_dXmax = max(s_dXmax, x0 + d * sizer(heights));
		for (decl i = 1; i <= ::sizec(heights); ++i)
			::DrawHistogram(s_iArea_pgraph, heights[][i]',
				x0, d);
	}

	pgShowDrawWindow();
	return {};
}
begwind()
{
    ::CloseDrawWindow();
	pgSetDrawWindow(0);
	_pwindmx = <>;

	s_bWindowing = 1;
	return {};
}
box(const vgroup, const my)
{
	pgCheckAxesHide(FALSE);

	for (decl i = 1; i <= columns(my); ++i)
	{
		if (i > 1)
			pgSetDrawWindow(s_iArea_pgraph + 1);

		s_dYmin = min(s_dYmin, my[][i]);
		s_dYmax = max(s_dYmax, my[][i]);
		s_dXmin = min(s_dXmin, 0);
		s_dXmax = max(s_dXmax, 2);
		
		::DrawBoxPlot(s_iArea_pgraph, my[][i]', "");
	}
	pgShowDrawWindow();
	return {};
}
contour(const x, const y, const z)
{
	s_dYmin = min(s_dYmin, y);
	s_dYmax = max(s_dYmax, y);
	s_dXmin = min(s_dXmin, x);
	s_dXmax = max(s_dXmax, x);
	::DrawXYZ(s_iArea_pgraph, x, y', z, 2);
	pgShowDrawWindow();
	return {};
}
draw()
{
	pgCheckAxesHide(FALSE);
	
	if (!s_bWindowing)
		pgShowDrawWindow();
	return {};
}
endwind()
{
	if (::rows(_pwindmx))	// translate inches to `pixels'
	{
		decl amax, left, top;
		amax = max(_pwindmx[][1]);
		_pwindmx[][1] /= amax;
		_pwindmx[][3] /= amax;
		amax = max(_pwindmx[][2]);
		_pwindmx[][2] /= amax;
		_pwindmx[][4] /= amax;
		for (decl i = 1; i <= ::rows(_pwindmx); ++i)
		{
			left = _pwindmx[i][3] ? 15000 * _pwindmx[i][3] : 800;
			top = _pwindmx[i][4] ? 10000 * _pwindmx[i][4] : 800;
			::DrawAdjust(ADJ_AREA_P, i - 1, left, top,
				15000 * _pwindmx[i][1] - 2 * left, 10000 * _pwindmx[i][2] - 2 * top);
		}
	}

	s_bWindowing = 0;
	pgShowDrawWindow();	
	_pwindmx = <>;
	return {};
}
fonts(const sfont)
{
	decl s = pgSplitString(strlwr(sfont), ' ', FALSE);
	s_iGreekFont = strfind(s, "simgrma");
    return {};
}
getwind()
{
	return s_iArea_pgraph + 1;
}
graphprt(const str)
{
	return {};
}
graphset()
{
	_paxes = 1;
	_pcrop = 1;
	_plctrl = 0;
	_plegstr = 0;
	_plegctl = 0;
	_pline = 0;
	_pmsgstr = 0;
	_psym = 0;
	_ptek = "";
	_ptitle = 0; 
	_pylabel = 0;
	_pxlabel = 0;
	_pzlabel = 0;
	_pwindmx = <>;
	s_iGreekFont = 4;
	return {};
}
hardcopy(const str)
{
	return {};
}
static __hist__(const x, const v, const dScale)
{
	decl m, xx, s, xmax = max(x), xmin = min(x);

	if (::sizerc(v) == 1)
	{
        decl inc = (xmax - xmin) / v;
        decl lo = xmin + inc;
        decl hi = xmax;
        s = ::range(lo, lo + inc * (v - 1), inc)';
        s[rows(s)] = xmax;
		xx = s[1 : ::sizer(s) - 1][];
        m = s - (s - (xmin | xx)) / 2;
	}
	else
	{
        if  (xmax > v[rows(v)])
            s = v | maxc(x);					 
		xx=v[1 : ::sizer(v) - 1][];
        m = v - (v - (xmin | xx)) / 2;
	}
	decl vb = counts(x, s)' / dScale;

	bar(m, vb'); 
	return {s, m, vb'};
}
hist(const x, const v)
{
	return __hist__(x, v, 1);
}
histf(const x, const v)
{
	return bar(v, x);
}
histp(const x, const v)
{
	return __hist__(x, v, ::sizerc(x) / 100);
}
line(const x, ...)
{
    return __printunsup__("line");
}
loadwin(const sname)
{
    return __printunsup__("loadwin");
}
loglog(const x, const y)
{
	return pgXY(::log10(x), ::log10(y), 1, 1);
}
logx(const x, const y)
{
	return pgXY(::log10(x), y, 1, 0);
}
logy(const x, const y)
{
	return pgXY(x, ::log10(y), 0, 1);
}
makewind(const xsize, const ysize, const xshft, const yshft, const typ)
{
	_pwindmx |= xsize ~ ysize ~ xshft ~ yshft;
	return {};
}
margin(const l, const r, const t, const b)
{
    return {};
}
nextwind()
{
    pgSetDrawWindow(s_iArea_pgraph + 1);
	return {};
}
plot(const x, const y)
{
    return __printunsup__("plot");
}
plotsym(const ch)
{
    return __printunsup__("plotsym");
}
polar(const radius, const theta)
{
    return __printunsup__("polar");
}
rerun()
{
	return {};
}
scale(const x, const y)
{
    return __printunsup__("scale");
}
scale3d(const x, const y, const z)
{
    return __printunsup__("scale3d");
}
setwind(const n)
{
	s_bWindowing = 1;
	pgSetDrawWindow(n - 1);
	return {};
}
surface(const x, const y, const z)
{
	pgCheckAxesHide(TRUE);
	s_dYmin = min(s_dYmin, y);
	s_dYmax = max(s_dYmax, y);
	s_dXmin = min(s_dXmin, x);
	s_dXmax = max(s_dXmax, x);
	
	::DrawXYZ(s_iArea_pgraph, x, y', z);
	pgShowDrawWindow();
	return {};
}
title(const s)
{
	_ptitle = pgString2tex(s);
	return {};
}
view(const x, const y, const z)
{
    return __printunsup__("view");
}
viewxyz(const x, const y, const z)
{
    return __printunsup__("viewxyz");
}
volume(const x, const y, const z)
{
//  only works is volume is large enough for graph
//	::DrawAdjust(ADJ_AREA_X, s_iArea_pgraph, 0, x);
//	::DrawAdjust(ADJ_AREA_Y, s_iArea_pgraph, 0, y);
//	::DrawAdjust(ADJ_AREA_Z, s_iArea_pgraph, 0, z);
	return {};
}
window(const r, const c, const trans)
{
	return begwind();
}
xlabel(const s)
{
	_pxlabel = pgString2tex(s);
	return {};
}
xtics(const min, const max, const step, const minordiv)
{
    return __printunsup__("xtics");
}
xy(const x, const y)
{
	return pgXY(x, y, 0, 0);
}

xyz(const x, const y, const z)
{
	decl isym = _plctrl < 0 ? 1 : _plctrl > 0 ? 2 : 0;
	decl aslegends = pgLegends();

	pgCheckAxesHide(TRUE);
	s_dYmin = min(s_dYmin, y);
	s_dYmax = max(s_dYmax, y);
	s_dXmin = min(s_dXmin, x);
	s_dXmax = max(s_dXmax, x);

	for (decl i = 1; i <= ::columns(x); ++i)
	{
		::DrawXMatrix(s_iArea_pgraph, y[][i]',
			i <= ::sizeof(aslegends) ? aslegends[i] : "", x[][i]', "", isym, 2 + i);
		::DrawZ(::columns(z) == 1 ? z : z[][i], "", ZMODE_3D);
	}	
	pgShowDrawWindow();
	return {};
}
ylabel(const s)
{
	_pylabel = pgString2tex(s);
	return {};
}
ytics(const min, const max, const step, const minordiv)
{
    return __printunsup__("ytics");
}
zlabel(const s)
{
	_pzlabel = pgString2tex(s);
	return {};
}
ztics(const min, const max, const step, const minordiv)
{
    return __printunsup__("ztics");
}

}

#endif
