#pragma once
#include <iostream>
#include <string>
#include "chocolat/string/string_aligner.hpp"

/** This utility is used to covert number to hexadecimal string.
 * It used to represent Hexadecimal value and string.
 */
class HexadecimalFormat
{
	unsigned long m_value;
	unsigned m_width;
	char m_filling;
	bool m_upper;
	bool m_is_valid;
public:
	/** get invalid value */
	static const HexadecimalFormat invalid()
    {
        HexadecimalFormat h;
        h.m_is_valid = false;
        return h;
    }

	/** construct from value.*/
	HexadecimalFormat(unsigned long value=0)
            :m_value(value),
             m_width(4),
             m_filling('0'),
             m_upper(true),
             m_is_valid(true)
    {
    }

	bool isValid()const
    {
        return m_is_valid;
    }

	/** construct from string.*/
	HexadecimalFormat(std::string line)
    		:m_width(4),
             m_filling('0'),
             m_upper(true),
             m_is_valid(true)
    {
        unsigned long value=0;
        for (unsigned i=0; i<line.length(); ++i){
            char c = line[i];
            if (isdigit(c)){
                value<<=4;
                value += (c - '0');
            }else if (('a'<= c && c<='f')){
                value<<=4;
                value += (c - 'a') + 10;
            }else if ('A'<= c && c<='F'){
                value<<=4;
                value += (c - 'A') + 10;
            } else if (c == 'x' || c=='X' || c=='$' || c == '#') {
                continue;
            } else {
                break;
            }
        }
        m_value = value;
    }

	bool isAccept(char c)const
    {
        if (isdigit(c)){
            return true;
        }else if (('a'<= c && c<='f')){
            return true;
        }else if ('A'<= c && c<='F'){
            return true;
        } else if (c == 'x' || c=='X' || c=='$' || c == '#') {
            return true;
        } else {
            return false;
        }
    }

	/** copy.*/
	HexadecimalFormat(const HexadecimalFormat& that)
    :m_value(that.m_value),
     m_width(that.m_width),
     m_filling(that.m_filling),
     m_upper(that.m_upper),
     m_is_valid(that.m_is_valid)
    {
    }

	/** downcast in context of unsigned value.*/
	operator unsigned long()const
    {
        return m_value;
    }

	/** assign.*/
	HexadecimalFormat& operator=(const HexadecimalFormat& rh)
    {
        m_value=rh.m_value;
        m_width=rh.m_width;
        m_filling=rh.m_filling;
        return *this;
    }

	/** assign by value.*/
	HexadecimalFormat& operator=(unsigned long value)
    {
        m_value = value;
        return *this;
    }

	/** add the value, ignore format.*/
	HexadecimalFormat operator+(const HexadecimalFormat& rh)const
    {
        return HexadecimalFormat(m_value+rh.m_value);
    }

	/** subtract, ignore format.*/
	HexadecimalFormat operator-(const HexadecimalFormat& rh)const
    {
        return HexadecimalFormat(m_value-rh.m_value);
    }

	/** add value.*/
	HexadecimalFormat& operator+=(unsigned long value)
    {
        m_value += value;
        return *this;
    }

	/** subtract value.*/
	HexadecimalFormat& operator-=(unsigned long value)
    {
        m_value -= value;
        return *this;
    }

	/** set format width.*/
	HexadecimalFormat& width(unsigned w)
    {
        m_width = w;
        return *this;
    }

	/** set format width.*/
	HexadecimalFormat width(unsigned w)const
    {
        HexadecimalFormat result(*this);
        result.m_width = w;
        return result;
    }

	/** 0x00FF <- 0x00ff*/
	HexadecimalFormat& upper(bool is_upper)
    {
        m_upper = is_upper;
        return *this;
    }

	HexadecimalFormat low()const
    {
        HexadecimalFormat v= m_value%256;
        return v.width(2);
    }

	HexadecimalFormat high()const
    {
        HexadecimalFormat v= (m_value/256)%256;
        return v.width(2);
    }

	std::string fillings(unsigned n)const
    {
        std::string result="";
        for (unsigned i=0; i<n;++i){
            result+=m_filling;
        }
        return result;
    }

	std::string to_s()const
    {
        std::string content="";
        for (unsigned long v=m_value; v>0; v/=16){
            char s;
            s = v%16;
            if (s < 10){
                s+='0';
                content = s + content;
            } else {
                s+=(m_upper ? 'A':'a') - 10;
                content = s + content;
            }
        }
        if (m_value == 0){
            content = "0";
        }
        StringAligner right(m_width, StringAligner::RIGHT, m_filling);
        return right(content);
    }

	/** display.*/
	friend std::ostream& operator<<(std::ostream& os,
									const HexadecimalFormat& rh)
    {
        os << rh.to_s();
        return os;
    }

	/** read*/
	friend std::istream& operator>>(std::istream& is,
									HexadecimalFormat& rh)

    {
        std::string word="";
        while (is && rh.isAccept(is.peek())){
            word += is.get();
        }
        rh = HexadecimalFormat(word);
        return is;
    }

};
class BinaryFormat
{
    unsigned m_value;
    unsigned m_width;
    char m_off;
    char m_on;
    unsigned m_length;
    char m_delimiter;
public:
    BinaryFormat()
    {
    }

    BinaryFormat(unsigned value)
    :m_value(value),
     m_width(8),
     m_off('0'),
     m_on('1'),
     m_length(4),
     m_delimiter(' ')
    {
    }

    BinaryFormat(const BinaryFormat& that)
    :m_value(that.m_value),
     m_width(that.m_width),
     m_off(that.m_off),
     m_on(that.m_on),
     m_length(that.m_length),
     m_delimiter(that.m_delimiter)
    {
    }


    bool operator [](unsigned index)const
    {
        unsigned value = 1<<index;
        return m_value & value;
    }
    
    operator unsigned()
    {
        return m_value;
    }

    BinaryFormat width(unsigned w)const
    {
        BinaryFormat that(*this);
        that.m_width=w;
        return that;
    }

    BinaryFormat length(unsigned len)const
    {
        BinaryFormat that(*this);
        that.m_length=len;
        return that;
    }

    void display(std::ostream& os)const
    {
        std::string result;
        unsigned v=m_value;
        for (unsigned i=0; i<m_width; ++i){
            if (i%m_length==0 && i!=0){
                result = m_delimiter+result;
            }
            result = (v%2 == 0 ? m_off:m_on) + result;
            v>>=1;
        }
        os << result;
    }

    friend std::ostream& operator<<(std::ostream& os, const BinaryFormat& rh)
    {
        rh.display(os);
        return os;
    }

};

class DecimalFormat
{
	int m_value;
	unsigned m_width;
	char m_filling;
public:
	DecimalFormat(int value)
    :m_value(value),
     m_width(0),
     m_filling(' ')
    {
    }

	/** construct from string.*/
	DecimalFormat(std::string line)
    		:m_width(0),
             m_filling(' ')
    {
        int value=0;
        int sign = 1;
        for (unsigned i=0; i<line.length(); ++i){
            char c = line[i];
            if (isdigit(c)){
                value *= 10;
                value += (c - '0');
            } else if (i == 0 && c == '-') {
                sign = -1;
                continue;
            } else if (i == 0 && c == '0') {
                m_filling = '0';
                continue;
            } else {
                break;
            }
        }
        m_value = value * sign;
    }

	DecimalFormat(const DecimalFormat& that)
    :m_value(that.m_value),
     m_width(that.m_width),
     m_filling(that.m_filling)
    {
    }

	operator int()
    {
        return m_value;
    }

	DecimalFormat width(unsigned w)const
    {
        DecimalFormat result(*this);
        result.m_width = w;
        return result;
    }

	DecimalFormat length(unsigned len)const;
	void display(std::ostream& os)const
    {
        os << to_s();
    }

	std::string to_s()const
    {
        std::string content="";
        unsigned v=m_value;
        if (m_value == 0){
            content = "0";
        } else {
            if (m_value < 0) {
                content+="-";
                v = -m_value;
            }
            for (; v>0; v/=10){
                char s;
                s = v%10 + '0';
                content = s + content;
            }
        }
        StringAligner right(m_width, StringAligner::RIGHT, m_filling);
        return right(content);
    }

	friend std::ostream& operator<<(std::ostream& os, const DecimalFormat& rh)
    {
        rh.display(os);
        return os;
    }

	
};
