/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 util

import (
	"github.com/go-chassis/gohessian"
	"reflect"

	"fmt"
	"github.com/go-chassis/openlog"
)

//BaseError is a struct
type BaseError struct {
	ErrMsg string
}

func (p *BaseError) Error() string {
	return p.ErrMsg
}

//TypMap is a variable of type map
var TypMap map[string]reflect.Type

func init() {
	TypMap = make(map[string]reflect.Type)
}

const (
	DefaultGrowSize   = 4096
	DefaultBufferSize = 1024
)

//ReadBuffer is a struct
type ReadBuffer struct {
	buffer   []byte
	rdInd    int
	length   int
	capacity int
}

//WriteBuffer is a struct
type WriteBuffer struct {
	buffer   []byte
	wrInd    int
	capacity int
}

//Init is a method to initialize write buffer attributes
func (b *WriteBuffer) Init(size int) {
	if size == 0 {
		size = DefaultBufferSize
	}
	b.buffer = make([]byte, size)
	b.wrInd = 0
	b.capacity = size
}

//Write is a method to write into buffer
func (b *WriteBuffer) Write(p []byte) (n int, err error) {
	result := b.WriteBytes(p)
	if result > 0 {
		return result, nil
	} else {
		return result, &BaseError{"Not enough space to write"}
	}
}

func (b *WriteBuffer) grow(n int) int {
	m := b.capacity
	if b.wrInd+n > m {
		var buf []byte
		buf = make([]byte, m+n)
		copy(buf, b.buffer[0:b.wrInd])
		b.buffer = buf
		b.capacity = m + n
	}

	return n
}

//WriteBytes is a method to write bytes
func (b *WriteBuffer) WriteBytes(src []byte) int {
	size := len(src)
	if b.capacity < (b.wrInd + size) {
		reSize := DefaultGrowSize
		if DefaultGrowSize < size {
			reSize = size
		}
		b.grow(reSize)
	}
	copy(b.buffer[b.wrInd:], src)
	b.wrInd = b.wrInd + size
	return size
}

//WriteIndex is a method to write index
func (b *WriteBuffer) WriteIndex(index int) error {
	if index <= b.capacity {
		b.wrInd = index
	} else {
		return &BaseError{fmt.Sprintf("Index(%d) over the capacity(%d)", index, b.capacity)}
	}
	return nil
}

//WriteByte is a method to write particular byte
func (b *WriteBuffer) WriteByte(src byte) error {
	gh := hessian.NewGoHessian(nil, nil)
	err := gh.ToBytes2(int32(src), b)
	return err
}

//WriteObject is a method to write object
func (b *WriteBuffer) WriteObject(src interface{}) error {
	gh := hessian.NewGoHessian(nil, nil)
	err := gh.ToBytes2(src, b)
	return err
}

//WrittenBytes is a methodto get amount of bytes written
func (b *WriteBuffer) WrittenBytes() int {
	return b.wrInd
}

//GetBuf is a method to get buffer
func (b *WriteBuffer) GetBuf() []byte {
	return b.buffer
}

//GetValidData is a method to get valid data
func (b *WriteBuffer) GetValidData() []byte {
	return b.buffer[0:b.wrInd]
}

//SetBuffer is a method to set buffer data
func (b *ReadBuffer) SetBuffer(src []byte) {
	b.buffer = src
	b.rdInd = 0
	b.capacity = len(src)
	b.length = len(src)
}

//GetBuf is a method to get buffer
func (b *ReadBuffer) GetBuf() []byte {
	return b.buffer
}

//ReadByte is a method to read particular byte from buffer
func (b *ReadBuffer) ReadByte() (byte, error) {
	var tmp interface{}
	tmp, err := b.ReadObject()
	if err != nil {
		openlog.Error(err.Error())
		return byte(0), err
	}
	return byte(tmp.(int32)), nil
}

//ReadBytes is a method to read data from buffer
func (b *ReadBuffer) ReadBytes(len int) []byte {
	start := b.rdInd
	b.rdInd = b.rdInd + len
	return b.buffer[start:b.rdInd]
}

//ReadObject is a method to read buffer and return object
func (b *ReadBuffer) ReadObject() (interface{}, error) {
	gh := hessian.NewGoHessian(TypMap, nil)
	obj, err := gh.ToObject2(b)
	return obj, err
}

//ReadString is a method to read buffer and return as string
func (b *ReadBuffer) ReadString() string {
	gh := hessian.NewGoHessian(nil, nil)
	obj, err := gh.ToObject2(b)
	if obj == nil || err != nil {
		return ""
	}
	return obj.(string)
}

//ReadMap is a method to read buffer and return as a map
func (b *ReadBuffer) ReadMap() (map[string]string, error) {
	gh := hessian.NewGoHessian(nil, nil)
	obj, err := gh.ToObject2(b)
	if err != nil {
		return nil, err
	} else {
		tmpMap := obj.(map[string]interface{})
		var strMap = make(map[string]string)
		for k, v := range tmpMap {
			strMap[k] = v.(string)
		}
		return strMap, nil
	}
}

//Read 实现io.Reader
func (b *ReadBuffer) Read(p []byte) (n int, err error) {
	size := len(p)
	if b.length > (b.rdInd + size) {
		copy(p, b.buffer[b.rdInd:b.rdInd+size])
		b.rdInd = b.rdInd + size
		return size, nil
	} else if b.length == b.rdInd {
		return 0, nil
	} else {
		cpysize := b.length - b.rdInd
		copy(p, b.buffer[b.rdInd:b.length])
		b.rdInd = b.length - 1
		return cpysize, nil
	}

}
