/*
   Recursively inserts the record based on Sedgewick's algorithm
*/
func (self *BTree) insert(block *KeyBlock, rec *Record, height int, dirty *dirty.DirtyBlocks) (*KeyBlock, *Record, bool) {
	//     fmt.Println("inserting", rec, "\n", block, height)
	var nextb *KeyBlock
	if height > 0 {
		// at an interior node
		var pos ByteSlice
		{
			// we need to find the next block to search
			k := rec.GetKey()
			i, _, _, _, _ := block.Find(k) // find where the key would go in the block
			if i >= int(block.RecordCount()) {
				i-- // is it after the last key?
			}
			r, left, right, ok := block.Get(i) // get the record
			if ok && (k.Lt(r.GetKey())) && left != nil {
				pos = left // hey it goes on the left
			} else if ok && right != nil {
				pos = right // the right
			} else {
				fmt.Println("Bad block pointer in interior node PANIC, for real? ", ok)
				fmt.Println(block)
				os.Exit(4)
			}
		}
		// recursive insert call, s is true we a node split occured in the level below so we change our insert
		if b, r, s := self.insert(self.getblock(pos), rec, height-1, dirty); s {
			// a node split occured in the previous call
			// so we are going to use this new record now
			nextb = b
			rec = r
		} else {
			// no node split we return to the parent saying it has nothing to do
			return nil, nil, false
		}
	}
	// this block is changed
	dirty.Insert(block)
	if i, ok := block.Add(rec); ok {
		// Block isn't full record inserted, now insert pointer (if one exists)
		// return to parent saying it has nothing to do
		if nextb != nil {
			block.InsertPointer(i+1, nextb.Position())
		}
		return nil, nil, false
	}
	// Block is full split the block
	return self.split(block, rec, nextb, dirty)
}
/*
   split takes a block figures out how to split it and splits it between the two blocks, it passes back
   the splitting record, and a pointer new block, and whether or not it succeeded
   nextb is the block that will be pointed at by one of the blocks
        ie. it was a block that was allocated by the previous split, normally a pointer to it would have
            been inserted into the block that is being split, but as that block is full it needs to go into
            one of the blocks here
        the function should always return a valid btree if the record it returns becomes the record at the root
        level.
*/
func (self *BTree) split(block *KeyBlock, rec *Record, nextb *KeyBlock, dirty *dirty.DirtyBlocks) (*KeyBlock, *Record, bool) {
	var split_rec *Record
	new_block := self.allocate()
	dirty.Insert(new_block)
	i, _, _, _, _ := block.Find(rec.GetKey())
	m := self.node.KeysPerBlock() >> 1
	//     fmt.Println("m=", m)
	if m > i {
		split_rec, _, _, _ = block.Get(m - 1)
		block.RemoveAtIndex(m - 1)
		if _, ok := block.Add(rec); !ok {
			fmt.Println("Inserting record into block failed PANIC")
			os.Exit(3)
		}
	} else if m < i {
		split_rec, _, _, _ = block.Get(m)
		block.RemoveAtIndex(m)
		if _, ok := block.Add(rec); !ok {
			fmt.Println("Inserting record into block failed PANIC")
			os.Exit(3)
		}
	} else {
		split_rec = rec
	}
	self.balance_blocks(block, new_block)
	dirty.Sync() // figure out how to remove
	if nextb != nil {
		//         fmt.Println("NEXTB: ", nextb)
		nextr, _, _, _ := nextb.Get(0)
		if i, _, _, _, ok := block.Find(rec.GetKey()); ok {
			// if this pointer is going into the old block that means there will be too many pointers in this block
			// so we must move the last one over to the new block

			if p, ok := block.GetPointer(m); ok {
				new_block.InsertPointer(0, p)
			}
			block.RemovePointer(m)

			_, left, _, _ := block.Get(i)
			//             fmt.Println("left=", left)
			lblock := self.getblock(left)
			r, _, _, _ := lblock.Get(0)
			//             fmt.Println("empty")
			//             fmt.Printf("nextr %v > %v r, %v\n", nextr.GetKey(), r.GetKey(), nextr.GetKey().Gt(r.GetKey()))
			if nextr.GetKey().Gt(r.GetKey()) {
				//                 fmt.Println("i=", i, "+1")
				block.InsertPointer(i+1, nextb.Position())
			} else {
				//                 fmt.Println("i=", i)
				block.InsertPointer(i, nextb.Position())
			}
		} else {
			i, _, _, _, _ := new_block.Find(rec.GetKey())
			_, left, _, _ := new_block.Get(i)
			//             fmt.Println("left=", left)
			lblock := self.getblock(left)
			r, _, _, _ := lblock.Get(0)
			//             fmt.Println("empty")
			//             fmt.Printf("nextr %v > %v r, %v\n", nextr.GetKey(), r.GetKey(), nextr.GetKey().Gt(r.GetKey()))
			if nextr.GetKey().Gt(r.GetKey()) {
				//                 fmt.Println("i=", i, "+1")
				new_block.InsertPointer(i+1, nextb.Position())
			} else {
				//                 fmt.Println("i=", i)
				new_block.InsertPointer(i, nextb.Position())
			}
		}
	}
	//     j, _, _, _, _ := new_block.Find(split_rec.GetKey())
	//     fmt.Println("split .... ", j)
	//     fmt.Println(new_block.Position(), split_rec, true)
	return new_block, split_rec, true
}
func (self *BpTree) split(a *KeyBlock, rec *tmprec, nextb *KeyBlock, dirty *dirty.DirtyBlocks) (*KeyBlock, *tmprec, bool) {

	//     fmt.Println("Splitting", rec.internal())
	//     fmt.Println("Original:\n", a)

	// this closure figures out which kind of block and record we need, either external or internal
	// we make this choice based on the mode of a because b+ trees have the property that the
	// allocated block always has the same node as the block to be split.
	b, r := func() (*KeyBlock, *Record) {
		if a.Mode() == self.external.Mode {
			return self.allocate(self.external), rec.external()
		}
		return self.allocate(self.internal), rec.internal()
	}()
	dirty.Insert(b)

	// m is the record to take as the "mid" point it is no longer stictly the mid point because
	//      one must be careful to not split up duplicate keys. instead m is the closest point to
	//      the mid point which is on the edge of the run of duplicate keys
	//
	// s is the point which the blocks should be balanced against. (ie the balance point)
	//
	// right is the block to insert into, true is a, false is b
	m, s, right := func() (m int, s int, right bool) {
		getk := func(i int) ByteSlice {
			r, _, _, _ := a.Get(i)
			return r.GetKey()
		}
		n := int(a.MaxRecordCount()) + 1
		key := getk(n >> 1)
		l, _, _, _, _ := a.Find(key) // l = left the left side of the run of dup keys
		r := l                       // r = right the right side of the run of dup keys
		for ; r < n-1 && getk(r).Eq(key); r++ {
		}
		r--
		lr := math.Fabs(float64(l) / float64(n)) // left ratio (ie. how close is left to mid)
		rr := math.Fabs(float64(r) / float64(n)) // right ration (ie. how close is right to mid)
		// we return which ever has a ratio closer to zero
		if lr <= rr && l != 0 {
			m = l
			s = l - 1 // since it is the left one we *must* subtract one from the balance point
			right = false
		} else {
			m = r
			s = r
			right = true
		}
		return
	}()

	// This dependent block finds which record or split the block on.
	// it also identifies which the record should point at
	var split_rec *Record
	var nextp ByteSlice
	{
		i, _, _, _, ok := a.Find(r.GetKey())
		//         fmt.Printf("m=%v, s=%v, i=%v, choice=%v\n", m, s, i, right)

		// so what is going on here is if the key is in a certain block we need to make sure
		// we insert our next key in that block. This is the cleanest way to do that for now. even
		// though it is hideously ugly.
		if ok && m > i {
			//             fmt.Println("choosing a", i, m, ok)
			right = true
		} else if ok && m < i {
			//             fmt.Println("choosing b", i, m, ok)
			right = false
		} else if m > i {
			// the mid point is after the spot where we would insert the key so we take the record
			// just before the mid point as our new record would shift the mid point over by 1
			split_rec, nextp, _, _ = a.Get(m - 1)
			a.RemoveAtIndex(m - 1)
			a.RemovePointer(m - 1)
		} else if m < i {
			// the mid point is before the new record so we can take the record at the mid point as
			// the split record.
			split_rec, nextp, _, _ = a.Get(m)
			a.RemoveAtIndex(m)
			a.RemovePointer(m)
		}

		if i == m || ok && m > i || ok && m < i {
			// the mid point is where the new record would go so in this case the new record will be
			// the split record for this split, and the nextb (which is associate with our new key)
			// become the nextp pointer
			split_rec = r
			if nextb != nil {
				nextp = nextb.Position()
			}
		} else {
			// otherwise we now need to insert our new record into the block before it is balanced
			if i, ok := a.Add(r); !ok {
				log.Exit("Inserting record into block failed PANIC")
			} else {
				// after it is inserted we need to associate the next block with its key (which is
				// the key we just inserted).
				if nextb != nil {
					a.InsertPointer(i, nextb.Position())
					nextb = nil
				}
			}
		}
	}
	self.balance_blocks(s, a, b) // using s as the balance point
	//     fmt.Println("AFTER BALANCE")
	//     fmt.Println("Full:\n", a)
	//     fmt.Println("Empty:\n", b)

	var return_rec *Record = split_rec
	var block *KeyBlock

	// choose which block to insert the record into
	if _, _, _, _, ok := a.Find(split_rec.GetKey()); ok {
		block = a
	} else if _, _, _, _, ok := b.Find(split_rec.GetKey()); ok {
		block = b
	} else if a.RecordCount() < b.RecordCount() {
		//         fmt.Println("chose a")
		block = a
	} else {
		//         fmt.Println("chose b")
		block = b
	}

	// add the record to the block
	if i, ok := block.Add(split_rec); !ok {
		fmt.Println(m, s, right)
		fmt.Println("Full:\n", a)
		fmt.Println("Empty:\n", b)
		fmt.Println("Rec:", r)
		fmt.Println("return rec:", return_rec)
		log.Exit("Could not add the split_rec to the selected block PANIC")
	} else {
		// we now insert the pointer if we have one
		if block.Mode()&POINTERS == POINTERS && nextp != nil {
			// we have a block that supports a pointer and a pointer
			if !block.InsertPointer(i, nextp) {
				log.Exit("166 pointer insert failed! PANIC")
			}
		} else if block.Mode()&POINTERS == 0 && nextp != nil {
			// we don't have a block that supports a pointer but we do have a pointer
			log.Exit("tried to set a pointer on a block with no pointers")
		} else if block.Mode()&POINTERS == POINTERS && nextp == nil {
			// we have a block that supports a pointer but don't have a pointer
			log.Exit("splitting an internal block split requires a next block to point at")
		} // else
		//    we have a block that doesn't support a pointer and we don't have pointer
	}

	if rec, _, _, ok := b.Get(0); !ok {
		log.Exit("Could not get the first record from block b PANIC")
	} else {
		// we change the record returned because the first record in block b will now not
		// be the record we split on which is ok we just need to return the correct record.
		return_rec = rec
	}

	// if we have an external node (leaf) then we need to hook up the pointers between the leaf
	// nodes to support range queries and duplicate keys
	if a.Mode() == self.external.Mode {
		tmp, _ := a.GetExtraPtr()
		b.SetExtraPtr(tmp)
		a.SetExtraPtr(b.Position())
	}

	//     fmt.Println("\n\n\nAFTER EVERYTHING")
	//     fmt.Println("Full:\n", a)
	//     fmt.Println("Empty:\n", b)
	//     fmt.Println("Rec:", r)
	//     fmt.Println("return rec:", return_rec)

	return b, rec_to_tmp(self, return_rec), true
}
func (self *BpTree) insert(block *KeyBlock, rec *tmprec, height int, dirty *dirty.DirtyBlocks) (*KeyBlock, *tmprec, bool) {
	var findlastblock func(*KeyBlock, ByteSlice) *KeyBlock
	findlastblock = func(block *KeyBlock, key ByteSlice) *KeyBlock {
		p, _ := block.GetExtraPtr()
		if p.Eq(ByteSlice64(0)) {
			return block
		}
		next := self.getblock(p)
		if r, _, _, ok := next.Get(0); !ok {
			return block
		} else {
			if r.GetKey().Eq(key) {
				return findlastblock(next, key)
			}
		}
		return block
	}

	// function to take a tmprec and turn it into the appropriate type of *Record
	_convert := func(rec *tmprec) *Record {
		if block.Mode() == self.external.Mode {
			return rec.external()
		}
		return rec.internal()
	}
	r := _convert(rec)

	//     fmt.Println("INSERTING ", r, block, "\n\n\n")

	// nextb is the block to be passed up the the caller in the case of a split at this level.
	var nextb *KeyBlock

	if height > 0 {
		// internal node
		// first we will need to find the next block to traverse down to
		var pos ByteSlice
		{
			// we find where in the block this key would be inserted
			i, _, _, _, ok := block.Find(rec.key)

			if i == 0 {
				// if that spot is zero it means that it is less than the smallest key the block
				// so we adjust the block appropriately
				if r, p, _, ok := block.Get(i); ok {
					dirty.Insert(block)
					r.SetKey(rec.key)
					pos = p
				} else {
					log.Exitf("227 Error could not get record %v from block %v", i, block)
				}
			} else if ok {
				if _, p, _, ok := block.Get(i); ok {
					pos = p
				} else {
					log.Exitf("235 Error could not get record %v from block %v", i, block)
				}
			} else {
				// else this spot is one to many so we get the previous spot
				i--
				if _, p, _, ok := block.Get(i); ok {
					pos = p
				} else {
					log.Exitf("235 Error could not get record %v from block %v", i, block)
				}
			}
		}

		// if pos is nil we have a serious problem
		if pos == nil {
			log.Exit("242 Nil Pointer")
		}

		// after we have found the position we get the block
		// then make a recursive call to insert to insert the record into the next block
		if b, srec, s := self.insert(self.getblock(pos), rec, height-1, dirty); s {
			// if the next block split we will insert the key passed up the chain.
			nextb = b
			r = _convert(srec)
			rec = srec
		} else {
			return nil, nil, false
		}
	} else {
		//         c := block.Count(rec.key)
		//         ratio := float(c) / float(block.MaxRecordCount())
		if block.Full() {
			firstr, _, _, _ := block.Get(0)
			if block.Count(firstr.GetKey()) == int(block.MaxRecordCount()) {
				if !r.GetKey().Lt(firstr.GetKey()) {
					block := findlastblock(block, firstr.GetKey())
					dirty.Insert(block)
					//                     fmt.Println("Magic Heres Abouts", r, "\n", block)
					if block.Full() || !r.GetKey().Eq(firstr.GetKey()) {
						newblock := self.allocate(self.external)
						dirty.Insert(newblock)
						p, _ := block.GetExtraPtr()
						newblock.SetExtraPtr(p)
						block.SetExtraPtr(newblock.Position())
						if _, ok := newblock.Add(r); !ok {
							log.Exit("347 could not add to empty block")
						}
						if r.GetKey().Eq(firstr.GetKey()) {
							return nil, nil, false
						} else {
							//                         return newblock, rec_to_tmp(self, self.internal.NewRecord(firstr.GetKey().Inc())), true
							return newblock, rec_to_tmp(self, r), true
						}
					} else {
						block.Add(r)
						return nil, nil, false
					}
				} else {
					//                     fmt.Println("more magic", r, firstr)
					newblock := self.allocate(self.external)
					dirty.Insert(block)
					dirty.Insert(newblock)
					p, _ := block.GetExtraPtr()
					newblock.SetExtraPtr(p)
					block.SetExtraPtr(newblock.Position())
					self.mv(block, newblock)
					if _, ok := block.Add(r); !ok {
						log.Exit("366 could not add to empty block")
					}
					return newblock, rec_to_tmp(self, firstr), true
				}
			}
		}
	}
	// this block is changed
	dirty.Insert(block)
	if i, ok := block.Add(r); ok {
		// Block isn't full record inserted, now insert pointer (if one exists)
		// return to parent saying it has nothing to do
		if block.Mode()&POINTERS == POINTERS && nextb != nil {
			if ok := block.InsertPointer(i, nextb.Position()); !ok {
				log.Exit("pointer insert failed")
			}
		} else if block.Mode()&POINTERS == 0 && nextb != nil {
			log.Exit("tried to set a pointer on a block with no pointers")
		}
		return nil, nil, false
	} else if i == -2 {
		// Dotty("error.dot", self)
		dirty.Sync()
		// Dotty("error2.dot", self)
		log.Exit("tried to insert a duplicate key into a block which does not allow that.\n", r, "\n", block)
	}
	// Block is full split the block
	return self.split(block, rec, nextb, dirty)
}