#include "console_output.h"

#include <string>
#include <utility>
#include <ncurses.h>

#include "constants.h"

console_output::console_output(std::ostream &cout): out{cout} {}

/* Attributes
    black        30         40
    red          31         41
    green        32         42
    yellow       33         43
    blue         34         44
    magenta      35         45
    cyan         36         46
    white        37         47
    reset             0  (everything back to normal)
    bold/bright       1  (often a brighter shade of the same colour)
    underline         4
    inverse           7  (swap foreground and background colours)
    bold/bright off  21
    underline off    24
    inverse off      27

   Format:
    \033[X;Ym
    X Y are numbers from above
*/

std::string console_output::get_code(const int attrs) {
        if (attrs < 255)
                return "\033[0m";

        std::string result = "\033[0m\033[";

        if (attrs & A_BOLD)
                result += "1;";

        if (attrs & A_UNDERLINE)
                result += "4;";

        if (attrs & A_STANDOUT)
                result += "7;";

        if ((attrs & COLOR_PAIR(COLOR_WHITE)) == COLOR_PAIR(COLOR_WHITE))
                result += "37;";
        else if ((attrs & COLOR_PAIR(COLOR_CYAN)) == COLOR_PAIR(COLOR_CYAN))
                result += "36;";
        else if ((attrs & COLOR_PAIR(COLOR_MAGENTA)) == COLOR_PAIR(COLOR_MAGENTA))
                result += "35;";
        else if ((attrs & COLOR_PAIR(COLOR_BLUE)) == COLOR_PAIR(COLOR_BLUE))
                result += "34;";
        else if ((attrs & COLOR_PAIR(COLOR_YELLOW)) == COLOR_PAIR(COLOR_YELLOW))
                result += "33;";
        else if ((attrs & COLOR_PAIR(COLOR_RED)) == COLOR_PAIR(COLOR_RED))
                result += "31;";
        else if ((attrs & COLOR_PAIR(COLOR_GREEN)) == COLOR_PAIR(COLOR_GREEN))
                result += "32;";
        else if ((attrs & COLOR_PAIR(COLOR_BLACK_ON_WHITE)) == COLOR_BLACK_ON_WHITE)
                result += "30;47;";

        result[result.length() - 1] = 'm';
        return result;
}

void console_output::render() {
        out << "\x1B[2J\x1B[H";

        for (std::size_t idx = 0; idx < contents.size(); ++idx) {
                if (idx % DISPLAY_WIDTH == 0 && idx)
                        out << std::endl;

                out << get_code(contents[idx])
                    << static_cast<char>(contents[idx] ? contents[idx] : ' ');
        }

        out << std::endl;
}

void console_output::print_char(const position &pos, const char ch,
                                const int attrs) {
        if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
                return;

        contents[pos.y * DISPLAY_WIDTH + pos.x] = attrs | ch;
}

void console_output::print_str(const position &pos, const std::string &str,
                               const int attrs) {
        if (pos.x >= DISPLAY_WIDTH || pos.y >= DISPLAY_HEIGHT)
                return;

        int head = pos.y * DISPLAY_WIDTH + pos.x;

        for (std::size_t i = 0; i < str.length(); ++i)
                contents[i + head] = attrs | str[i];
}
