// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package keys

import (
	"sync"
	"time"
)

type decoder struct {
	r *reader
}

var decoders = sync.Pool{
	New: func() interface{} {
		return &decoder{
			r: newReader(),
		}
	},
}

// decode decodes an encoded string using the unicode collation algorithm.
func decode(data []byte, items ...interface{}) {
	dec := newDecoder(data)
	dec.Decode(items...)
	dec.Reset()
	return
}

func newDecoder(b []byte) *decoder {
	d := decoders.Get().(*decoder)
	d.r.r.ResetBytes(b)
	return d
}

func (d *decoder) Reset() {
	decoders.Put(d)
}

func (d *decoder) Decode(items ...interface{}) {

	for _, item := range items {

		switch value := item.(type) {

		case *time.Time:
			*value = d.r.readTime()

		case *bool:
			*value = d.r.readBool()

		case *[]byte:
			*value = d.r.readBytes()

		case *string:
			*value = d.r.readString()

		case *int:
			*value = int(d.r.readFloat())

		case *int8:
			*value = int8(d.r.readFloat())

		case *int16:
			*value = int16(d.r.readFloat())

		case *int32:
			*value = int32(d.r.readFloat())

		case *int64:
			*value = int64(d.r.readFloat())

		case *uint:
			*value = uint(d.r.readFloat())

		case *uint8:
			*value = uint8(d.r.readFloat())

		case *uint16:
			*value = uint16(d.r.readFloat())

		case *uint32:
			*value = uint32(d.r.readFloat())

		case *uint64:
			*value = uint64(d.r.readFloat())

		case *float32:
			*value = float32(d.r.readFloat())

		case *float64:
			*value = float64(d.r.readFloat())

		case *[]time.Time:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, d.r.readTime())
				}
			}

		case *[]bool:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, d.r.readBool())
				}
			}

		case *[]string:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, d.r.readString())
				}
			}

		case *[]int:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, int(d.r.readFloat()))
				}
			}

		case *[]int8:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, int8(d.r.readFloat()))
				}
			}

		case *[]int16:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, int16(d.r.readFloat()))
				}
			}

		case *[]int32:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, int32(d.r.readFloat()))
				}
			}

		case *[]int64:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, int64(d.r.readFloat()))
				}
			}

		case *[]uint:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, uint(d.r.readFloat()))
				}
			}

		case *[]uint16:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, uint16(d.r.readFloat()))
				}
			}

		case *[]uint32:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, uint32(d.r.readFloat()))
				}
			}

		case *[]uint64:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, uint64(d.r.readFloat()))
				}
			}

		case *[]float32:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, float32(d.r.readFloat()))
				}
			}

		case *[]float64:
			if d.r.readNext(bARR) {
				for !d.r.readNext(bEND) {
					*value = append(*value, float64(d.r.readFloat()))
				}
			}

		case *[]interface{}:
			*value = d.r.readArray()

		case *interface{}:

			switch d.r.lookNext() {
			default:
				*value = d.r.readAny()
			case bNIL:
				*value = d.r.readNull()
			case bVAL:
				*value = d.r.readBool()
			case bTME:
				*value = d.r.readTime()
			case bNEG, bPOS:
				*value = d.r.readFloat()
			case bSTR, bPRE, bSUF:
				*value = d.r.readString()
			case bARR:
				*value = d.r.readArray()

			}

		}

	}

}