#ifndef DICTIONARY_H
#define DICTIONARY_H

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <Arduino.h>

template <typename T>
class Dictionary;

namespace DictionaryHelper
{
    //String helper class with c style char array
    class StringEntity
    {
      public:
        StringEntity(const char *string) : _string(NULL)
        {
          if(string == NULL)
          {
            _string = (char *) malloc((sizeof(char)) * 2); //+1 for the string terminating character
            strcpy(_string, "");
          }
          else
          {
            _string = (char *) malloc((strlen(string) * sizeof(char)) + 1); //+1 for the string terminating character
            strcpy(_string, string);
          } 
        }
        StringEntity(const StringEntity &Object)
        {
          _string = (char *) malloc((strlen(Object._string) * sizeof(char)) + 1); //+1 for the string terminating character
          strcpy(_string, Object._string);
        }
        ~StringEntity(){free(_string);}
        char *getString(){return _string;}
        Dictionary<StringEntity> *split(char character);
      private:
      char *_string;
    };
}

template <typename T>
class Dictionary
{
public:
    Dictionary() :_parameter(NULL), _value(NULL), _next(NULL), _head(this){}

    Dictionary(Dictionary const& dictionaryToCopy) //Copy constructor needed because of pointers
    {
        _head = this;
        _next = NULL;

        if(dictionaryToCopy._parameter != NULL)
        {
          _parameter = (char *) malloc((strlen(dictionaryToCopy._parameter) * sizeof(char)) + 1); //+1 for the string terminating character
          _value = new T(*(dictionaryToCopy._value));

          strcpy(_parameter, dictionaryToCopy._parameter);
        }else
        {
          _parameter = NULL;
          _value = NULL;
        }
          
    }

    ~Dictionary()
    {
        if(_head == this)
            dispose();
        free(_parameter);
        //_parameter = NULL; Useless, just my c habits
        delete _value;
        //_value = NULL; //Useless, just my c habits 
    }

    boolean add(const char *parameter, T *value)
    {
        Dictionary *dictionaryNode = new Dictionary(parameter, value);
        return addNewNodeAtTheEnd(dictionaryNode);
    }

    boolean add(int indice, T *value)
    {
        char indiceToStr[10];
        sprintf(indiceToStr,"%d", indice);
        Dictionary *dictionaryNode = new Dictionary(indiceToStr, value);
        return addNewNodeAtTheEnd(dictionaryNode);
    }

    boolean add(const char *parameter, T value)
    {
        Dictionary *dictionaryNode = new Dictionary(parameter, new T(value));
        return addNewNodeAtTheEnd(dictionaryNode);
    }

    boolean add(int indice, T value)
    {
        char indiceToStr[10];
        sprintf(indiceToStr,"%d", indice);
        Dictionary *dictionaryNode = new Dictionary(indiceToStr, new T(value));
        return addNewNodeAtTheEnd(dictionaryNode);
    }

    boolean remove(const char *parameter)
    {
        if(_head->_next == NULL) return false;

        Dictionary *cursor = _head, *toDelete(NULL);

        while(!isListEmpty(cursor->_next))
        {
            if(strcmp(cursor->_next->_parameter, parameter) == 0)
            {
                toDelete = cursor->_next;
                cursor->_next = cursor->_next->_next;

                delete toDelete;
                return true;
            }
            cursor = cursor->_next;
        }
        return false;
    }

    boolean remove(int indice)
    {
      char indiceToStr[10];
      sprintf(indiceToStr,"%d", indice);
      return remove(indiceToStr);
    }

    boolean removeAt(unsigned int position)
    {
        unsigned int pos(0);
        if(_head->_next == NULL) return false;

        Dictionary *cursor = _head, *toDelete(NULL);

        while(!isListEmpty(cursor->_next))
        {
            if(pos++ == position)
            {
                toDelete = cursor->_next;
                cursor->_next = cursor->_next->_next;

                delete toDelete;
                return true;
            }
            cursor = cursor->_next;
        }
        return false;
    }

    T* get(const char *parameter)
    {
        if(parameter == NULL) return NULL;
        if(isListEmpty(_head->_next))return NULL;

        Dictionary *cursor = _head->_next;

        while(!isListEmpty(cursor))
        {
            if(strcmp(cursor->_parameter,parameter) == 0)
                return cursor->_value;
            cursor = cursor->_next;
        }

        return NULL;
    }

    T* operator()(const char *parameter)
    {
        return get(parameter);
    }

    T* get(const unsigned int id)
    {
        char indiceToStr[10];
        sprintf(indiceToStr,"%d", id);
        
        return get(indiceToStr);
    }

    T* operator()(const unsigned int id)
    {
        char indiceToStr[10];
        sprintf(indiceToStr,"%d", id);
        
        return get(indiceToStr);
    }

    T* getAt(const unsigned int position)
    {
        unsigned int pos(0);
        if(isListEmpty(_head->_next))return NULL;

        Dictionary *cursor = _head->_next;

        while(!isListEmpty(cursor))
        {
            if(pos++ == position)
                return cursor->_value;
            cursor = cursor->_next;
        }

        return NULL;
    }

    unsigned int count()
    {
        unsigned int counter(0);
        if(isListEmpty(_head->_next))return counter;

        Dictionary *cursor = _head->_next;
        while(!isListEmpty(cursor))
        {
            counter++;
            cursor = cursor->_next;
        }
        return counter;
    }

    void clear() {this->dispose();}

    void dispose()
    {
        if(isListEmpty(_head->_next))return;

        Dictionary *cursor = _head->_next, *toDelete(NULL);

        while(!isListEmpty(cursor))
        {
            toDelete = cursor;
            cursor = cursor->_next;
            
            delete toDelete;
        }
        _head = this;
        _next = NULL;
    }

    const char *stringValue() const {return _value == NULL ? "" : _value->toString();}
    const char *getParameter(const unsigned int index) 
    {
      unsigned int position(0);
        if(isListEmpty(_head->_next))return "";

        Dictionary *cursor = _head->_next;

        while(!isListEmpty(cursor))
        {
            if(position++ == index)
                return cursor->_parameter;
            cursor = cursor->_next;
        }

        return "";
    }
protected:
    Dictionary(const char *parameter, T *value) : Dictionary()
    {
        //We copy  the parameter and the value
        _parameter = (char *) malloc((strlen(parameter) * sizeof(char)) + 1); //+1 for the string terminating character

        strcpy(_parameter, parameter);
        _value = value;
    }

    boolean addNewNodeAtTheEnd(Dictionary *node)
    {
        if(node == NULL) return false;

        node->_head = _head; //Every node should point to the first node

        if(_next == NULL) //This is our first node then
        {
            _next = node;
            return true;
        }

        // /!\ We have to work with the _next reference in the loop, if we don't it won't work as expected
        Dictionary *cursor = _head;
        while(!isListEmpty(cursor->_next))
        {
            if(strcmp(cursor->_next->_parameter, node->_parameter) == 0)//If we find the same parameter name, we replace it
            {
                delete (cursor->_next->_value);
                cursor->_next->_value = new T(*(node->_value));
                if(cursor->_next->_value == NULL)return false;

                delete node;

                return true;
            }
            cursor = cursor->_next;
        }
        cursor->_next = node;

        return true;
    }
    boolean isListEmpty(Dictionary *node) {return node == NULL;}

    char *_parameter;
    T *_value;
    Dictionary *_next;
    Dictionary *_head;
private:
};



#endif //DICTIONARY_H
