package d2lib

import (
	
	
	
	
	

	
	
	
	
	
	
	
	
	
	
	
	
)

type CompileOptions struct {
	UTF16Pos bool
	// MaxVariableExpansion bounds work added by variable substitutions and the
	// automatic copies they induce. Zero uses the secure compiler default.
	MaxVariableExpansion int64
	// MaxGlobExpansion bounds work performed by glob matching and
	// materialization. Zero uses the secure compiler default. Explicit source
	// fields are not counted as materialization work.
	MaxGlobExpansion int64
	// MaxEdgeExpansion bounds distinct edge-segment and endpoint combinations
	// considered by edge globs. Zero uses the secure compiler default. Explicit
	// edges do not consume this budget.
	MaxEdgeExpansion int64
	// MaxEdgeExpansionWork bounds all endpoint-pair examinations performed by
	// edge globs, including lazy replays. Zero uses the secure compiler default.
	MaxEdgeExpansionWork int64
	// FS is the file system used for resolving imports in the D2 text. Nil
	// disables imports. Callers that accept untrusted input should prefer a
	// filesystem constrained to the intended import root; lib/localfile provides
	// rooted and explicit unrestricted host-filesystem policies.
	FS             fs.FS
	MeasuredTexts  []*d2target.MText
	Ruler          *textmeasure.Ruler
	RouterResolver func(engine string) (d2graph.RouteEdges, error)
	LayoutResolver func(engine string) (d2graph.LayoutGraph, error)

	// LayoutReuse allows scenarios and steps whose layout inputs are unchanged
	// to reuse the geometry of their base board. Callers should enable this only
	// when LayoutResolver and RouterResolver return a stable built-in Dagre or
	// ELK configuration for the duration of Compile. It is disabled by default
	// so custom resolvers retain their existing per-board behavior.
	LayoutReuse bool

	Layout *string

	// FontFamily controls the font family used for all texts that are not the following:
	// - code
	// - latex
	// - pre-measured (web setting)
	// TODO maybe some will want to configure code font too, but that's much lower priority
	FontFamily *d2fonts.FontFamily

	// MonoFontFamily controls the font family used for code/mono texts
	MonoFontFamily *d2fonts.FontFamily

	InputPath string
}

func ( context.Context,  string,  *CompileOptions) (*d2ast.Map, error) {
	if  == nil {
		 = &CompileOptions{}
	}

	,  := d2parser.ParseContext(, .InputPath, strings.NewReader(), &d2parser.ParseOptions{
		UTF16Pos: .UTF16Pos,
	})
	return , 
}

func ( context.Context,  string,  *CompileOptions,  *d2svg.RenderOpts) (*d2target.Diagram, *d2graph.Graph, error) {
	return compileInput(, , , )
}

func compileInput( context.Context,  string,  *CompileOptions,  *d2svg.RenderOpts) (*d2target.Diagram, *d2graph.Graph, error) {
	if  == nil {
		 = &CompileOptions{}
	}
	if  == nil {
		 = &d2svg.RenderOpts{}
	}

	, ,  := d2compiler.Compile(.InputPath, strings.NewReader(), &d2compiler.CompileOptions{
		Context:              ,
		UTF16Pos:             .UTF16Pos,
		MaxVariableExpansion: .MaxVariableExpansion,
		MaxGlobExpansion:     .MaxGlobExpansion,
		MaxEdgeExpansion:     .MaxEdgeExpansion,
		MaxEdgeExpansionWork: .MaxEdgeExpansionWork,
		FS:                   .FS,
	})
	if  != nil {
		return nil, nil, 
	}

	applyConfigs(, , )
	// Disabled and absent isometric modes have the same appearance. Keep both
	// out of existing diagram JSON/hash IDs while returning the effective false
	// value to callers for precedence handling.
	 := .Isometric
	if  != nil && !* {
		 = nil
	}
	applyDefaults(, )
	if *.Sketch && *.Isometric {
		return nil, nil, errors.New("sketch cannot be combined with isometric rendering")
	}
	if  != nil {
		applyGraphData(, .Data)
	}

	,  := compile(, , , )
	if  != nil {
		if  == nil {
			 = &d2target.Config{}
		}
		// These are fields that affect a diagram's appearance, so feed them back
		// into diagram.Config to ensure the hash computed for CSS styling purposes
		// is unique to its appearance
		.ThemeID = .ThemeID
		.DarkThemeID = .DarkThemeID
		.Sketch = .Sketch
		.Isometric = 
		.Config = 
	}
	return , , 
}

func applyGraphData( *d2graph.Graph,  map[string]interface{}) {
	if  == nil {
		return
	}
	.Data = cloneGraphData()
	for ,  := range [][]*d2graph.Graph{.Layers, .Scenarios, .Steps} {
		for ,  := range  {
			(, )
		}
	}
}

func cloneGraphData( map[string]interface{}) map[string]interface{} {
	if  == nil {
		return nil
	}
	 := make(map[string]interface{}, len())
	for ,  := range  {
		[] = cloneGraphDataValue()
	}
	return 
}

func cloneGraphDataValue( interface{}) interface{} {
	switch value := .(type) {
	case map[string]interface{}:
		return cloneGraphData()
	case []interface{}:
		 := make([]interface{}, len())
		for ,  := range  {
			[] = ()
		}
		return 
	default:
		return 
	}
}

func compile( context.Context,  *d2graph.Graph,  *CompileOptions,  *d2svg.RenderOpts) (*d2target.Diagram, error) {
	, ,  := compileBoard(, , , , nil, false)
	return , 
}

func compileBoard( context.Context,  *d2graph.Graph,  *CompileOptions,  *d2svg.RenderOpts,  *layoutGeometry,  bool) (*d2target.Diagram, *layoutGeometry, error) {
	 := .ApplyTheme(*.ThemeID)
	if  != nil {
		return nil, nil, 
	}

	var  *layoutGeometry
	if len(.Objects) > 0 {
		 := .SetDimensions(.MeasuredTexts, .Ruler, .FontFamily, .MonoFontFamily)
		if  != nil {
			return nil, nil, 
		}

		 := .LayoutReuse && ( != nil ||  || len(.Scenarios) > 0 || len(.Steps) > 0)
		var  layoutInputSignature
		 := false
		if  {
			 := ""
			if .Layout != nil {
				 = *.Layout
			}
			, ,  = newLayoutInputSignature(, )
			if  != nil {
				return nil, nil, 
			}
		}
		// A built-in layout may surface context cancellation at the start of
		// every board. Do not let geometry replay bypass that error path.
		 := .Err() == nil &&  && .LayoutReuse && .apply(, )
		if ! {
			,  := getLayout()
			if  != nil {
				return nil, nil, 
			}
			,  := getEdgeRouter()
			if  != nil {
				return nil, nil, 
			}
			 := d2layouts.NestedGraphInfo(.Root)
			 = d2layouts.LayoutNested(, , , , )
			if  != nil {
				return nil, nil, 
			}
		}
		if  && ( || len(.Scenarios) > 0 || len(.Steps) > 0) {
			 = captureLayoutGeometry(, )
		}
	}

	,  := d2exporter.Export(, , .FontFamily, .MonoFontFamily)
	if  != nil {
		return nil, nil, 
	}

	for ,  := range .Layers {
		// Layers are independent boards rather than overlays of their parent, so
		// retain their existing always-layout behavior.
		, ,  := (, , , , nil, false)
		if  != nil {
			return nil, nil, 
		}
		.Layers = append(.Layers, )
	}
	for ,  := range .Scenarios {
		, ,  := (, , , , , false)
		if  != nil {
			return nil, nil, 
		}
		.Scenarios = append(.Scenarios, )
	}
	 := 
	for ,  := range .Steps {
		, ,  := (, , , , , +1 < len(.Steps))
		if  != nil {
			return nil, nil, 
		}
		.Steps = append(.Steps, )
		 = 
	}
	return , , nil
}

func getLayout( *CompileOptions) (d2graph.LayoutGraph, error) {
	if .Layout == nil {
		return nil, errors.New("no available layout")
	}
	if .LayoutResolver == nil {
		return nil, fmt.Errorf("no layout resolver configured for layout engine %q", *.Layout)
	}
	return .LayoutResolver(*.Layout)
}

func getEdgeRouter( *CompileOptions) (d2graph.RouteEdges, error) {
	if .Layout != nil && .RouterResolver != nil {
		,  := .RouterResolver(*.Layout)
		if  != nil {
			return nil, 
		}
		if  != nil {
			return , nil
		}
	}
	return d2layouts.DefaultRouter, nil
}

// applyConfigs applies the configs read from D2 and applies it to passed in opts
// It will only write to opt fields that are nil, as passed-in opts have precedence
func applyConfigs( *d2target.Config,  *CompileOptions,  *d2svg.RenderOpts) {
	if  == nil {
		return
	}

	if .Layout == nil {
		.Layout = .LayoutEngine
	}

	if .ThemeID == nil {
		.ThemeID = .ThemeID
	}
	if .DarkThemeID == nil {
		.DarkThemeID = .DarkThemeID
	}
	if .Sketch == nil {
		.Sketch = .Sketch
	}
	if .Isometric == nil {
		.Isometric = .Isometric
	}
	if .Pad == nil {
		.Pad = .Pad
	}
	if .Center == nil {
		.Center = .Center
	}
	if .ThemeOverrides == nil {
		.ThemeOverrides = .ThemeOverrides
	}
	if .DarkThemeOverrides == nil {
		.DarkThemeOverrides = .DarkThemeOverrides
	}
}

func applyDefaults( *CompileOptions,  *d2svg.RenderOpts) {
	if .Layout == nil {
		.Layout = go2.Pointer("dagre")
	}

	if .ThemeID == nil {
		.ThemeID = &d2themescatalog.NeutralDefault.ID
	}
	if .Sketch == nil {
		.Sketch = go2.Pointer(false)
	}
	if .Isometric == nil {
		.Isometric = go2.Pointer(false)
	}
	if *.Sketch && .FontFamily == nil {
		.FontFamily = go2.Pointer(d2fonts.HandDrawn)
	}
	if .Pad == nil {
		.Pad = go2.Pointer(int64(d2svg.DEFAULT_PADDING))
	}
	if .Center == nil {
		.Center = go2.Pointer(false)
	}
}