package arrayn

type SizeN struct {
	Sizes []int
}

type ProductN struct {
	Values []int
}

type DimensionSize struct {
	Sizes []int
}

type Node struct {
	size int
	data interface{}
}

type ArrayN struct {
	dims    []int
	product []int

	dimN int
	data *Node // []*Node

	cap int
}

func New() *ArrayN {
	return NewWithCap(8, 8, 8)
}

func NewWithCap(dims ...int) *ArrayN {
	arr := &ArrayN{dimN: len(dims), dims: dims}
	arr.product = make([]int, len(dims)-1, len(dims)-1)
	for i := 0; i < len(dims)-1; i++ {
		pvalue := 1
		for n := i + 1; n < len(dims); n++ {
			pvalue *= dims[n]
		}
		arr.product[i] = pvalue
	}
	// arr.data = make([]*Node, arr.dims[0], arr.dims[0])
	arr.cap = 1
	for _, d := range arr.dims {
		arr.cap *= d
	}
	return arr
}

func (arr *ArrayN) collectValues(curDim int, cur *Node, result *[]interface{}) {
	if cur == nil {
		return
	}

	if curDim == 1 {
		for _, v := range cur.data.([]interface{}) {
			if v != nil {
				*result = append(*result, v)
			} else {
				*result = append(*result, nil)
			}
		}
		return
	}

	for _, n := range cur.data.([]*Node) {
		if n != nil {
			arr.collectValues(curDim-1, n, result)
		} else {
			total := 1
			for i := len(arr.dims) - curDim + 1; i < len(arr.dims); i++ {
				total *= arr.dims[i]
			}

			for i := 0; i < total; i++ {
				*result = append(*result, nil)
			}
		}
	}
}

func (arr *ArrayN) Values() (result []interface{}) {
	arr.collectValues(arr.dimN, arr.data, &result)
	return
}

func (arr *ArrayN) set(curDim int, curidx int, pdata **Node, parent *Node) (*Node, int) {

	sidx := arr.dimN - curDim

	if *pdata == nil {
		if parent != nil {
			parent.size++
		}
		if curDim > 1 {
			*pdata = &Node{data: make([]*Node, arr.dims[sidx], arr.dims[sidx])}
		} else {
			*pdata = &Node{data: make([]interface{}, arr.dims[sidx], arr.dims[sidx])}
			return *pdata, curidx
		}
	}

	cur := *pdata
	if curDim == 1 {
		return cur, curidx
	}

	nidx := curidx % arr.product[sidx]
	dimindex := curidx / arr.product[sidx]

	return arr.set(curDim-1, nidx, &cur.data.([]*Node)[dimindex], cur)
}

func (arr *ArrayN) Cap() int {
	return arr.cap
}

func (arr *ArrayN) Grow(size int) {
	arr.dims[0] += size

	pvalue := 1
	for n := 1; n < len(arr.dims); n++ {
		pvalue *= arr.dims[n]
	}
	arr.product[0] = pvalue

	tempdata := arr.data.data.([]*Node)

	newData := make([]*Node, arr.dims[0], arr.dims[0])
	copy(newData, tempdata)
	arr.data.data = newData

	arr.cap = 1
	for _, d := range arr.dims {
		arr.cap *= d
	}
}

func (arr *ArrayN) Set(idx int, value interface{}) {
	n, nidx := arr.set(arr.dimN, idx, &arr.data, nil)
	n.data.([]interface{})[nidx] = value
}

func (arr *ArrayN) get(curDim int, curidx int, pdata **Node) (*Node, int) {
	sidx := arr.dimN - curDim

	if *pdata == nil {
		return nil, 0
	}

	cur := *pdata
	if curDim == 1 {
		return cur, curidx
	}

	nidx := curidx % arr.product[sidx]
	dimindex := curidx / arr.product[sidx]
	return arr.get(curDim-1, nidx, &cur.data.([]*Node)[dimindex])
}

func (arr *ArrayN) Get(idx int) (interface{}, bool) {
	n, nidx := arr.get(arr.dimN, idx, &arr.data)
	if n != nil {
		v := n.data.([]interface{})[nidx]
		return v, v != nil
	}
	return nil, false
}

func (arr *ArrayN) del(curDim int, curidx int, pdata **Node) (interface{}, bool) {

	cur := *pdata
	if cur == nil {
		return nil, false
	}

	if curDim == 1 {
		values := cur.data.([]interface{})

		v := values[curidx]
		if v != nil {
			cur.size--
			values[curidx] = nil
			if cur.size == 0 {
				return v, true
			}
		}
		return v, false
	}

	sidx := arr.dimN - curDim

	nidx := curidx % arr.product[sidx]
	dimindex := curidx / arr.product[sidx]
	curdata := cur.data.([]*Node)

	v, ok := arr.del(curDim-1, nidx, &curdata[dimindex])
	if ok {
		cur.size--
		curdata[dimindex] = nil
		if cur.size == 0 {
			return v, true
		}
	}

	return v, false
}

func (arr *ArrayN) Del(idx int) (interface{}, bool) {
	v, _ := arr.del(arr.dimN, idx, &arr.data)
	if v != nil {
		return v, true
	}
	return nil, false
}