#pragma once
#include "acl_cpp/acl_cpp_define.hpp"
#include "acl_cpp/stdlib/pipe_stream.hpp"

namespace acl {
	
class string;

class ACL_CPP_API mime_code : public pipe_stream
{
public:
	/**
	 * 构造函数
	 * @param addCrlf {bool} 非流式编码时是否在末尾添加 "\r\n"
	 * @param addInvalid {bool} 流式解码时是否遇到非法字符是否原样拷贝
	 * @param encoding {const char*} 编码类型标识符
	 */
	mime_code(bool addCrlf, bool addInvalid, const char* encoding_type);
	virtual ~mime_code() = 0;

	/**
	 * 获得编码类型标识符
	 * @return {const char*}
	 */
	const char* get_encoding_type() const
	{
		return encoding_type_;
	}

	/* 流式编码函数，使用方法： encode_update->encode_update->...->encode_finish */

	/**
	 * 编码过程, 添加源数据, 结果存于 out 中, 如果输入的
	 * 数据不满足编码时缓存条件, 则内部仅临时缓存而不做编码
	 * @param src {const char*} 源数据地址
	 * @param n {int} 源数据长度
	 * @param out {string*} 存储编码结果, 可以通过比较
	 *  调用此函数前后的 out->length() 来判断该函数是进行了
	 *  编码过程还是临时缓存过程; 如果使用了 out 中的结果数据，
	 *  则用完后应该调用 out->clear() 清空用过的数据
	 */
	virtual void encode_update(const char *src, int n,
		string* out);

	/**
	 * 编码结束后需要调用此函数来对可能存在于临时缓存中的
	 * 源数据进行最后的编码
	 * @param out {string*} 存储编码结果, 可以通过比较
	 *  调用此函数前后的 out->length() 来判断该函数是进行了
	 *  编码过程还是临时缓存过程; 如果使用了 out 中的结果数据，
	 *  则用完后应该调用 out->clear() 清空用过的数据
	 */
	virtual void encode_finish(string* out);

	/* 流式解码函数，使用方法： decode_update->decode_update->...->decode_finish */

	/**
	 * 解码过程, 添加经过编码的数据, 经本函数后进行解码, 如果
	 * 输入的数据不满足解码时的字节数条件, 则内部仅是临时缓存
	 * 该数据, 等满足解码条件时才进行解码
	 * @param src {const char*} 经过编码后的数据
	 * @param n {int} 数据长度
	 * @param out {string*} 存储解码结果, 可以通过比较
	 *  调用此函数前后的 out->length() 来判断该函数是进行了
	 *  解码过程还是临时缓存过程; 如果使用了 out 中的结果数据，
	 *  则用完后应该调用 out->clear() 清空用过的数据
	 */
	virtual void decode_update(const char *src, int n, string* out);

	/**
	 * 解码结束后需要调用此函数来对可能存在于临时缓存中的
	 * 源数据进行最后的解码
	 * @param out {string*} 存储解码结果, 可以通过比较
	 *  调用此函数前后的 out->length() 来判断该函数是进行了
	 *  解码过程还是临时缓存过程; 如果使用了 out 中的结果数据，
	 *  则用完后应该调用 out->clear() 清空用过的数据
	 */
	virtual void decode_finish(string* out);

	/**
	 * 重置内部缓冲区
	 */
	virtual void reset();

	/**
	 * 在编码过程中设置是否自动在每个编码段添加 "\r\n"
	 * @param on {bool}
	 */
	virtual void add_crlf(bool on);

	/**
	 * 在解码过程中如果遇到非法字符是否将其添加在解码结果中
	 * @param on {bool}
	 */
	virtual void add_invalid(bool on);

	/**
	 * 根据输入的编码表生成相应的解码表
	 * @param toTab {const unsigned char*} 编码表字符串
	 * @param out {string*} 存储结果
	 */
	static void create_decode_tab(const unsigned char *toTab, string *out);

	/**
	 * 如果子类未重载以上虚函数而因此使用基类的以上默认虚函数时
	 * 则子类必须调用此函数设置自己的编码表, 解码表及填充字符
	 * @param toTab {const unsigned char*} 编码表
	 * @param unTab {const unsigned char*} 解码表
	 * @param fillChar {unsigned char} 填充字符
	 */
	void init(const unsigned char* toTab,
		const unsigned char* unTab, unsigned char fillChar);

	/**
	 * 设置转码器的工作状态，因为该转码器由编码器和解码器组成，
	 * 所以在使用 pipe_stream 方式工作时必须指定该转码器的状态
	 * 以指定是处于编码器状态还是解码器状态
	 * @param encoding {bool} 如果为 true 表示为编码器状态，否则
	 *  为解码器状态
	 */
	void set_status(bool encoding = true);

	// pipe_stream 虚函数重载

	virtual int push_pop(const char* in, size_t len,
		string* out, size_t max = 0);
	virtual int pop_end(string* out, size_t max = 0);
	virtual void clear();

	/**
	 * 静态函数，根据编码类型 MIME_ENC_XXX (参见：mime_define.hpp) 获得
	 * 对应的编解码对象，当有当 encoding 类型为 MIME_ENC_QP,
	 * MIME_ENC_BASE64, MIME_ENC_UUCODE, MIME_ENC_XXCODE
	 * @param encoding {int} 编码类型，只能为 MIME_ENC_QP, MIME_ENC_BASE64,
	 *  MIME_ENC_UUCODE, MIME_ENC_XXCODE
	 * @param warn_unsupport {bool} 当未找到匹配的编码对象时是否记录警告信息
	 * @return {mime_code*} 编码对象，当未找到匹配的编码类型时返回 NULL
	 */
	static mime_code* create(int encoding, bool warn_unsupport = true);

private:
	void encode(string* out);
	void decode(string* out);

	char  m_encodeBuf[57];
	int   m_encodeCnt;
	char  m_decodeBuf[76];
	int   m_decodeCnt;
	bool  m_addCrLf;
	bool  m_addInvalid;
	bool  m_encoding;
	const unsigned char *m_toTab;
	const unsigned char *m_unTab;
	unsigned char m_fillChar;
	string* m_pBuf;
	char* encoding_type_;
};

} // namespace acl
