package ndr

import (
	
	
	
	
)

// intFromTag returns an int that is a value in a struct tag key/value pair
func intFromTag( reflect.StructTag,  string) (int, error) {
	 := parseTags()
	 := 1
	if ,  := .Map[];  {
		,  := strconv.Atoi()
		if  != nil {
			return , fmt.Errorf("invalid dimensions tag [%s]: %v", , )
		}
		 = 
	}
	return , nil
}

// parseDimensions returns the a slice of the size of each dimension and type of the member at the deepest level.
func parseDimensions( reflect.Value) ( []int,  reflect.Type) {
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	 := .Type()
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	if .Kind() != reflect.Array && .Kind() != reflect.Slice {
		return
	}
	 = append(, .Len())
	if .Elem().Kind() == reflect.Array || .Elem().Kind() == reflect.Slice {
		// contains array or slice
		var  []int
		,  = (.Index(0))
		 = append(, ...)
	} else {
		 = .Elem()
	}
	return
}

// sliceDimensions returns the count of dimensions a slice has.
func sliceDimensions( reflect.Type) ( int,  reflect.Type) {
	if .Kind() == reflect.Ptr {
		 = .Elem()
	}
	if .Kind() == reflect.Slice {
		++
		var  int
		,  = (.Elem())
		 += 
	} else {
		 = 
	}
	return
}

// makeSubSlices is a deep recursive creation/initialisation of multi-dimensional slices.
// Takes the reflect.Value of the 1st dimension and a slice of the lengths of the sub dimensions
func makeSubSlices( reflect.Value,  []int) {
	 := .Type().Elem()
	if .Kind() != reflect.Slice {
		return
	}
	for  := 0;  < .Len(); ++ {
		 := reflect.MakeSlice(, [0], [0])
		.Index().Set()
		// Are there more sub dimensions?
		if len() > 1 {
			(.Index(), [1:])
		}
	}
	return
}

// multiDimensionalIndexPermutations returns all the permutations of the indexes of a multi-dimensional slice.
// The input is a slice of integers that indicates the max size/length of each dimension
func multiDimensionalIndexPermutations( []int) ( [][]int) {
	 := make([]int, len(), len()) // The zeros permutation
	 = append(, )
	// for each dimension, in reverse
	for  := len() - 1;  >= 0; -- {
		 := make([][]int, len())
		copy(, )
		//create a permutation for each of the iterations of the current dimension
		for  := 1;  <= []-1; ++ {
			// For each existing permutation
			for ,  := range  {
				 := make([]int, len(), len())
				copy(, )
				[] = 
				 = append(, )
			}
		}
	}
	return
}

// precedingMax reads off the next conformant max value
func ( *Decoder) () uint32 {
	 := .conformantMax[0]
	.conformantMax = .conformantMax[1:]
	return 
}

// fillFixedArray establishes if the fixed array is uni or multi dimensional and then fills it.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	,  := parseDimensions()
	if .Kind() == reflect.String {
		 = reflect.StructTag(subStringArrayTag)
	}
	if len() < 1 {
		return errors.New("could not establish dimensions of fixed array")
	}
	if len() == 1 {
		 := .fillUniDimensionalFixedArray(, , )
		if  != nil {
			return fmt.Errorf("could not fill uni-dimensional fixed array: %v", )
		}
		return nil
	}
	// Fixed array is multidimensional
	 := multiDimensionalIndexPermutations([:len()-1])
	for ,  := range  {
		// Get current multi-dimensional index to fill
		 := 
		for ,  := range  {
			 = .Index()
		}
		// fill with the last dimension array
		 := .fillUniDimensionalFixedArray(, , )
		if  != nil {
			return fmt.Errorf("could not fill dimension %v of multi-dimensional fixed array: %v", , )
		}
	}
	return nil
}

// readUniDimensionalFixedArray reads an array (not slice) from the byte stream.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	for  := 0;  < .Len(); ++ {
		 := .fill(.Index(), , )
		if  != nil {
			return fmt.Errorf("could not fill index %d of fixed array: %v", , )
		}
	}
	return nil
}

// fillConformantArray establishes if the conformant array is uni or multi dimensional and then fills the slice.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	,  := sliceDimensions(.Type())
	if  > 1 {
		 := .fillMultiDimensionalConformantArray(, , , )
		if  != nil {
			return 
		}
	} else {
		 := .fillUniDimensionalConformantArray(, , )
		if  != nil {
			return 
		}
	}
	return nil
}

// fillUniDimensionalConformantArray fills the uni-dimensional slice value.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	 := .precedingMax()
	 := int()
	 := reflect.MakeSlice(.Type(), , )
	for  := 0;  < ; ++ {
		 := .fill(.Index(), , )
		if  != nil {
			return fmt.Errorf("could not fill index %d of uni-dimensional conformant array: %v", , )
		}
	}
	.Set()
	return nil
}

// fillMultiDimensionalConformantArray fills the multi-dimensional slice value provided from conformant array data.
// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
// method not to panic.
func ( *Decoder) ( reflect.Value,  int,  reflect.StructTag,  *[]deferedPtr) error {
	// Read the max size of each dimensions from the ndr stream
	 := make([]int, , )
	for  := range  {
		[] = int(.precedingMax())
	}
	// Initialise size of slices
	//   Initialise the size of the 1st dimension
	 := .Type()
	.Set(reflect.MakeSlice(, [0], [0]))
	// Initialise the size of the other dimensions recursively
	makeSubSlices(, [1:])

	// Get all permutations of the indexes and go through each and fill
	 := multiDimensionalIndexPermutations()
	for ,  := range  {
		// Get current multi-dimensional index to fill
		 := 
		for ,  := range  {
			 = .Index()
		}
		 := .fill(, , )
		if  != nil {
			return fmt.Errorf("could not fill index %v of slice: %v", , )
		}
	}
	return nil
}

// fillVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	,  := sliceDimensions(.Type())
	if  > 1 {
		 := .fillMultiDimensionalVaryingArray(, , , , )
		if  != nil {
			return 
		}
	} else {
		 := .fillUniDimensionalVaryingArray(, , )
		if  != nil {
			return 
		}
	}
	return nil
}

// fillUniDimensionalVaryingArray fills the uni-dimensional slice value.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	,  := .readUint32()
	if  != nil {
		return fmt.Errorf("could not read offset of uni-dimensional varying array: %v", )
	}
	,  := .readUint32()
	if  != nil {
		return fmt.Errorf("could not establish actual count of uni-dimensional varying array: %v", )
	}
	 := .Type()
	// Total size of the array is the offset in the index being passed plus the actual count of elements being passed.
	 := int( + )
	 := reflect.MakeSlice(, , )
	// Populate the array starting at the offset specified
	for  := int();  < ; ++ {
		 := .fill(.Index(), , )
		if  != nil {
			return fmt.Errorf("could not fill index %d of uni-dimensional varying array: %v", , )
		}
	}
	.Set()
	return nil
}

// fillMultiDimensionalVaryingArray fills the multi-dimensional slice value provided from varying array data.
// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
// method not to panic.
func ( *Decoder) ( reflect.Value,  reflect.Type,  int,  reflect.StructTag,  *[]deferedPtr) error {
	// Read the offset and actual count of each dimensions from the ndr stream
	 := make([]int, , )
	 := make([]int, , )
	for  := range  {
		,  := .readUint32()
		if  != nil {
			return fmt.Errorf("could not read offset of dimension %d: %v", +1, )
		}
		[] = int()
		,  := .readUint32()
		if  != nil {
			return fmt.Errorf("could not read size of dimension %d: %v", +1, )
		}
		[] = int() + int()
	}
	// Initialise size of slices
	//   Initialise the size of the 1st dimension
	 := .Type()
	.Set(reflect.MakeSlice(, [0], [0]))
	// Initialise the size of the other dimensions recursively
	makeSubSlices(, [1:])

	// Get all permutations of the indexes and go through each and fill
	 := multiDimensionalIndexPermutations()
	for ,  := range  {
		// Get current multi-dimensional index to fill
		 := 
		var  bool // should this permutation be skipped due to the offset of any of the dimensions?
		for ,  := range  {
			if  < [] {
				 = true
				break
			}
			 = .Index()
		}
		if  {
			// This permutation should be skipped as it is less than the offset for one of the dimensions.
			continue
		}
		 := .fill(, , )
		if  != nil {
			return fmt.Errorf("could not fill index %v of slice: %v", , )
		}
	}
	return nil
}

// fillConformantVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	,  := sliceDimensions(.Type())
	if  > 1 {
		 := .fillMultiDimensionalConformantVaryingArray(, , , , )
		if  != nil {
			return 
		}
	} else {
		 := .fillUniDimensionalConformantVaryingArray(, , )
		if  != nil {
			return 
		}
	}
	return nil
}

// fillUniDimensionalConformantVaryingArray fills the uni-dimensional slice value.
func ( *Decoder) ( reflect.Value,  reflect.StructTag,  *[]deferedPtr) error {
	 := .precedingMax()
	,  := .readUint32()
	if  != nil {
		return fmt.Errorf("could not read offset of uni-dimensional conformant varying array: %v", )
	}
	,  := .readUint32()
	if  != nil {
		return fmt.Errorf("could not establish actual count of uni-dimensional conformant varying array: %v", )
	}
	if  < + {
		return errors.New("max count is less than the offset plus actual count")
	}
	 := .Type()
	 := int()
	 := reflect.MakeSlice(, , )
	for  := int();  < ; ++ {
		 := .fill(.Index(), , )
		if  != nil {
			return fmt.Errorf("could not fill index %d of uni-dimensional conformant varying array: %v", , )
		}
	}
	.Set()
	return nil
}

// fillMultiDimensionalConformantVaryingArray fills the multi-dimensional slice value provided from conformant varying array data.
// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
// method not to panic.
func ( *Decoder) ( reflect.Value,  reflect.Type,  int,  reflect.StructTag,  *[]deferedPtr) error {
	// Read the offset and actual count of each dimensions from the ndr stream
	 := make([]int, , )
	for  := range  {
		[] = int(.precedingMax())
	}
	 := make([]int, , )
	 := make([]int, , )
	for  := range  {
		,  := .readUint32()
		if  != nil {
			return fmt.Errorf("could not read offset of dimension %d: %v", +1, )
		}
		[] = int()
		,  := .readUint32()
		if  != nil {
			return fmt.Errorf("could not read actual count of dimension %d: %v", +1, )
		}
		if [] < int()+int() {
			[] = int() + int()
		}
		[] = int()
	}
	// Initialise size of slices
	//   Initialise the size of the 1st dimension
	 := .Type()
	.Set(reflect.MakeSlice(, [0], [0]))
	// Initialise the size of the other dimensions recursively
	makeSubSlices(, [1:])

	// Get all permutations of the indexes and go through each and fill
	 := multiDimensionalIndexPermutations()
	for ,  := range  {
		// Get current multi-dimensional index to fill
		 := 
		var  bool // should this permutation be skipped due to the offset of any of the dimensions or max is higher than the actual count being passed
		for ,  := range  {
			if  < [] ||  >= [] {
				 = true
				break
			}
			 = .Index()
		}
		if  {
			// This permutation should be skipped as it is less than the offset for one of the dimensions.
			continue
		}
		 := .fill(, , )
		if  != nil {
			return fmt.Errorf("could not fill index %v of slice: %v", , )
		}
	}
	return nil
}