#include "avl.h"

avl::avl(): root(NULL) {}

avl::~avl() {
    deltree(root);
}

void avl::deltree (node* root) {
 //   if(root != NULL) {
 //       deltree(root->left);
 //       deltree(root->right);
 //   }
 //   delete root;
}

NodeInterface* avl::getRootNode() {
    return root;
}

bool avl::add(int data) {
    bool found = false;
    node* current;
    node* parent;
    current = root;
    while(current) {
        if(current->data == data) {
            found = true;
            break;
        }
        else {
            parent = current;
            if(data > current->data) {
                current = current->right;
            }
            else if(data < current->data) {
                current = current->left;
            }
        }
    }
    if(found) {
        return false;
    }
    root = build(root, data);
    return true;
}

node* avl::build(node* root, int data) {
    node* temp1 = NULL;
    node* temp2 = NULL;
    if(root == NULL) {
        root = new node(data);
        h = true;
        return root;
    }
    if(data < root->data) {
        root->left = build(root->left, data);
        if(h) {
            switch(root->height) {
                case 1:
                    temp1 = root->left;
                    if(temp1->height == 1) {
                        root->left = temp1->right;
                        temp1->right = root;
                        root->height = 0;
                        root = temp1;
                    }
                    else {
                        temp2 = temp1->right;
                        temp1->right = temp2->left;
                        temp2->left = temp1;
                        root->left = temp2->right;
                        temp2->right = root;
                        if(temp2->height == 1) {
                            root->height = -1;
                        }
                        else {
                            root->height = 0;
                        }
                        if(temp2->height == -1) {
                            temp1->height = 1;
                        }
                        else {
                            temp1->height = 0;
                        }
                        root = temp2;
                    }
                    root->height = 0;
                    h = false;
                    break;
                case 0:
                    root->height = 1;
                    break;
                case -1:
                    root->height = 0;
                    h = false;
            }
        }
    }
    if(data > root->data) {
        root->right = build(root->right, data);
        if(h) {
            switch(root->height) {
                case 1:
                    root->height = 0;
                    h = false;
                    break;
                case 0:
                    root->height = -1;
                    break;
                case -1:
                    temp1 = root->right;
                    if(temp1->height == -1) {
                        root->right = temp1->left;
                        temp1->left = root;
                        root->height = 0;
                        root = temp1;
                    }
                    else {
                        temp2 = temp1->left;
                        temp1->left = temp2->right;
                        temp2->right = temp1;
                        root->right = temp2->left;
                        temp2->left = root;
                        if(temp2->height == -1) {
                            root->height = 1;
                        }
                        else {
                            root->height = 0;
                        }
                        if(temp2->height == 1) {
                            temp1->height = -1;
                        }
                        else {
                            temp1->height = 0;
                        }
                        root = temp2;
                    }
                    root->height = 0;
                    h = false;
            }
        }
    }
    return root;
}

bool avl::remove(int data) {
    bool found = false;
    if(root == NULL) {
        return false;
    }
    node* current;
    node* parent;
    current = root;
    while(current) {
        if(current->data == data) {
            found = true;
            break;
        }
        else {
            parent = current;
            if(data > current->data) {
                current = current->right;
            }
            else if(data < current->data) {
                current = current->left;
            }
        }
    }
    if(!found) {
        return false;
    }
    root = del_leaf(root, data);
    return true;
}

node* avl::del_leaf(node* root, int data) {
    node* temp;
    if(root == NULL) {
        return root;
    }
    else {
        if(data < root->data) {
            root->left = del_leaf(root->left, data);
            if(h) {
                root = bal_right(root);
            }
        }
        else {
            if(data > root->data) {
                root->right = del_leaf(root->right, data);
                if(h) {
                    root = bal_left(root);
                }
            }
            else {
                temp = root;
                if(temp->right == NULL) {
                    root = temp->left;
                    h = true;
                    delete temp;
                }
                else {
                    if(temp->left == NULL) {
                        root = temp->right;
                        h = true;
                        delete temp;
                    }
                    else {
                        temp->left = del(temp->left, temp);
                        if(h) {
                            root = bal_right(root);
                        }
                    }
                }
            }
        }
    }
    return root;
}

node* avl::del(node* succ, node* n) {
    node* temp = succ;
    if(succ->right != NULL) {
        succ->right = del(succ->right, n);
        if(h) {
            succ = bal_left(succ);
        }
    }
    else {
        node* current;
        node* parent;
        current = root;
        while(current) {
            if(current->data == succ->data) {
                break;
            }
            else {
                parent = current;
                if(succ->data > current->data) {
                    current = current->right;
                }
                else if(succ->data < current->data) {
                    current = current->left;
                }
            }
        }
        if(root->left->data == succ->data) {
            root->data = succ->data;
            n->right = root->right;
            succ = succ->left;
	    delete temp;
            h = true;
            return succ;
        } 
        else if(root->right->data == succ->data) {
            root->data = succ->data;
            n->right = root->right;
            succ = succ->right;
	    delete temp;
            h = false;
            return succ;
        }
        temp = succ;
        n->data = succ->data;
        succ = succ->left;
        delete temp;
        h = true;
    }
    return succ;
}

node* avl::bal_right(node* root) {
    node* temp1 = NULL;
    node* temp2 = NULL;
    switch(root->height) {
        case 1:
            root->height = 0;
            break;
        case 0:
            root->height = -1;
            h = false;
            break;
        case -1:
            temp1 = root->right;
            if(temp1->height <= 0) {
                root->right = temp1->left;
                temp1->left = root;
                if(temp1->height == 0) {
                    root->height = -1;
                    temp1->height = 1;
                    h = false;
                }
                else {
                    root->height = 0;
                    temp1->height = 0;
                }
                root = temp1;
            }
            else {
                temp2 = temp1->left;
                temp1->left = temp2->right;
                temp2->right = temp1;
                root->right = temp2->left;
                temp2->left = root;
                if(temp2->height == -1) {
                    root->height = 1;
                }
                else {
                    root->height = 0;
                }
                if(temp2->height == 1) {
                    temp1->height = -1;
                }
                else {
                    temp1->height = 0;
                }
                root = temp2;
                temp2->height = 0;
            }
    }
    return root;
}

node* avl::bal_left(node* root) {
    node* temp1 = NULL;
    node* temp2 = NULL;
    switch(root->height) {
        case -1:
            root->height = 0;
            break;
        case 0:
            root->height = 1;
            h = false;
            break;
        case 1:
            temp1 = root->left;
            if(temp1->height >= 0) {
                root->left = temp1->right;
                temp1->right = root;
                if(temp1->height == 0) {
                    root->height = 1;
                    temp1->height = -1;
                    h = false;
                }
                else {
                    root->height = 0;
                    temp1->height = 0;
                }
                root = temp1;
            }
            else {
                temp2 = temp1->right;
                temp1->right = temp2->left;
                temp2->left = temp1;
                root->left = temp2->right;
                temp2->right = root;
                if(temp2->height == 1) {
                    root->height = -1;
                }
                else {
                    root->height = 0;
                }
                if(temp2->height == -1) {
                    temp1->height = 1;
                }
                else {
                    temp1->height = 0;
                }
                root = temp2;
                temp2->height = 0;
            }
    }
    return root;
}

void avl::display(node* root) {
    if(root != NULL) {
        display(root->left);
        cout << root->data << "\t";
        display(root->right);
    }
}
