package jsonmerge

import (
	
	
	
	
	
	
)

// Merger describes result of merge operation and provides
// configuration.
type Merger struct {
	// Errors is slice of non-critical errors of merge operations
	Errors []error
	// Replaced is describe replacements
	// Key is path in document like
	//   "prop1.prop2.prop3" for object properties or
	//   "arr1.1.prop" for arrays
	// Value is value of replacemet
	Replaced map[string]interface{}
	// CopyNonexistent enables setting fields into the result
	// which only exist in the patch.
	CopyNonexistent bool
}

func ( *Merger) ( []string,  map[string]interface{},  string,  interface{}) interface{} {
	,  := []

	if ! {
		return 
	}

	,  := .(map[string]interface{})

	 = append(, )
	 := strings.Join(, ".")

	if ,  := .(map[string]interface{});  {
		if ! {
			 := fmt.Errorf("patch value must be object for key \"%v\"", )
			.Errors = append(.Errors, )
			return 
		}

		return .mergeObjects(, , )
	}

	if ,  := .([]interface{});  &&  {
		return .mergeObjects(, , )
	}

	if !reflect.DeepEqual(, ) {
		.Replaced[] = 
	}

	return 
}

func ( *Merger) (,  interface{},  []string) interface{} {
	if ,  := .(map[string]interface{});  {
		if ,  := .([]interface{});  {
			 := make([]interface{}, len())

			for ,  := range  {
				[] = .mergeValue(, , strconv.Itoa(), )
			}

			return 
		} else if ,  := .(map[string]interface{});  {
			 := make(map[string]interface{})

			for ,  := range  {
				[] = .mergeValue(, , , )
			}
			if .CopyNonexistent {
				for ,  := range  {
					if ,  := []; ! {
						[] = 
					}
				}
			}

			return 
		}
	}

	return 
}

// Merge merges patch document to data document
//
// Returning merged document. Result of merge operation can be
// obtained from the Merger. Result information is discarded before
// merging.
func ( *Merger) (,  interface{}) interface{} {
	.Replaced = make(map[string]interface{})
	.Errors = make([]error, 0)
	return .mergeObjects(, , nil)
}

// MergeBytesIndent merges patch document buffer to data document buffer
//
// Use prefix and indent for set indentation like in json.MarshalIndent
//
// Returning merged document buffer and error if any.
func ( *Merger) (,  []byte, ,  string) ( []byte,  error) {
	var , ,  interface{}

	 = unmarshalJSON(, &)
	if  != nil {
		 = fmt.Errorf("error in data JSON: %v", )
		return
	}

	 = unmarshalJSON(, &)
	if  != nil {
		 = fmt.Errorf("error in patch JSON: %v", )
		return
	}

	 = .Merge(, )

	,  = json.MarshalIndent(, , )
	if  != nil {
		 = fmt.Errorf("error writing merged JSON: %v", )
	}

	return
}

// MergeBytes merges patch document buffer to data document buffer
//
// Returning merged document buffer, merge info and
// error if any
func ( *Merger) (,  []byte) ( []byte,  error) {
	var , ,  interface{}

	 = unmarshalJSON(, &)
	if  != nil {
		 = fmt.Errorf("error in data JSON: %v", )
		return
	}

	 = unmarshalJSON(, &)
	if  != nil {
		 = fmt.Errorf("error in patch JSON: %v", )
		return
	}

	 = .Merge(, )

	,  = json.Marshal()
	if  != nil {
		 = fmt.Errorf("error writing merged JSON: %v", )
	}

	return
}

func ( []byte,  interface{}) error {
	 := json.NewDecoder(bytes.NewReader())
	.UseNumber()

	return .Decode()
}