//go:build !nodagre

package d2plugin

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"

	"github.com/d2lang/d2/d2graph"
	"github.com/d2lang/d2/d2layouts/d2dagrelayout"
	"github.com/d2lang/util-go/xmain"
)

var DagrePlugin = dagrePlugin{}

func init() {
	plugins = append(plugins, &DagrePlugin)
}

type dagrePlugin struct {
	mu   sync.Mutex
	opts *d2dagrelayout.ConfigurableOpts
}

func (p *dagrePlugin) Flags(context.Context) ([]PluginSpecificFlag, error) {
	return []PluginSpecificFlag{
		{
			Name:    "dagre-nodesep",
			Type:    "int64",
			Default: int64(d2dagrelayout.DefaultOpts.NodeSep),
			Usage:   "number of pixels that separate nodes horizontally.",
			Tag:     "nodesep",
		},
		{
			Name:    "dagre-edgesep",
			Type:    "int64",
			Default: int64(d2dagrelayout.DefaultOpts.EdgeSep),
			Usage:   "number of pixels that separate edges horizontally.",
			Tag:     "edgesep",
		},
	}, nil
}

func (p *dagrePlugin) HydrateOpts(opts []byte) error {
	p.mu.Lock()
	defer p.mu.Unlock()
	if opts != nil {
		var dagreOpts d2dagrelayout.ConfigurableOpts
		err := json.Unmarshal(opts, &dagreOpts)
		if err != nil {
			return xmain.UsageErrorf("non-dagre layout options given for dagre")
		}

		p.opts = &dagreOpts
	}
	return nil
}

func (p *dagrePlugin) Info(ctx context.Context) (*PluginInfo, error) {
	p.mu.Lock()
	defer p.mu.Unlock()
	opts := xmain.NewOpts(nil, nil)
	flags, err := p.Flags(ctx)
	if err != nil {
		return nil, err
	}
	for _, f := range flags {
		f.AddToOpts(opts)
	}

	return &PluginInfo{
		Name:      "dagre",
		Type:      "bundled",
		Features:  []PluginFeature{},
		ShortHelp: "The directed graph layout library Dagre",
		LongHelp: fmt.Sprintf(`dagre is a directed graph layout algorithm implemented natively in Go by Dagro.
See https://d2lang.com/tour/dagre for more.

Dagro implements the Dagre 3.1.1 layout surface used by D2: https://github.com/d2lang/dagro.

Flags correspond to ones found at https://github.com/dagrejs/dagre/wiki.

Flags:
%s
`, opts.Defaults()),
	}, nil
}

func (p *dagrePlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
	p.mu.Lock()
	optsCopy := *p.opts
	p.mu.Unlock()
	return d2dagrelayout.Layout(ctx, g, &optsCopy)
}
