/** \file color.h Color class.
  */
#ifndef FISH_COLOR_H
#define FISH_COLOR_H

#include <stdint.h>
#include <cstddef>
#include "config.h"
#include "common.h"


/* A type that represents a color. We work hard to keep it at a size of 4 bytes. */
class rgb_color_t
{

    /* Types */
    enum
    {
        type_none,
        type_named,
        type_rgb,
        type_normal,
        type_reset,
        type_ignore
    };
    unsigned char type:4;

    /* Flags */
    enum
    {
        flag_bold = 1 << 0,
        flag_underline = 1 << 1
    };
    unsigned char flags:4;

    union
    {
        unsigned char name_idx; //0-10
        unsigned char rgb[3];
    } data;

    /** Try parsing a special color name like "normal" */
    bool try_parse_special(const wcstring &str);

    /** Try parsing an rgb color like "#F0A030" */
    bool try_parse_rgb(const wcstring &str);

    /** Try parsing an explicit color name like "magenta" */
    bool try_parse_named(const wcstring &str);

    /* Parsing entry point */
    void parse(const wcstring &str);

    /** Private constructor */
    explicit rgb_color_t(unsigned char t, unsigned char i=0);

public:

    /** Default constructor of type none */
    explicit rgb_color_t() : type(type_none), flags(), data() {}

    /** Parse a color from a string */
    explicit rgb_color_t(const wcstring &str);
    explicit rgb_color_t(const std::string &str);

    /** Returns white */
    static rgb_color_t white();

    /** Returns black */
    static rgb_color_t black();

    /** Returns the reset special color */
    static rgb_color_t reset();

    /** Returns the normal special color */
    static rgb_color_t normal();

    /** Returns the ignore special color */
    static rgb_color_t ignore();

    /** Returns the none special color */
    static rgb_color_t none();

    /** Returns whether the color is the ignore special color */
    bool is_ignore(void) const
    {
        return type == type_ignore;
    }

    /** Returns whether the color is the normal special color */
    bool is_normal(void) const
    {
        return type == type_normal;
    }

    /** Returns whether the color is the reset special color */
    bool is_reset(void) const
    {
        return type == type_reset;
    }

    /** Returns whether the color is the none special color */
    bool is_none(void) const
    {
        return type == type_none;
    }

    /** Returns whether the color is a named color (like "magenta") */
    bool is_named(void) const
    {
        return type == type_named;
    }

    /** Returns whether the color is specified via RGB components */
    bool is_rgb(void) const
    {
        return type == type_rgb;
    }

    /** Returns whether the color is special, that is, not rgb or named */
    bool is_special(void) const
    {
        return type != type_named && type != type_rgb;
    }

    /** Returns a description of the color */
    wcstring description() const;

    /** Returns the name index for the given color. Requires that the color be named or RGB. */
    unsigned char to_name_index() const;

    /** Returns the term256 index for the given color. Requires that the color be named or RGB. */
    unsigned char to_term256_index() const;

    /** Returns whether the color is bold */
    bool is_bold() const
    {
        return flags & flag_bold;
    }

    /** Set whether the color is bold */
    void set_bold(bool x)
    {
        if (x) flags |= flag_bold;
        else flags &= ~flag_bold;
    }

    /** Returns whether the color is underlined */
    bool is_underline() const
    {
        return !!(flags & flag_underline);
    }

    /** Set whether the color is underlined */
    void set_underline(bool x)
    {
        if (x) flags |= flag_underline;
        else flags &= ~flag_underline;
    }

    /** Compare two colors for equality */
    bool operator==(const rgb_color_t &other) const
    {
        return type == other.type && ! memcmp(&data, &other.data, sizeof data);
    }

    /** Compare two colors for inequality */
    bool operator!=(const rgb_color_t &other) const
    {
        return !(*this == other);
    }
};

#endif