#pragma once

/****************************************************************************
 *
 * Classes MvLink and MvList - sorted double linked list
 *
 ****************************************************************************/

typedef enum
{
    NoCase,
    LowerCase,
    UpperCase
} ListCase;
typedef enum
{
    SequentialList,
    SortedList
} ListType;
typedef enum
{
    NoSortBy,
    SortListByCode,
    SortListByName
} ListSortBy;
typedef enum
{
    NoSortType,
    SortListSingular,
    SortListMultiple
} ListSortType;

#include <MvElement.hpp>

class MvList;

//
// Class MvLink
//
// This class is used exclusively by the MvList and implement one
// entry of the linked list
//

class MvLink
{
protected:
    friend class MvList;
    MvElement* _self;
    MvLink* _next;
    MvLink* _previous;

public:
    MvLink(MvElement* itself, MvLink* previous = nullptr, MvLink* next = nullptr)
    {
        _self = itself;
        _next = next;
        _previous = previous;
        _self->increment();
    }

    ~MvLink() { _self->decrement(); }
};

//
// Class MvList
//
// This class implements sorted double linked list
//

class MvList
{
protected:
    MvLink* _first;
    MvLink* _last;
    int _n;
    MvLink* _current;
    int _curpos;  // position of current element
    int _protect;

    ListType _list_type;
    ListSortBy _sort_by;
    ListSortType _sort_type;
    ListCase _list_case;

    // Private functions

    void searchInit();

    void insertPrivate(int, MvElement*);
    void insertFirstPrivate(MvElement* v) { insertPrivate(0, v); }
    void insertLastPrivate(MvElement*);

    void sort(int pos, MvLink*);
    int compare(MvLink*, MvLink*);

public:
    MvList(int protect = 0);
    //	Constructor
    //	Input:
    //		deleteElement: if TRUE, MvElement is deleted when MvLink

    MvList(ListType, ListSortBy, ListSortType, ListCase, int protect = 0, MvElement* = 0L);
    //	Constructor of empty list
    //	Input:
    //		type, link type, sort, sorttype and case
    //		deleteElement: if TRUE, MvElement is deleted when MvLink
    //		first element of the list

    ~MvList() { removeAll(); }
    //	Delete list and components

    void removeAll();
    //	The list is emptied and all the contents deleted

    void setListTypes(ListType, ListSortBy, ListSortType, ListCase);

    void print(const char* = nullptr);
    //	Print contents of the list
    //	Input:
    //		header to be printed before the list

    // Inquire Functions

    MvElement* first();
    //	Return:
    //		first element of list and initialize sequential
    //		retrieval

    MvElement* last() { return (_last ? _last->_self : nullptr); }
    //	Return:
    //		last element of list

    MvElement* next();
    //	Return next element of list

    MvElement* previous();
    //	Resturn previous element of list

    MvElement* current() { return (_current ? _current->_self : nullptr); }
    //	Return current element of list

    MvElement* get(int posit);
    //	Input:
    //		position of element, starting from 0

    //	Return:
    //		element in the desired position

    int positionOf(MvElement*);
    //	Input:
    //		element
    //	Return:
    //		position of element starting from 0, if not found -1

    int size() { return _n; }
    //	Return:
    //		List size

    inline ListType getListType() { return _list_type; }
    inline ListSortBy getSortBy() { return _sort_by; }
    inline ListSortType getSortType() { return _sort_type; }
    inline int getListCase() { return _list_case; }
    inline int isEmpty() { return _n == 0; }

    // Search

    MvElement* findByCode(int);
    MvElement* findByName(const char*);
    MvElement* findNearestCode(int);
    MvElement* findNearestName(const char*);

    // Edit List

    void protectElement() { _protect = 1; }
    //	Set protection flag. Does not delete MvElement
    //	when the entry in the list is deleted

    void unprotectElement() { _protect = 1; }
    //	Reset protection flag. Will delete MvElement
    //	when the entry in the list is deleted

    void insert(int, MvElement*);
    //	Put element in a certain position
    //	Input:
    //		position to insert element, starting from 0
    //		pointer to element

    void insertFirst(MvElement* elem) { insert(0, elem); }
    //	Insert element at the beginning of the list
    //	Input:
    //		pointer to element

    void insertLast(MvElement*);
    //	Put element at the end of the List
    //	Input:
    //		element

    void append(MvElement* elem) { insertLast(elem); }

    void insertBeforeCurr(MvElement*);

    void insertAfterCurr(MvElement*);

    void replaceCurr(MvElement*);

    MvElement* insertByType(MvElement*);
    //	Include by Type
    //	May fail if only singular occurrences allowed

    MvElement* extract(int posit);
    // Removes object from the list without destroying it

    MvElement* extract(MvElement* elem) { return extract(positionOf(elem)); }
    // Removes object from the list without destroying it

    int remove(int posit);
    //	Remove element from list. If the element does not
    //	occurr in any other list and the list is not of type
    //	'protected', the element is deleted too.
    //	Input:
    //		position to be deleted
    //	Return:
    //		1 if deleted

    int remove(MvElement* elem) { return remove(positionOf(elem)); }
    //	Remove element from list. If the element does not
    //	occurr in any other list and the list is not of type
    //	'protected', the element is deleted too.
    //	Input:
    //		pointer to element
    //	Return:
    //		1 if deleted

    void removeCurr();
    //	Remove current element

    void removeByCode(int);
    void removeByName(const char*);

    void sort();
    // Utility

    void convertCase(char*);
};
