func (f binaryRecordFormatV0) readDecimal(r *rw.ReadSeeker) interface{} {
	scale := int(r.ReadInt())
	value := big.NewInt(0).SetBytes(r.ReadBytes())
	return orient.Decimal{
		Scale: scale,
		Value: value,
	}
}
func (f binaryRecordFormatV0) readLinkCollection(r *rw.ReadSeeker) []orient.OIdentifiable {
	n := int(r.ReadVarint())
	out := make([]orient.OIdentifiable, n)
	for i := range out {
		if id := f.readOptimizedLink(r); id != nilRID {
			out[i] = id
		}
	}
	return out
}
func (f binaryRecordFormatV0) readLinkMap(r *rw.ReadSeeker, doc *orient.Document) (interface{}, error) {
	size := int(r.ReadVarint())
	if size == 0 {
		return nil, r.Err()
	}
	type entry struct {
		Key interface{}
		Val orient.OIdentifiable
	}
	var (
		result   = make([]entry, 0, size) // TODO: can't return just this slice, need some public implementation
		keyTypes = make(map[orient.OType]bool, 2)
	)
	for i := 0; i < size; i++ {
		keyType := f.readOType(r)
		keyTypes[keyType] = true
		key, err := f.readSingleValue(r, keyType, doc)
		if err != nil {
			return nil, err
		}
		value := f.readOptimizedLink(r)
		if value == nilRID {
			result = append(result, entry{Key: key, Val: nil})
		} else {
			result = append(result, entry{Key: key, Val: value})
		}
	}
	if len(keyTypes) == 1 { // TODO: reflect-based converter
		tp := orient.UNKNOWN
		for k, _ := range keyTypes {
			tp = k
			break
		}
		switch tp {
		case orient.UNKNOWN:
			return result, nil
		case orient.STRING:
			mp := make(map[string]orient.OIdentifiable, len(result))
			for _, kv := range result {
				mp[kv.Key.(string)] = kv.Val
			}
			return mp, nil
		default:
			panic(fmt.Errorf("don't how to make map of type %v", tp))
		}
	} else {
		panic(fmt.Errorf("map with different key type: %+v", keyTypes))
	}
	//return result
}
func (f binaryRecordFormatV0) readEmbeddedCollection(r *rw.ReadSeeker, doc *orient.Document) ([]interface{}, error) {
	n := int(r.ReadVarint())
	if vtype := f.readOType(r); vtype == orient.ANY {
		out := make([]interface{}, n) // TODO: convert to determined slice type with reflect?
		var err error
		for i := range out {
			if itemType := f.readOType(r); itemType != orient.ANY {
				out[i], err = f.readSingleValue(r, itemType, doc)
				if err != nil {
					return nil, err
				}
			}
		}
		return out, nil
	}
	// TODO: @orient: manage case where type is known
	return nil, r.Err()
}
func (f binaryRecordFormatV0) readSingleValue(r *rw.ReadSeeker, valueType orient.OType, doc *orient.Document) (value interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			if ic, ok := r.(*runtime.TypeAssertionError); ok {
				err = fmt.Errorf("writeSingleValue(%v): %v", valueType, ic)
			} else {
				panic(r)
			}
		}
	}()
	switch valueType {
	case orient.INTEGER:
		value = int32(r.ReadVarint())
	case orient.LONG:
		value = int64(r.ReadVarint())
	case orient.SHORT:
		value = int16(r.ReadVarint())
	case orient.STRING:
		value = f.readString(r)
	case orient.DOUBLE:
		value = r.ReadDouble()
	case orient.FLOAT:
		value = r.ReadFloat()
	case orient.BYTE:
		value = f.readByte(r)
	case orient.BOOLEAN:
		value = f.readByte(r) == 1
	case orient.DATETIME:
		longTime := r.ReadVarint()
		value = time.Unix(longTime/1000, (longTime%1000)*1e6)
	case orient.DATE:
		//	long savedTime = OVarIntSerializer.readAsLong(bytes) * MILLISEC_PER_DAY;
		//	int offset = ODateHelper.getDatabaseTimeZone().getOffset(savedTime);
		//	value = new Date(savedTime - offset);
		savedTime := r.ReadVarint() * millisecPerDay
		t := time.Unix(savedTime/1000, (savedTime%1000)*1e6) //.UTC().Local()
		//		_, offset := t.Zone()
		//		value = t.Add(-time.Duration(offset) * time.Second)
		value = t
	case orient.EMBEDDED:
		doc2 := orient.NewEmptyDocument()
		if err = f.Deserialize(doc2, r); err != nil {
			return nil, err
		}
		value = doc2
	//	if (((ODocument) value).containsField(ODocumentSerializable.CLASS_NAME)) {
	//	String className = ((ODocument) value).field(ODocumentSerializable.CLASS_NAME);
	//	try {
	//	Class<?> clazz = Class.forName(className);
	//	ODocumentSerializable newValue = (ODocumentSerializable) clazz.newInstance();
	//	newValue.fromDocument((ODocument) value);
	//	value = newValue;
	//	} catch (Exception e) {
	//	throw new RuntimeException(e);
	//	}
	//	} else
	//	ODocumentInternal.addOwner((ODocument) value, document);
	case orient.EMBEDDEDSET, orient.EMBEDDEDLIST:
		value, err = f.readEmbeddedCollection(r, doc)
	case orient.LINKSET, orient.LINKLIST:
		value = f.readLinkCollection(r)
	case orient.BINARY:
		value = f.readBinary(r)
	case orient.LINK:
		value = f.readOptimizedLink(r)
	case orient.LINKMAP:
		value, err = f.readLinkMap(r, doc)
	case orient.EMBEDDEDMAP:
		value, err = f.readEmbeddedMap(r, doc)
	case orient.DECIMAL:
		value = f.readDecimal(r)
	case orient.LINKBAG:
		bag := orient.NewRidBag()
		if err = bag.FromStream(r); err != nil {
			return nil, err
		}
		bag.SetOwner(doc)
		value = bag
	case orient.TRANSIENT:
	case orient.ANY:
	case orient.CUSTOM:
		// TODO: implement via Register global function
		panic("CUSTOM type is not supported for now")
		//	try {
		//	String className = readString(bytes);
		//	Class<?> clazz = Class.forName(className);
		//	OSerializableStream stream = (OSerializableStream) clazz.newInstance();
		//	stream.fromStream(readBinary(bytes));
		//	if (stream instanceof OSerializableWrapper)
		//	value = ((OSerializableWrapper) stream).getSerializable();
		//	else
		//	value = stream;
		//	} catch (Exception e) {
		//	throw new RuntimeException(e);
		//	}
	}
	if err == nil {
		err = r.Err()
	}
	return
}
func (f binaryRecordFormatV0) readEmbeddedMap(r *rw.ReadSeeker, doc *orient.Document) (interface{}, error) {
	size := int(r.ReadVarint())
	if size == 0 {
		return nil, r.Err()
	}
	last := int64(0)
	type entry struct {
		Key interface{}
		Val interface{}
	}
	var (
		result     = make([]entry, 0, size) // TODO: can't return just this slice, need some public implementation
		keyTypes   = make(map[orient.OType]bool, 1)
		valueTypes = make(map[orient.OType]bool, 2)
	)
	for i := 0; i < size; i++ {
		keyType := f.readOType(r)
		key, err := f.readSingleValue(r, keyType, doc)
		if err != nil {
			return nil, err
		}
		valuePos := f.readInteger(r)
		valueType := f.readOType(r)
		keyTypes[keyType] = true
		valueTypes[valueType] = true
		if valuePos != 0 {
			headerCursor, _ := r.Seek(0, 1)
			r.Seek(int64(valuePos), 0)
			value, err := f.readSingleValue(r, valueType, doc)
			if err != nil {
				return nil, err
			}
			if off, _ := r.Seek(0, 1); off > last {
				last = off
			}
			r.Seek(headerCursor, 0)
			result = append(result, entry{Key: key, Val: value})
		} else {
			result = append(result, entry{Key: key, Val: nil})
		}
	}
	if off, _ := r.Seek(0, 1); last > off {
		r.Seek(last, 0)
	}
	if err := r.Err(); err != nil {
		return nil, err
	}
	//fmt.Printf("embedded map: types: %+v, vals: %+v\n", keyTypes, valueTypes)
	var (
		keyType reflect.Type
		valType reflect.Type = orient.UNKNOWN.ReflectType()
	)
	if len(keyTypes) == 1 {
		for k, _ := range keyTypes {
			if k == orient.UNKNOWN {
				return result, nil
			}
			keyType = k.ReflectType()
			break
		}
	} else {
		panic(fmt.Errorf("map with different key type: %+v", keyTypes))
	}
	if len(valueTypes) == 1 {
		for v, _ := range valueTypes {
			valType = v.ReflectType()
			break
		}
	}
	rv := reflect.MakeMap(reflect.MapOf(keyType, valType))
	for _, kv := range result {
		rv.SetMapIndex(reflect.ValueOf(kv.Key), reflect.ValueOf(kv.Val))
	}
	return rv.Interface(), nil
}
func (f binaryRecordFormatV0) readOptimizedLink(r *rw.ReadSeeker) orient.RID {
	return orient.RID{ClusterID: int16(r.ReadVarint()), ClusterPos: int64(r.ReadVarint())}
}
func (f binaryRecordFormatV0) readInteger(r *rw.ReadSeeker) int32 {
	return r.ReadInt()
}
func (f binaryRecordFormatV0) readString(r *rw.ReadSeeker) string {
	return r.ReadStringVarint()
}
func (f binaryRecordFormatV0) readBinary(r *rw.ReadSeeker) []byte {
	return r.ReadBytesVarint()
}
func (f binaryRecordFormatV0) readByte(r *rw.ReadSeeker) byte {
	return r.ReadByte()
}
func (f binaryRecordFormatV0) Deserialize(doc *orient.Document, r *rw.ReadSeeker) error {

	className := f.readString(r)
	if err := r.Err(); err != nil {
		return err
	}
	if len(className) != 0 {
		doc.FillClassNameIfNeeded(className)
	}

	var (
		fieldName string
		valuePos  int
		valueType orient.OType
		last      int64
	)
	for {
		//var prop core.OGlobalProperty
		leng := int(r.ReadVarint())
		if err := r.Err(); err != nil {
			return err
		}
		if leng == 0 {
			// SCAN COMPLETED
			break
		} else if leng > 0 {
			// PARSE FIELD NAME
			fieldNameBytes := make([]byte, leng)
			r.ReadRawBytes(fieldNameBytes)
			fieldName = string(fieldNameBytes)
			valuePos = int(f.readInteger(r))
			valueType = f.readOType(r)
		} else {
			// LOAD GLOBAL PROPERTY BY ID
			prop := f.getGlobalProperty(doc, leng)
			fieldName = prop.Name
			valuePos = int(f.readInteger(r))
			if prop.Type != orient.ANY {
				valueType = prop.Type
			} else {
				valueType = f.readOType(r)
			}
		}

		if doc.RawContainsField(fieldName) {
			continue
		}
		if valuePos != 0 {
			headerCursor, _ := r.Seek(0, 1)
			r.Seek(int64(valuePos), 0)
			value, err := f.readSingleValue(r, valueType, doc)
			if err != nil {
				return err
			}
			if cur, _ := r.Seek(0, 1); cur > last {
				last = cur
			}
			r.Seek(headerCursor, 0)
			doc.RawSetField(fieldName, value, valueType)
		} else {
			doc.RawSetField(fieldName, nil, orient.UNKNOWN)
		}
	}

	//doc.ClearSource()

	if cur, _ := r.Seek(0, 1); last > cur {
		r.Seek(last, 0)
	}
	return r.Err()
}