/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package diff

import (
	"reflect"
	"unsafe"
)

var isExportFlag uintptr = (1 << 5) | (1 << 6)

func (d *Differ) diffPtr(path []string, a, b reflect.Value, parent interface{}) error {
	if a.Kind() != b.Kind() {
		if a.Kind() == reflect.Invalid {
			if !b.IsNil() {
				return d.diff(path, reflect.ValueOf(nil), reflect.Indirect(b), parent)
			}

			d.cl.Add(CREATE, path, nil, exportInterface(b), parent)
			return nil
		}

		if b.Kind() == reflect.Invalid {
			if !a.IsNil() {
				return d.diff(path, reflect.Indirect(a), reflect.ValueOf(nil), parent)
			}

			d.cl.Add(DELETE, path, exportInterface(a), nil, parent)
			return nil
		}

		return ErrTypeMismatch
	}

	if a.IsNil() && b.IsNil() {
		return nil
	}

	if a.IsNil() {
		d.cl.Add(UPDATE, path, nil, exportInterface(b), parent)
		return nil
	}

	if b.IsNil() {
		d.cl.Add(UPDATE, path, exportInterface(a), nil, parent)
		return nil
	}

	return d.diff(path, reflect.Indirect(a), reflect.Indirect(b), parent)
}

func exportInterface(v reflect.Value) interface{} {
	if !v.CanInterface() {
		flagTmp := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 2*unsafe.Sizeof(uintptr(0))))
		*flagTmp = (*flagTmp) & (^isExportFlag)
	}
	return v.Interface()
}
