package d2graph

import (
	
	
	

	
	
)

type SerializedGraph struct {
	Root      SerializedObject       `json:"root"`
	Edges     []SerializedEdge       `json:"edges"`
	Objects   []SerializedObject     `json:"objects"`
	RootLevel int                    `json:"rootLevel"`
	Data      map[string]interface{} `json:"data,omitempty"`
}

type SerializedObject map[string]interface{}

type SerializedEdge map[string]interface{}

func ( []byte,  *Graph) error {
	var  *SerializedGraph
	 := json.Unmarshal(, &)
	if  != nil {
		return 
	}

	.Data = .Data
	var  Object
	Convert(.Root, &)
	.Root = &
	.Graph = 
	.RootLevel = .RootLevel

	 := make(map[string]*Object)
	[""] = .Root
	var  []*Object
	for ,  := range .Objects {
		var  Object
		if  := Convert(, &);  != nil {
			return 
		}
		.Graph = 
		 = append(, &)
		[["AbsID"].(string)] = &
	}

	for ,  := range append(.Objects, .Root) {
		if ["ChildrenArray"] != nil {
			 := make(map[string]*Object)
			var  []*Object

			for ,  := range ["ChildrenArray"].([]interface{}) {
				 := [.(string)]
				 = append(, )
				[strings.ToLower(.ID)] = 

				.Parent = [["AbsID"].(string)]
			}

			[["AbsID"].(string)].Children = 
			[["AbsID"].(string)].ChildrenArray = 
		}
	}

	var  []*Edge
	for ,  := range .Edges {
		var  Edge
		if  := Convert(, &);  != nil {
			return 
		}

		if ["Src"] != nil {
			.Src = [["Src"].(string)]
		}
		if ["Dst"] != nil {
			.Dst = [["Dst"].(string)]
		}
		 = append(, &)
	}

	.Objects = 
	.Edges = 

	return nil
}

func ( *Graph) ([]byte, error) {
	 := SerializedGraph{}

	,  := toSerializedObject(.Root)
	if  != nil {
		return nil, 
	}
	.Root = 
	.RootLevel = .RootLevel
	.Data = .Data

	var  []SerializedObject
	for ,  := range .Objects {
		,  := toSerializedObject()
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	.Objects = 

	var  []SerializedEdge
	for ,  := range .Edges {
		,  := ToSerializedEdge()
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	.Edges = 

	return json.Marshal()
}

func toSerializedObject( *Object) (SerializedObject, error) {
	var  SerializedObject
	if  := Convert(, &);  != nil {
		return nil, 
	}

	["AbsID"] = .AbsID()

	if len(.ChildrenArray) > 0 {
		var  []string
		for ,  := range .ChildrenArray {
			 = append(, .AbsID())
		}
		["ChildrenArray"] = 
	}

	return , nil
}

func ( *Edge) (SerializedEdge, error) {
	var  SerializedEdge
	if  := Convert(, &);  != nil {
		return nil, 
	}

	if .Src != nil {
		["Src"] = go2.Pointer(.Src.AbsID())
	}
	if .Dst != nil {
		["Dst"] = go2.Pointer(.Dst.AbsID())
	}

	return , nil
}

func [,  any]( ,  *) error {
	,  := json.Marshal()
	if  != nil {
		return 
	}
	if  := json.Unmarshal(, );  != nil {
		return 
	}
	return nil
}

func (,  *Graph) error {
	if len(.Objects) != len(.Objects) {
		return fmt.Errorf("object count differs: g=%d, other=%d", len(.Objects), len(.Objects))
	}

	if len(.Edges) != len(.Edges) {
		return fmt.Errorf("edge count differs: g=%d, other=%d", len(.Edges), len(.Edges))
	}

	if  := CompareSerializedObject(.Root, .Root);  != nil {
		return fmt.Errorf("root differs: %v", )
	}

	for  := 0;  < len(.Objects); ++ {
		if  := CompareSerializedObject(.Objects[], .Objects[]);  != nil {
			return fmt.Errorf(
				"objects differ at %d [g=%s, other=%s]: %v",
				,
				.Objects[].ID,
				.Objects[].ID,
				,
			)
		}
	}

	for  := 0;  < len(.Edges); ++ {
		if  := CompareSerializedEdge(.Edges[], .Edges[]);  != nil {
			return fmt.Errorf(
				"edges differ at %d [g=%s, other=%s]: %v",
				,
				.Edges[].AbsID(),
				.Edges[].AbsID(),
				,
			)
		}
	}

	return nil
}

func (,  *Object) error {
	if  != nil &&  == nil {
		return fmt.Errorf("other is nil")
	} else if  == nil &&  != nil {
		return fmt.Errorf("obj is nil")
	} else if  == nil {
		// both are nil
		return nil
	}

	if .ID != .ID {
		return fmt.Errorf("ids differ: obj=%s, other=%s", .ID, .ID)
	}

	if .AbsID() != .AbsID() {
		return fmt.Errorf("absolute ids differ: obj=%s, other=%s", .AbsID(), .AbsID())
	}

	if .Box != nil && .Box == nil {
		return fmt.Errorf("other should have a box")
	} else if .Box == nil && .Box != nil {
		return fmt.Errorf("other should not have a box")
	} else if .Box != nil {
		if .Width != .Width {
			return fmt.Errorf("widths differ: obj=%f, other=%f", .Width, .Width)
		}

		if .Height != .Height {
			return fmt.Errorf("heights differ: obj=%f, other=%f", .Height, .Height)
		}
	}

	if .Parent != nil && .Parent == nil {
		return fmt.Errorf("other should have a parent")
	} else if .Parent == nil && .Parent != nil {
		return fmt.Errorf("other should not have a parent")
	} else if .Parent != nil && .Parent.ID != .Parent.ID {
		return fmt.Errorf("parent differs: obj=%s, other=%s", .Parent.ID, .Parent.ID)
	}

	if len(.Children) != len(.Children) {
		return fmt.Errorf("children count differs: obj=%d, other=%d", len(.Children), len(.Children))
	}

	for ,  := range .Children {
		if ,  := .Children[];  {
			if  := (, );  != nil {
				return fmt.Errorf("children differ at key %s: %v", , )
			}
		} else {
			return fmt.Errorf("child %s does not exist in other", )
		}
	}

	if len(.ChildrenArray) != len(.ChildrenArray) {
		return fmt.Errorf("childrenArray count differs: obj=%d, other=%d", len(.ChildrenArray), len(.ChildrenArray))
	}

	for  := 0;  < len(.ChildrenArray); ++ {
		if  := (.ChildrenArray[], .ChildrenArray[]);  != nil {
			return fmt.Errorf("childrenArray differs at %d: %v", , )
		}
	}

	if d2target.IsShape(.Shape.Value) != d2target.IsShape(.Shape.Value) {
		return fmt.Errorf(
			"shapes differ: obj=%s, other=%s",
			.Shape.Value,
			.Shape.Value,
		)
	}

	if .Icon == nil && .Icon != nil {
		return fmt.Errorf("other does not have an icon")
	} else if .Icon != nil && .Icon == nil {
		return fmt.Errorf("obj does not have an icon")
	}

	if .Direction.Value != .Direction.Value {
		return fmt.Errorf(
			"directions differ: obj=%s, other=%s",
			.Direction.Value,
			.Direction.Value,
		)
	}

	if .Label.Value != .Label.Value {
		return fmt.Errorf(
			"labels differ: obj=%s, other=%s",
			.Label.Value,
			.Label.Value,
		)
	}

	if .NearKey != nil {
		if .NearKey == nil {
			return fmt.Errorf("other does not have near")
		}
		 := strings.Join(Key(.NearKey), ".")
		 := strings.Join(Key(.NearKey), ".")
		if  !=  {
			return fmt.Errorf(
				"near differs: obj=%s, other=%s",
				,
				,
			)
		}
	} else if .NearKey != nil {
		return fmt.Errorf("other should not have near")
	}

	if .LabelDimensions.Width != .LabelDimensions.Width {
		return fmt.Errorf(
			"label width differs: obj=%d, other=%d",
			.LabelDimensions.Width,
			.LabelDimensions.Width,
		)
	}

	if .LabelDimensions.Height != .LabelDimensions.Height {
		return fmt.Errorf(
			"label height differs: obj=%d, other=%d",
			.LabelDimensions.Height,
			.LabelDimensions.Height,
		)
	}

	if .SQLTable == nil && .SQLTable != nil {
		return fmt.Errorf("other is not a sql table")
	} else if .SQLTable != nil && .SQLTable == nil {
		return fmt.Errorf("obj is not a sql table")
	}

	if .SQLTable != nil {
		if len(.SQLTable.Columns) != len(.SQLTable.Columns) {
			return fmt.Errorf(
				"table columns count differ: obj=%d, other=%d",
				len(.SQLTable.Columns),
				len(.SQLTable.Columns),
			)
		}
	}

	return nil
}

func (,  *Edge) error {
	if .AbsID() != .AbsID() {
		return fmt.Errorf(
			"absolute ids differ: edge=%s, other=%s",
			.AbsID(),
			.AbsID(),
		)
	}

	if .Src.AbsID() != .Src.AbsID() {
		return fmt.Errorf(
			"sources differ: edge=%s, other=%s",
			.Src.AbsID(),
			.Src.AbsID(),
		)
	}

	if .Dst.AbsID() != .Dst.AbsID() {
		return fmt.Errorf(
			"targets differ: edge=%s, other=%s",
			.Dst.AbsID(),
			.Dst.AbsID(),
		)
	}

	if .SrcArrow != .SrcArrow {
		return fmt.Errorf(
			"source arrows differ: edge=%t, other=%t",
			.SrcArrow,
			.SrcArrow,
		)
	}

	if .DstArrow != .DstArrow {
		return fmt.Errorf(
			"target arrows differ: edge=%t, other=%t",
			.DstArrow,
			.DstArrow,
		)
	}

	if .Label.Value != .Label.Value {
		return fmt.Errorf(
			"labels differ: edge=%s, other=%s",
			.Label.Value,
			.Label.Value,
		)
	}

	if .LabelDimensions.Width != .LabelDimensions.Width {
		return fmt.Errorf(
			"label width differs: edge=%d, other=%d",
			.LabelDimensions.Width,
			.LabelDimensions.Width,
		)
	}

	if .LabelDimensions.Height != .LabelDimensions.Height {
		return fmt.Errorf(
			"label height differs: edge=%d, other=%d",
			.LabelDimensions.Height,
			.LabelDimensions.Height,
		)
	}

	if .SrcTableColumnIndex != nil && .SrcTableColumnIndex == nil {
		return fmt.Errorf("other should have src column index")
	} else if .SrcTableColumnIndex != nil && .SrcTableColumnIndex == nil {
		return fmt.Errorf("other should not have src column index")
	} else if .SrcTableColumnIndex != nil {
		 := *.SrcTableColumnIndex
		 := *.SrcTableColumnIndex
		if  !=  {
			return fmt.Errorf("src column differs: edge=%d, other=%d", , )
		}
	}

	if .DstTableColumnIndex != nil && .DstTableColumnIndex == nil {
		return fmt.Errorf("other should have dst column index")
	} else if .DstTableColumnIndex != nil && .DstTableColumnIndex == nil {
		return fmt.Errorf("other should not have dst column index")
	} else if .DstTableColumnIndex != nil {
		 := *.DstTableColumnIndex
		 := *.DstTableColumnIndex
		if  !=  {
			return fmt.Errorf("dst column differs: edge=%d, other=%d", , )
		}
	}
	return nil
}