package imgui

// #cgo CXXFLAGS: -std=c++11
// #include "implotWrapper.h"
import "C"
import "unsafe"

// The following functions MUST be called BEFORE BeginPlot!
// Set the axes range limits of the next plot. Call right before BeginPlot(). If ImGuiCond_Always is used, the axes limits will be locked.
func ImPlotSetNextPlotLimits(xmin, xmax, ymin, ymax float64, cond Condition) {
	C.iggImPlotSetNextPlotLimits(C.double(xmin), C.double(xmax), C.double(ymin), C.double(ymax), C.int(cond))
}

func ImPlotSetNextPlotTicksX(values []float64, labels []string, showDefault bool) {
	if len(values) == 0 || len(labels) == 0 {
		return
	}

	labelsArg := make([]*C.char, len(labels))
	for i, l := range labels {
		la, lf := wrapString(l)
		defer lf()
		labelsArg[i] = la
	}

	C.iggImPlotSetNextPlotTicksX(
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		&labelsArg[0],
		castBool(showDefault),
	)
}

func ImPlotSetNextPlotTicksY(values []float64, labels []string, showDefault bool, yAxis int) {
	if len(values) == 0 || len(labels) == 0 {
		return
	}

	labelsArg := make([]*C.char, len(labels))
	for i, l := range labels {
		la, lf := wrapString(l)
		defer lf()
		labelsArg[i] = la
	}

	C.iggImPlotSetNextPlotTicksY(
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		&labelsArg[0],
		castBool(showDefault),
		C.int(yAxis),
	)
}

func ImPlotFitNextPlotAxis(x, y, y2, y3 bool) {
	C.iggImPlotFitNextPlotAxes(
		castBool(x),
		castBool(y),
		castBool(y2),
		castBool(y3),
	)
}

type ImPlotContext struct {
	handle C.IggImPlotContext
}

// Creates a new ImPlot context. Call this after ImGui::CreateContext.
func ImPlotCreateContext() *ImPlotContext {
	return &ImPlotContext{handle: C.iggImPlotCreateContext()}
}

// Destroys an ImPlot context. Call this before ImGui::DestroyContext. NULL = destroy current context
func ImPlotDestroyContext() {
	C.iggImPlotDestroyContext()
}

type ImPlotFlags int

const (
	ImPlotFlags_None        ImPlotFlags = 0       // default
	ImPlotFlags_NoTitle     ImPlotFlags = 1 << 0  // the plot title will not be displayed (titles are also hidden if preceeded by double hashes, e.g. "##MyPlot")
	ImPlotFlags_NoLegend    ImPlotFlags = 1 << 1  // the legend will not be displayed
	ImPlotFlags_NoMenus     ImPlotFlags = 1 << 2  // the user will not be able to open context menus with right-click
	ImPlotFlags_NoBoxSelect ImPlotFlags = 1 << 3  // the user will not be able to box-select with right-click drag
	ImPlotFlags_NoMousePos  ImPlotFlags = 1 << 4  // the mouse position, in plot coordinates, will not be displayed inside of the plot
	ImPlotFlags_NoHighlight ImPlotFlags = 1 << 5  // plot items will not be highlighted when their legend entry is hovered
	ImPlotFlags_NoChild     ImPlotFlags = 1 << 6  // a child window region will not be used to capture mouse scroll (can boost performance for single ImGui window applications)
	ImPlotFlags_Equal       ImPlotFlags = 1 << 7  // primary x and y axes will be constrained to have the same units/pixel (does not apply to auxiliary y-axes)
	ImPlotFlags_YAxis2      ImPlotFlags = 1 << 8  // enable a 2nd y-axis on the right side
	ImPlotFlags_YAxis3      ImPlotFlags = 1 << 9  // enable a 3rd y-axis on the right side
	ImPlotFlags_Query       ImPlotFlags = 1 << 10 // the user will be able to draw query rects with middle-mouse or CTRL + right-click drag
	ImPlotFlags_Crosshairs  ImPlotFlags = 1 << 11 // the default mouse cursor will be replaced with a crosshair when hovered
	ImPlotFlags_AntiAliased ImPlotFlags = 1 << 12 // plot lines will be software anti-aliased (not recommended for high density plots, prefer MSAA)
	ImPlotFlags_CanvasOnly  ImPlotFlags = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoMousePos
)

type ImPlotAxisFlags int

const (
	ImPlotAxisFlags_None          ImPlotAxisFlags = 0       // default
	ImPlotAxisFlags_NoLabel       ImPlotAxisFlags = 1 << 0  // the axis label will not be displayed (axis labels also hidden if the supplied string name is NULL)
	ImPlotAxisFlags_NoGridLines   ImPlotAxisFlags = 1 << 1  // no grid lines will be displayed
	ImPlotAxisFlags_NoTickMarks   ImPlotAxisFlags = 1 << 2  // no tick marks will be displayed
	ImPlotAxisFlags_NoTickLabels  ImPlotAxisFlags = 1 << 3  // no text labels will be displayed
	ImPlotAxisFlags_Foreground    ImPlotAxisFlags = 1 << 4  // grid lines will be displayed in the foreground (i.e. on top of data) in stead of the background
	ImPlotAxisFlags_LogScale      ImPlotAxisFlags = 1 << 5  // a logartithmic (base 10) axis scale will be used (mutually exclusive with ImPlotAxisFlags_Time)
	ImPlotAxisFlags_Time          ImPlotAxisFlags = 1 << 6  // axis will display date/time formatted labels (mutually exclusive with ImPlotAxisFlags_LogScale)
	ImPlotAxisFlags_Invert        ImPlotAxisFlags = 1 << 7  // the axis will be inverted
	ImPlotAxisFlags_NoInitialFit  ImPlotAxisFlags = 1 << 8  // axis will not be initially fit to data extents on the first rendered frame (also the case if SetNextPlotLimits explicitly called)
	ImPlotAxisFlags_AutoFit       ImPlotAxisFlags = 1 << 9  // axis will be auto-fitting to data extents
	ImPlotAxisFlags_RangeFit      ImPlotAxisFlags = 1 << 10 // axis will only fit points if the point is in the visible range of the **orthoganol** axis
	ImPlotAxisFlags_LockMin       ImPlotAxisFlags = 1 << 11 // the axis minimum value will be locked when panning/zooming
	ImPlotAxisFlags_LockMax       ImPlotAxisFlags = 1 << 12 // the axis maximum value will be locked when panning/zooming
	ImPlotAxisFlags_Lock          ImPlotAxisFlags = ImPlotAxisFlags_LockMin | ImPlotAxisFlags_LockMax
	ImPlotAxisFlags_NoDecorations ImPlotAxisFlags = ImPlotAxisFlags_NoLabel | ImPlotAxisFlags_NoGridLines | ImPlotAxisFlags_NoTickMarks | ImPlotAxisFlags_NoTickLabels
)

type ImPlotYAxis int

const (
	ImPlotYAxis_1 ImPlotYAxis = 0
	ImPlotYAxis_2 ImPlotYAxis = 1
	ImPlotYAxis_3 ImPlotYAxis = 2
)

//-----------------------------------------------------------------------------
// Begin/End Plot
//-----------------------------------------------------------------------------

// Starts a 2D plotting context. If this function returns true, EndPlot() must
// be called, e.g. "if (BeginPlot(...)) { ... EndPlot(); }". #title_id must
// be unique. If you need to avoid ID collisions or don't want to display a
// title in the plot, use double hashes (e.g. "MyPlot##Hidden" or "##NoTitle").
// If #x_label and/or #y_label are provided, axes labels will be displayed.
func ImPlotBegin(title string, xLabel, yLabel string, size Vec2, flags ImPlotFlags, xFlags, yFlags, y2Flags, y3Flags ImPlotAxisFlags, y2Label, y3Label string) bool {
	titleArg, titleFin := wrapString(title)
	defer titleFin()

	xLabelArg, xLabelFin := wrapString(xLabel)
	defer xLabelFin()

	yLabelArg, yLabelFin := wrapString(yLabel)
	defer yLabelFin()

	sizeArg, _ := size.wrapped()

	y2LabelArg, y2LabelFin := wrapString(y2Label)
	defer y2LabelFin()

	y3LabelArg, y3LabelFin := wrapString(y3Label)
	defer y3LabelFin()

	return C.iggImPlotBeginPlot(
		titleArg,
		xLabelArg,
		yLabelArg,
		sizeArg,
		C.int(flags),
		C.int(xFlags),
		C.int(yFlags),
		C.int(y2Flags),
		C.int(y3Flags),
		y2LabelArg,
		y3LabelArg) != 0
}

// Only call EndPlot() if BeginPlot() returns true! Typically called at the end
// of an if statement conditioned on BeginPlot().
func ImPlotEnd() {
	C.iggImPlotEndPlot()
}

// Plots a vertical bar graph. #width and #shift are in X units.
func ImPlotBars(label string, values []float64, width, shift float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelFin := wrapString(label)
	defer labelFin()

	C.iggImPlotBars(labelArg, (*C.double)(unsafe.Pointer(&values[0])), C.int(len(values)), C.double(width), C.double(shift), C.int(offset))
}

// Plots a vertical bar graph. #width and #shift are in X units.
func ImPlotBarsXY(label string, xs, ys []float64, width float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotBarsXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.double(width),
		C.int(offset))
}

// Plots a horizontal bar graph. #height and #shift are in Y units.
func ImPlotBarsH(label string, values []float64, height, shift float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelFin := wrapString(label)
	defer labelFin()

	C.iggImPlotBarsH(labelArg, (*C.double)(unsafe.Pointer(&values[0])), C.int(len(values)), C.double(height), C.double(shift), C.int(offset))
}

// Plots a horizontal bar graph. #height and #shift are in Y units.
func ImPlotBarsHXY(label string, xs, ys []float64, height float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotBarsHXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.double(height),
		C.int(offset),
	)
}

// Plots a standard 2D line plot.
func ImPlotLine(label string, values []float64, xscale, x0 float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelFin := wrapString(label)
	defer labelFin()

	C.iggImPlotLine(
		labelArg,
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		C.double(xscale),
		C.double(x0),
		C.int(offset),
	)
}

// Plots a standard 2D line plot.
func ImPlotLineXY(label string, xs, ys []float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 || (len(xs) != len(ys)) {
		return
	}

	labelArg, labelFin := wrapString(label)
	defer labelFin()

	C.iggImPlotLineXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.int(offset),
	)
}

// Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle.
func ImPlotScatter(label string, values []float64, xscale, x0 float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotScatter(
		labelArg,
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		C.double(xscale),
		C.double(x0),
		C.int(offset),
	)
}

// Plots a standard 2D scatter plot. Default marker is ImPlotMarker_Circle.
func ImPlotScatterXY(label string, xs, ys []float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotScatterXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.int(offset),
	)
}

// Plots a a stairstep graph. The y value is continued constantly from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i].
func ImPlotStairs(label string, values []float64, xscale, x0 float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotStairs(
		labelArg,
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		C.double(xscale),
		C.double(x0),
		C.int(offset),
	)
}

// Plots a a stairstep graph. The y value is continued constantly from every x position, i.e. the interval [x[i], x[i+1]) has the value y[i].
func ImPlotStairsXY(label string, xs, ys []float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotStairsXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.int(offset),
	)
}

// Plots vertical error bar. The label_id should be the same as the label_id of the associated line or bar plot.
func ImPlotErrorBars(label string, xs, ys, err []float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 || len(err) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotErrorBars(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs)),
		(*C.double)(unsafe.Pointer(&ys)),
		(*C.double)(unsafe.Pointer(&err)),
		C.int(len(xs)),
		C.int(offset),
	)
}

// Plots horizontal error bars. The label_id should be the same as the label_id of the associated line or bar plot.
func ImPlotErrorBarsH(label string, xs, ys, err []float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 || len(err) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotErrorBarsH(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs)),
		(*C.double)(unsafe.Pointer(&ys)),
		(*C.double)(unsafe.Pointer(&err)),
		C.int(len(xs)),
		C.int(offset),
	)
}

/// Plots vertical stems.
func ImPlotStems(label string, values []float64, yRef, xscale, x0 float64, offset int) {
	if len(values) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotStems(
		labelArg,
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		C.double(yRef),
		C.double(xscale),
		C.double(x0),
		C.int(offset),
	)
}

/// Plots vertical stems.
func ImPlotStemsXY(label string, xs, ys []float64, yRef float64, offset int) {
	if len(xs) == 0 || len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotStemsXY(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(xs)),
		C.double(yRef),
		C.int(offset),
	)
}

/// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes).
func ImPlotVLines(label string, xs []float64, offset int) {
	if len(xs) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotVLines(
		labelArg,
		(*C.double)(unsafe.Pointer(&xs[0])),
		C.int(len(xs)),
		C.int(offset),
	)
}

/// Plots infinite vertical or horizontal lines (e.g. for references or asymptotes).
func ImPlotHLines(label string, ys []float64, offset int) {
	if len(ys) == 0 {
		return
	}

	labelArg, labelDeleter := wrapString(label)
	defer labelDeleter()

	C.iggImPlotHLines(
		labelArg,
		(*C.double)(unsafe.Pointer(&ys[0])),
		C.int(len(ys)),
		C.int(offset),
	)
}

// Plots a pie chart. If the sum of values > 1 or normalize is true, each value will be normalized. Center and radius are in plot units. #label_fmt can be set to NULL for no labels.
func ImPlotPieChart(labelIds []string, values []float64, x, y, radius float64, normalize bool, labelFmt string, angle0 float64) {
	if len(labelIds) == 0 || len(values) == 0 {
		return
	}

	labelIdsArg := make([]*C.char, len(labelIds))
	for i, l := range labelIds {
		la, lf := wrapString(l)
		defer lf()
		labelIdsArg[i] = la
	}

	labelFmtArg, labelFmtDeleter := wrapString(labelFmt)
	defer labelFmtDeleter()

	C.iggImPlotPieChart(
		&labelIdsArg[0],
		(*C.double)(unsafe.Pointer(&values[0])),
		C.int(len(values)),
		C.double(x),
		C.double(y),
		C.double(radius),
		castBool(normalize),
		labelFmtArg,
		C.double(angle0),
	)
}

func ImPlotGetPlotPos() Vec2 {
	var pos Vec2
	posArg, _ := pos.wrapped()
	C.iggImPlotGetPlotPos(posArg)
	return pos
}

func ImPlotGetPlotSize() Vec2 {
	var size Vec2
	sizeArg, _ := size.wrapped()
	C.iggImPlotGetPlotSize(sizeArg)
	return size
}

func ImPlotIsPlotHovered() bool {
	return C.iggImPlotIsPlotHovered() != 0
}

func ImPlotIsPlotXAxisHovered() bool {
	return C.iggImPlotIsPlotXAxisHovered() != 0
}

func ImPlotIsPlotYAxisHovered(yAxis int) bool {
	return C.iggImPlotIsPlotYAxisHovered(C.int(yAxis)) != -0
}

func ImPlotUseLocalTime(localtime bool) {
	C.iggImPlotUseLocalTime(castBool(localtime))
}

func ImPlotUseISO8601(iso8601 bool) {
	C.iggImPlotUseISO8601(castBool(iso8601))
}

func ImPlotUse24HourClock(clock24h bool) {
	C.iggImPlotUse24HourClock(castBool(clock24h))
}

func ImPlotSetPlotYAxis(yAxis ImPlotYAxis) {
	C.iggImPlotSetPlotYAxis(C.int(yAxis))
}
