#include<stdio.h>

// a list element
struct IntListElem {
    int value; // int value
    IntListElem* next; // pointer to the next element
};

// A list of integers
// Beware: indexing is done starting from 1 as the excercise sheet requests it
class IntList {
public:
    // empty constructor, creates an empty list
    IntList();
    
    // copy constructor, intializes by copying from 'other'
    IntList(IntList& other);

    // assignment operator, removes own elements and copies from 'other'
    IntList& operator=(IntList& other);

    // destructor, removes list content
    ~IntList();

    // returns the number of elements in the list
    int getCount();

    // returns if the list is empty
    bool isEmpty();

    // prints the list
    void print();

    // inserts 'element' in the beginning of the list
    void insert(int element);

    // inserts 'element' AFTER 'position'
    void insert(int element, int position);

    // removes the element AFTER 'position'
    void remove(int position);

    // returns the element AT 'position'
    int getElement(int position);
private:
    // pointer to first element
    IntListElem* first;    
    // element count
    int count;

    // returns the IntListElem AT 'position'
    IntListElem* getListElement(int position);

    // Kopiert die Liste 'other'
    void copy(IntList& other);
};

// creates new empty list
IntList::IntList() {
    first = 0;
    count = 0;
}

// creates a new list by copying an old one
IntList::IntList(IntList& list) {
    copy(list);
}

// Assignment operator
IntList& IntList::operator=(IntList& other) {
    if (this == &other) { // if assigning to the same object
        return *this; // do nothing
    }
    copy(other); // otherwise copy contents
    return *this; 
}

// destroys the list
IntList::~IntList() {
    first = 0; // remove the connection to the list elements
    count = 0; // reset list count
};

// private helper that copies from 'other' list
void IntList::copy(IntList& other) {
    // clear and copy elements
    first = 0;
    count = 0;
    // copy element by element from 'other' to this new list
    for (int i = 1; i <= other.count; i++) {
        insert(other.getElement(i), i-1);
    }
}

// private helper that returns the list element struct AT 'position'
IntListElem* IntList::getListElement(int position) {
    IntListElem* current = first;
    for (int i = 1; i < position; i++) {
        // if list is not long enough
        if (current->next == 0) {
            // return the last element
            return current;
        }
        current = current->next;
    }
    return current;
}

// return element AT 'position'
int IntList::getElement(int position) {
    if (position < 1 || position > count) {
        printf("Error: Index out of Bounds, no element at position %d\n", position);
        return -1;
    }
    return getListElement(position)->value; // return the value of the element
}

// insert 'element' AFTER 'position'
void IntList::insert(int element, int position) {
    if (position < 0 || position > count) { // check for invalid positions
        printf("Error: Index out of Bounds, can't insert.\n");
        return;
    }
    IntListElem* elem = new IntListElem(); // create a new list element
    elem->value = element; // with the given value
    if (position == 0) { // insert in the very beginning
        elem->next = first; // the successor of elem is the old first, now second element
        first = elem; // make the new element the new first
    } else {
        IntListElem* where = getListElement(position); // find the element to insert after
        elem->next = where->next; // make the old successor of where now follow elem
        where->next = elem; // make elem the new successor of where
    }
    count += 1;
    return;
}

// insert 'element' in the very beginning of the list
void IntList::insert(int element) {
    insert(element, 0); // insert AFTER position 0
}

// remove next element AFTER 'position'
void IntList::remove(int position) {
    if (position < 0 || position >= count) { // check for invalid positions
        printf("Error: Index out of bounds, can't remove after %d\n", position);
        return;
    }
    if (position == 0) { // remove first element
        first = first->next; // new first is the new element
    } else {
        IntListElem* where = getListElement(position); // find the element to remove after
        where->next = where->next->next; // remove by skipping the next element of where
    }
    count -= 1; // reduce element count
    return;
}

// check if list is empty
bool IntList::isEmpty() {
    return count == 0;
}

// returns the list's size
int IntList::getCount() {
    return count;
}

// print a list of integers
void IntList::print() {
    printf("["); // opening brackets
    IntListElem* current = first;
    while (current != 0) { // while not last element
        printf("%d", current->value); // print value
        if (current->next != 0) {
            printf(", "); // if not the last element, append a comma
        }
        current = current->next; // go to next
    }
    printf("]\n"); // closing brackets
}

int main() {
    IntList list;
    list.insert(30);
    list.insert(20);
    list.insert(10);
    list.print();

    list.remove(2);
    list.print();

    list.insert(30,2);
    list.print();

    list.insert(40,3);
    list.print();

    IntList copy(list);
    copy.print();

    copy.remove(0);
    copy.print();
    list.print();

    copy = list;
    copy.print();

    return 0;
}
