/**************************************************************************************
 *
 *     Project:    kvmstoreserver
 *     File name:  btree.go
 *     Version:    1.0.0
 *     Date:       2018-01-14
 *
 *     Autor:      Bogdanovic Theodor
 *     Contact:    t.bogdanovic@hotmail.com
 *
 *     License:    GPLv3
 *
 **************************************************************************************/
/**************************************************************************************
 * Copyright 2018 Bogdanovic Theodor
 *
 * This file is part of kvmstoreserver.
 *
 * kvmstoreserver is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * kvmstoreserver is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with kvmstoreserver.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************************/
/**************************************************************************************
 *  Further explanations:  This source code is under the license specified by the
 *                         Autor above. Core components or other packages are still
 *                         in source code form, under the license specified by the
 *                         respective author.
 **************************************************************************************/

// Package bTreeHandling contains all the components that are necessary for the
// operation of the binary tree. Some components, such as MetaData, are not
// required, but are included here. The Pakage implements the Signleton
// Pattern. If this is removed, this package can be partially used as a
// stand-alone data type
package bTreeHandling

import (
	"encoding/json"
	"fmt"
	knh "kvmstoreserver_utils/keyNameHandling"

	log "github.com/judwhite/logrjack"
)

//*****************************************************************************
//*
//*      Declarations
//*
//*****************************************************************************
var (
	// This reference is always transported to the outside
	// after the first creation
	binTree *btree

	// Is necessary for mapping the tree into a slice
	nodeMatrixSlice []*nodeMatrix
)

//*****************************************************************************
//*
//*      STRUCTURE: Btree (Public)
//*
//*****************************************************************************

// btree includes a reference to nodeCOntainer and provides the
// only known node (root)
//
// Implement: IBtreeController
type btree struct {
	root *nodeContainer
}

//********************* Iface Methods: IBtreeController ***********************

// Member checks if a node exists with the passed key context in the tree.
func (slf *btree) Member(nodeCtx knh.IHandelWithIds) error {

	if slf.root != nil {
		return slf.root.member(nodeCtx)
	}

	return fmt.Errorf("Can not find a ressource with key name -> %v",
		nodeCtx.GetKeyStrName())
}

// InsertContainer a complete container in the Tree.
// Is it only used by the node loader -> stage0_ext1
func (slf *btree) InsertContainer(ct IHandelWithNodes) {

	if slf.root == nil {
		slf.root = (ct).(*nodeContainer)
	} else {
		slf.root.insertContainer(ct)
	}
}

// Insert a dataModel in the tree. the position (nodeID) is
// read from the key context.
func (slf *btree) Insert(nodeCtx knh.IHandelWithIds, data *DataModel) (int, error) {

	if slf.root == nil {
		slf.root = newNode(nodeCtx)
		log.Infof("Create new ROOT node with (ID: %v) for (Key name: %v)",
			nodeCtx.GetNodeID(), nodeCtx.GetKeyStrName())

		return slf.root.insertData(data)
	}

	return slf.root.insert(nodeCtx, data)
}

// Get returns an interface to the node container.
// This can be used to access data in the node contianer
func (slf *btree) Get(nodeCtx knh.IHandelWithIds) IHandelWithNodes {

	return slf.root.get(nodeCtx)
}

// Del empties the node container and deletes the information on the disk.
// The Contian itself remains intact during the term of the program and
// can be addressed and filled as needed
func (slf *btree) Del(nodeCtx knh.IHandelWithIds) error {

	return slf.root.del(nodeCtx)
}

//******************** Tree representation ************************************

/*
In this section is the complete functionality to map the Btree into a slice.

It calls mapTreeIntoSlice recrucively and goes down the whole tree on the left
until it meets an end. Then the call goes back one step and takes the right
branch. If this is not empty, the function tries again first on the left side
to an end, then goes back one step and so goes the recursive call until the
tree is worked from bottom left to bottom right
*/

type nodeMatrix struct {
	NodeID        int  `json:"node_id,omitempty"`
	IsRootNode    bool `json:"is_root_node"`
	BranchLeftID  int  `json:"branch_left_id,omitempty"`
	BranchRightID int  `json:"branch_right_id,omitempty"`
}

type emptyTree struct {
	Message string `json:"message,omitempty"`
}

// GetBtreeStruct returns the structure of the tree mapped
// into a slice. The slice is also converted into a JSON document
func (slf *btree) GetBtreeStruct() []byte {

	nodeMatrixSlice = make([]*nodeMatrix, 0)
	mapTreeIntoSlice(slf.root, true)
	jsonOutput, _ := json.Marshal(&nodeMatrixSlice)
	if len(jsonOutput) <= 2 {
		empty := &emptyTree{
			Message: "The Tree is empty :(",
		}
		jsonEmpty, _ := json.Marshal(empty)
		return jsonEmpty
	}

	nodeMatrixSlice = nil
	return jsonOutput
}

func mapTreeIntoSlice(nc *nodeContainer, isRoot bool) {

	if nc == nil {
		return
	}

	newNodeInfo := nodeMatrix{}
	newNodeInfo.IsRootNode = isRoot
	if nc.BranchLeft != nil {
		newNodeInfo.BranchLeftID = nc.BranchLeft.NodeID
	}
	if nc.BranchRight != nil {
		newNodeInfo.BranchRightID = nc.BranchRight.NodeID
	}
	newNodeInfo.NodeID = nc.NodeID

	nodeMatrixSlice = append(nodeMatrixSlice, &newNodeInfo)

	mapTreeIntoSlice(nc.BranchLeft, false)
	mapTreeIntoSlice(nc.BranchRight, false)
}

//*****************************************************************************
//*
//*      Public Functions
//*
//*****************************************************************************

// NewBtree generates a new empty tree on the first call and saves it in the
// global variable of the package. Each call causes only the reference to the
// global variable to be passed on
func NewBtree() IBtreeController {

	if binTree == nil {
		binTree = &btree{}
		return binTree
	}

	return binTree
}
