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

#ifndef PlistCoders_hpp
#define PlistCoders_hpp

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

namespace tinyxml2 {
    class XMLElement;
    class XMLDocument;
}

NS_BEGIN

/**
 * @class           PlistEncoder
 * @ingroup         Filesystem Foundation
 * @brief           An Encoder that can archive data to Apple-style XML-based Property Lists
 *                  (`.plist`) files.
 * @details         PlistEncoder fully implements EncoderProtocol. You should never need to create
 *                  a PlistEncoder object. Instead, you should use the class method
 *                  encodeObjectToFile(), which will handle resources internally.
 */
class PlistEncoder : public EncoderProtocol {
public:
    
    /*!
     * @brief       Create a Property List encoder.
     *
     */
    PlistEncoder() {}
    
    virtual ~PlistEncoder() {}
    
    /**
     * @name        Archiving an object.
     * @{
     */
    
    /*!
     * @brief       Archives an obeject that conforms to <code>CodingProtocol</code>
     * @param       object      Object to archive.
     * @param       file        Path to the encoded file.
     * @return      `true` if the object was successfulyy archived, `false</code` otherwise.
     */
    static bool
    encodeObjectToFile(ARC<CodingProtocol> object, const std::string& file);
    
    /**
     * @}
     * @name        Encoding Values In an Archive
     * @{
     */
    
    /*!
     * @brief       Save a boolean value to the archive.
     * @param       value       Boolean value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeBoolWithKey(bool value, const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save an integer value to the archive.
     * @param       value       Integer value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeIntegerWithKey(int value, const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save a real value to the archive.
     * @param       value       Real (double) value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeRealWithKey(double value, const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save a string value to the archive.
     * @param       value       String value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeStringWithKey(const std::string& value, const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save an array value to the archive.
     * @param       value       Array value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeArrayWithKey(const std::vector<Value>& value, const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save an dictionary value to the archive.
     * @param       value       Dictionary value.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeDictionaryWithKey(const std::map<std::string, Value>& value,
                            const std::string& key) override {
        _values[key] = Value{value};
    }
    
    /*!
     * @brief       Save a vector as an array of its members.
     * @param       value       Vector object.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeVectorWithKey(const vec2& value, const std::string& key) override;
    
    /*!
     * @brief       Save a color as an array of its members.
     * @param       value       Color object.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeColorWithKey(const Color& value, const std::string& key) override;
    
    /*!
     * @brief       Save a rectangle as an array of its members.
     * @param       value       Rect object.
     * @param       key         Key used to tag the value in the archive.
     */
    virtual void
    encodeRectWithKey(const Rect& value, const std::string& key) override;
    
    /**
     * @}
     */
    
private:
    
    bool
    writeFile(const std::string& path);
    
    tinyxml2::XMLElement*
    element(const Value& value,
            tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    boolElement(bool value,
                tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    intElement(int value,
               tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    realElement(double value,
                tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    stringElement(const std::string& value,
                  tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    arrayElement(const std::vector<Value>& value,
                 tinyxml2::XMLDocument& doc);
    
    tinyxml2::XMLElement*
    dictElement(const std::map<std::string, Value>& value,
                tinyxml2::XMLDocument& doc);
    
    std::map<std::string, Value> _values;
};



NS_END

#endif /* PlistCoders_hpp */
