#ifndef __AppConfig__
#define __AppConfig__

#include <exception>
#include <string>
#include <sstream>
#include <map>

#include "SharedResource.h"


#ifndef CORE_DLL 
    #ifdef CORE_BUILD
	    #define CORE_DLL __declspec(dllexport)
    #else
	    #define CORE_DLL __declspec(dllimport)
        #pragma comment(lib, "Core.lib")
    #endif
#endif

#pragma warning(push)
#pragma warning(disable:4251)



class CORE_DLL AppConfig : private SharedResource {
public:
    class CORE_DLL InvalidEntryException : public std::exception {
    public:
        virtual const char* what() const throw() {
            return "Section and/or attribute not found in .ini file.";
        }
    };

    ~AppConfig();

    static AppConfig& instance();

    const wchar_t* getFileName() const;


    template <class T> T get(const wchar_t* section, const wchar_t* attribute)
    {
        lock();

        std::map<std::wstring, std::map<std::wstring, std::wstring>>::iterator iSection = _entries.find(section);

        if (iSection == _entries.end()) {
            unlock();
            throw InvalidEntryException();
        }

        std::map<std::wstring, std::wstring>::iterator iAttribute = iSection->second.find(attribute);

        if (iAttribute == iSection->second.end()) {
            unlock();
            throw InvalidEntryException();
        }

        if (typeid(T) == typeid(std::wstring)) {
            std::wstring result = iAttribute->second;
            std::wstring* ptr = &result;
            
            unlock();
            return *(reinterpret_cast<T*>(ptr));
        }
        else {
            std::wstringstream stream;
            stream << iAttribute->second;

            T value;
            stream >> value;
            
            unlock();
            return value;
        }
    }

    template <class T> void set(const wchar_t* section, const wchar_t* attribute, T& value)
    {
        lock();

        std::wstringstream stream;
        stream << value;

        std::map<std::wstring, std::map<std::wstring, std::wstring>>::iterator iSection = _entries.find(section);

        if (iSection == _entries.end()) {
            std::map<std::wstring, std::wstring> newSection;
            newSection.insert(std::pair<std::wstring, std::wstring>(attribute, stream.str()));

            _entries.insert(std::pair<std::wstring, std::map<std::wstring, std::wstring>>(section, newSection));
        }
        else {
            std::map<std::wstring, std::wstring>::iterator iAttribute = iSection->second.find(attribute);

            if (iAttribute == iSection->second.end()) {
                iSection->second.insert(std::pair<std::wstring, std::wstring>(attribute, stream.str()));
            }
            else {
                iAttribute->second = stream.str();
            }
        }

        unlock();
    }

    void flush();

private:
    AppConfig();

    void _readFile(const wchar_t* fileName);
    void _writeFile(const wchar_t* fileName);

    std::wstring _fileName;
    std::map<std::wstring, std::map<std::wstring, std::wstring>> _entries;
};



#pragma warning(pop)


#endif