#include <iostream>
#include <cctype>

using namespace std;


// throw out an impending new-line so we can get real data that
// follows.  (this new-line is probably from a prior extraction.)
inline void toss_stray_newline(void)
{
    cout.flush();
    if (cin.peek() == '\n')
    {
        cin.ignore();
    }
    return;
}

// reads a non-negative value -- potentially ensuring validity of
// the number.  a generic message is printed when a negative is
// encountered.
short get_non_neg(short & value, bool only_valid = true);

// attempts to force valid numeric entry by ignoring any invalid
// characters in the way.  no message is printed for invalid data.
short get_valid_num(short & value);


// begin prototypes -- that should go in a library (draw.h?)
/****************************************************************/

/*
 *  draw a line with a 'border'.  if HOLLOW, middle length-2
 *  positions are printed as space.  first and last position
 *  are always the symbol.  a newline is always appended.
*/
const bool HOLLOW = true,
           SOLID  = false;
void draw_border_line(short length, char symbol = '*',
                      bool hollow = HOLLOW, char space = ' ');

/*
 *  draw a line segment of length symbol characters.  if this
 *  segment is at the end of the line, a newline is also printed.
*/
void draw_line_segment(short length, char symbol = '*',
                       bool at_end = false);

/*
 *  draw a rectangle height rows by width columns with the specified
 *  border character on the edges and the specified filler character
 *  in the center.
*/
void draw_rectangle(short height, short width, char border = '*',
                    char filler = ' ');

/*
 *  draw a right-isoceles triangle (common side length side) of
 *  the specified symbol.  if the orientation is UL_LR, one of:
 *
 *    **..
 *    *...
 *    ...*
 *    ..**
 *
 *  will be drawn.  the lower-right one will be drawn if we are
 *  told the triangle should be on the RIGHT side of the 'box'.
 *  likewise, if the orientation is UR_LL, one of:
 *
 *    ..**
 *    ...*
 *    *...
 *    **..
 *
 *  will be drawn.  again, the upper-right one will be drawn if
 *  we are told the triangle should be on the RIGHT side of the
 *  'box'.
 *
 *  TO DO:
 *
 *  future versions will allow non-isoceles right triangles,
 *  non-right triangles, and account for the non-square nature
 *  of the console font.  a filled or border-only feature is
 *  also in the works.
*/
const bool RIGHT = true, LEFT = false,
           UL_LR = true, UR_LL = false;
void draw_triangle(short side, char symbol = '*', bool right = RIGHT,
                   bool ul_lr = UL_LR);

/****************************************************************/
// end prototypes -- that should go in a library (draw.h?)


// print menu and read user's response.  choice is forced to caps.
char get_menu_choice(void);

// given a (capital) choice from the menu, do as we're told or
// report an invalid choice.  false is returned unless we're
// told to quit (in which case we return true).
bool process(char choice);

int main(void)
{
    bool done;
    do
    {
        done = process(get_menu_choice());
    } while (!done);
    return 0;
}

// print menu and read user's response.  choice is forced to caps.
char get_menu_choice(void)
{
    char user_entry;
    cout << "\tMain Menu\n\n"
            "1) Rectangles\n"
            "2) Triangles\n"
            "3) Quit\n\n"
            "Choice:  ";
    cin >> user_entry;
    return toupper(user_entry);
}

// handle rectangle and triangle sub-menus.
void rectangle_sub(void);
void triangle_sub(void);

// given a (capital) choice from the menu, do as we're told or
// report an invalid choice.  false is returned unless we're
// told to quit (in which case we return true).
bool process(char choice)
{
    bool quitting = false;
    switch (choice)
    {
        default:
            cout << "\n\n\aInvalid choice!  Please read more"
                    " carefully...\n\n";
        break;
        case '3': case 'Q':
            quitting = true;
        break;
        case '1': case 'R':
            rectangle_sub();
        break;
        case '2': case 'T':
            triangle_sub();
        break;
    }
    return quitting;
}

// rectangle sub-menu helpers..
char get_rectangle_choice(short width, short height, char border,
                          char filler, bool hollow)
{
    char user_entry;
    cout << "\tRectangle Sub-Menu\n\n"
            "1) Draw rectangle\n"
            "2) set Width [" << width << "]\n"
            "3) set Height [" << height << "]\n"
            "4) set Border ['" << border << "']\n"
            "5) set Filler ['" << filler << "']\n"
            "6) toggle Solidness [" << (hollow ? "HOLLOW" : "SOLID")
         << "]\n"
            "7) Return to Main Menu\n\n"
            "Choice:  ";
    cin >> user_entry;
    return toupper(user_entry);
}

bool process_rectangle(char choice, short & width, short & height,
                       char & border, char & filler, char & old_filler)
{
    bool returning = false;
    char t;
    switch (choice)
    {
        default:
            cout << "\n\n\aInvalid choice!  Please read more"
                    " carefully next time...\n\n";
        break;
        case '7': case 'R': case 'M':
            returning = true;
        break;
        case '1': case 'D':
            draw_rectangle(height, width, border, filler);
        break;
        case '2': case 'W':
            cout << "\nEnter new width:  ";
            get_non_neg(width);
            cout << endl;
        break;
        case '3': case 'H':
            cout << "\nEnter new height:  ";
            get_non_neg(height);
            cout << endl;
        break;
        case '4': case 'B':
            cout << "\nEnter new border character:  ";
            toss_stray_newline();
            border = cin.get();
            cout << endl;
        break;
        case '5': case 'F':
            old_filler = filler;
            cout << "\nEnter new filler character:  ";
            toss_stray_newline();
            filler = cin.get();
            cout << endl;
        break;
        case '6': case 'S':
            t = filler;
            filler = (filler == ' ' ? old_filler : ' ');
            old_filler = t;
        break;
    }
    return returning;
}

// handle rectangle sub-menu.  results/inputs as for main menu.
void rectangle_sub(void)
{
    short wide = 5, tall = 3;
    char outside = '*', inside = '*', last_inside = '*';
    bool go_back;
    char entered;
    do
    {
        entered = get_rectangle_choice(wide, tall, outside,
                                       inside, inside==' ');
        go_back = process_rectangle(entered, wide, tall, outside,
                                    inside, last_inside);
    } while (!go_back);
    return;
}

// triangle menu helpers...
char get_triangle_choice(short side, char symbol, bool ul_lr,
                         bool right)
{
    char user_entry;
    cout << "\tTriangle Sub-Menu\n\n"
            "1) Draw Triangle\n"
            "2) set Side Length [" << side << "]\n"
            "3) set Character ['" << symbol << "']\n"
            "4) toggle Orientation ["
         << (ul_lr ? "NEGATIVE" : "POSITIVE") << " AXIS]\n"
            "5) toggle Balance [" << (right ? "RIGHT" : "LEFT")
         << " SIDE]\n"
            "6) Return to Main Menu\n\n"
            "Choice:  ";
    cin >> user_entry;
    return toupper(user_entry);
}

bool process_triangle(char choice, short & side, char & symbol,
                      bool & orientation, bool & balance)
{
    bool returning = false;
    switch (choice)
    {
        default:
            cout << "\n\n\aInvalid choice!  Please read more"
                    " carefully...\n\n";
        break;
        case '6': case 'R': case 'M':
            returning = true;
        break;
        case '1': case 'D': case 'T':
            draw_triangle(side, symbol, balance, orientation);
        break;
        case '2': case 'S': case 'L':
            cout << "\nEnter new side length:  ";
            get_non_neg(side);
            cout << endl;
        break;
        case '3': case 'C':
            cout << "\nEnter new drawing character:  ";
            toss_stray_newline();
            symbol = cin.get();
            cout << endl;
        break;
        case '4': case 'O':
            orientation = !orientation;
        break;
        case '5': case 'B':
            balance = !balance;
        break;
    }
    return returning;
}

// handle triangle sub-menu.  results/inputs as for main menu.
void triangle_sub(void)
{
    short side_length = 5;
    char draw_char = '*';
    bool axis = UL_LR, left_right = RIGHT;
    char entered;
    bool over;
    do
    {
        entered = get_triangle_choice(side_length, draw_char, axis,
                                      left_right);
        over = process_triangle(entered, side_length, draw_char,
                                axis, left_right);
    } while (!over);
    return;
}


// begin definitions -- that should go in a library (draw.C?)
/****************************************************************/

/*
 *  draw a line with a 'border'.  if HOLLOW, middle length-2
 *  positions are printed as space.  first and last position
 *  are always the symbol.  a newline is always appended.
*/
void draw_border_line(short length, char symbol,
                      bool hollow, char space)
{
    cout << symbol;
    draw_line_segment(length-2, (hollow ? space : symbol));
    cout << symbol << endl;
    return;
}

/*
 *  draw a line segment of length symbol characters.  if this
 *  segment is at the end of the line, a newline is also printed.
*/
void draw_line_segment(short length, char symbol, bool at_end)
{
    for (short col = 0; col != length; col++)
    {
        cout << symbol;
    }
    if (at_end)
    {
        cout << endl;
    }
    return;
}

/*
 *  draw a rectangle height rows by width columns with the specified
 *  border character on the edges and the specified filler character
 *  in the center.
*/
void draw_rectangle(short height, short width, char border,
                    char filler)
{
    if (height > 0)
    {
        draw_line_segment(width, border, true);
                             // careful when height is 1 or 2
        for (short row = 1; row < height-1; row++)
        {
            draw_border_line(width, border, filler != border, filler);
        }
        if (height != 1)
        {
            draw_line_segment(width, border, true);
        }
    }
    return;
}

/*
 *  draw a right-isoceles triangle (common side length side) of
 *  the specified symbol.  if the orientation is UL_LR, one of:
 *
 *    **..
 *    *...
 *    ...*
 *    ..**
 *
 *  will be drawn.  the lower-right one will be drawn if we are
 *  told the triangle should be on the RIGHT side of the 'box'.
 *  likewise, if the orientation is UR_LL, one of:
 *
 *    ..**
 *    ...*
 *    *...
 *    **..
 *
 *  will be drawn.  again, the upper-right one will be drawn if
 *  we are told the triangle should be on the RIGHT side of the
 *  'box'.
 *
 *  the 'box' will be side+1 tall and side wide -- the extra row
 *  will be all spaces.
 *
 *  TO DO:
 *
 *  future versions will allow non-isoceles right triangles,
 *  non-right triangles, and account for the non-square nature
 *  of the console font.  a filled or border-only feature is
 *  also in the works.  maybe remove the empty extra row...
*/
void draw_triangle(short side, char symbol, bool right, bool ul_lr)
{
    if (side > 0)
    {
        for (short row = 0; row != side; row++)
        {
            draw_line_segment( (ul_lr ? side-row : row),
                               (right ? ' ' : symbol));
            draw_line_segment( (ul_lr ? row : side-row),
                               (right ? symbol : ' '), true);
        }
        draw_line_segment(side,        // lower gets symbol, else space
                          (right && ul_lr || !right && !ul_lr
                              ? symbol : ' '),
                          true);
    }
    return;
}

/****************************************************************/
// end definitions -- that should go in a library (draw.C?)


// reads a non-negative value -- potentially ensuring validity of
// the number.  a generic message is printed when a negative is
// encountered.
short get_non_neg(short & value, bool only_valid)
{
    if (only_valid)
    {
        get_valid_num(value);
    }
    else
    {
        cin >> value;
    }
    while (value < 0 && !cin.fail())
    {
        cout << "\n\aZero or more, please!\n";
        if (only_valid)
        {
            get_valid_num(value);
        }
        else
        {
            cin >> value;
        }
    }
    return value;
}

// attempts to force valid numeric entry by ignoring any invalid
// characters in the way.  no message is printed for invalid data.
short get_valid_num(short & value)
{
    cin >> value;
    while (cin.fail())
    {
        cin.clear();
        cin.ignore();    // optimistic -- only 1 crap char
        cin >> value;
    }
    return value;
}

