//
//  PlistDecoder.hpp
//  Meteor
//
//  Created by Cesar Parent on 06/09/2015.
//  Copyright © 2015 Cesar Parent. All rights reserved.
//

#ifndef PlistDecoder_hpp
#define PlistDecoder_hpp

#include "../defines.h"
#include "../CPFoundation.h"
#include "CodingProtocols.h"
#include "Value.h"
#include <map>
#include <string>

namespace tinyxml2 {
    class XMLElement;
}

NS_BEGIN

/**
 * @class           PlistDecoder
 * @ingroup         Filesystem Foundation
 * @brief           An Decoder that can unarchive data from Apple-style XML-based Property Lists
 *                  (`.plist`) files. PlistDecoder fully implements DecoderProtocol.
 *
 *                  You should never need to create a PlistDecoder object. Instead, you should use
 *                  the templated class method decodeObjectFromFile<T>(), that will return
 *                  an object of type `T`.
 */
class PlistDecoder : public DecoderProtocol {
    friend class Game;
public:
    
    PlistDecoder(const std::string& filename);
    
    /**
     * @name        Unarchiving Objects From Files
     * @{
     */
    
    /*!
     * @brief       Unarchives an object from a xml property list file.
     * @note        Unarchiving an object requires that it can be constructed with a DecoderProtocol
     *              implementation. A compile-time error will be thrown otherwise
     * @tparam      T               The type of the object to unarchive.
     * @param       filename        The path of the file to decode from.
     * @return      An object of type `T` decoded from the `plist` file.
     */
    template<typename T> static ARC<T>
    decodeObjectFromFile(const std::string& filename) {
        PlistDecoder coder(filename);
        return alloc<T>(&coder);
    }
    
    /**
     * @}
     * @name        Decoding Values From an Archive
     * @{
     */
    virtual ~PlistDecoder();
    
    /*
     * @brief       Decode a value as a boolean. If there is no boolean value for that key,
     *              false will be returned.
     * @param       key         Key to look for in the archive.
     * @return      Boolean value for the given key.
     */
    virtual bool
    decodeBoolForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].boolValue();
        }
        return false;
    }
    
    /*!
     * @brief       Decode a value as an integer. If there is no integer value for that key,
     *              0 will be returned.
     * @param       key         Key to look for in the archive.
     * @return      Integer value for the given key.
     */
    virtual int
    decodeIntegerForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].integerValue();
        }
        return 0;
    }
    
    /*!
     * @brief       Decode a value as an real (double). If there is no double value for that key,
     *              0 will be returned.
     * @param       key         Key to look for in the archive.
     * @return      Double value for the given key.
     */
    virtual double
    decodeRealForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].realValue();
        }
        return 0;
    }
    
    /*!
     * @brief       Decode a value as a string. If there is no string value for that key,
     *              an empty string will be returned.
     * @param       key         Key to look for in the archive.
     * @return      String value for the given key.
     */
    virtual std::string
    decodeStringForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].stringValue();
        }
        return "";
    }
    
    /*!
     * @brief       Decode a value as an array. If there is no array value for that key,
     *              an empty array will be returned.
     * @param       key         Key to look for in the archive.
     * @return      Array value for the given key.
     */
    virtual std::vector<Value>
    decodeArrayForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].arrayValue();
        }
        return {};
    }
    
    /*!
     * @brief       Decode a value as a dictionary. If there is no dictionary value for that key,
     *              an empty dictionary will be returned.
     * @param       key         Key to look for in the archive.
     * @return      Dictionary value for the given key.
     */
    virtual std::map<std::string, Value>
    decodeDictionaryForKey(const std::string& key) override {
        if(_values.find(key) != _values.end()) {
            return _values[key].dictionaryValue();
        }
        return {};
    }
    
    /*!
     * @brief       Decode a value as a vector. If there is no vector value for that key,
     *              an error will be thrown.
     * @param       key         Key to look for in the archive.
     * @return      Vector value for the given key.
     */
    virtual vec2
    decodeVectorForKey(const std::string& key) override;
    
    /*!
     * @brief       Decode a value as a color. If there is no color value for that key,
     *              an error will be thrown.
     * @param       key         Key to look for in the archive.
     * @return      Color value for the given key.
     */
    virtual Color
    decodeColorForKey(const std::string& key) override;
    
    /*!
     * @brief       Decode a value as a rectangle. If there is no rectangle value for that key,
     *              an error will be thrown.
     * @param       key         Key to look for in the archive.
     * @return      Rectangle value for the given key.
     */
    virtual Rect
    decodeRectForKey(const std::string& key) override;
    
private:
    
    Value
    parsePlistValue(tinyxml2::XMLElement* valueElement);
    
    std::map<std::string, Value> _values;
    
    bool _error;
    
};






NS_END

#endif /* PlistDecoder_hpp */
