/************************************************************************
*                                                                       *
*             Star Traders: A Game of Interstellar Trading              *
*                Copyright (C) 1990-2026, John Zaitseff                 *
*                                                                       *
************************************************************************/

/*
  Author: John Zaitseff <J.Zaitseff@zap.org.au>
  SPDX-License-Identifier: GPL-3.0-or-later
  $Id: 49574bbd6445ce38c7d565ca448edd801365445e $

  This file, exch.c, contains the implementation of functions dealing
  with the Interstellar Stock Exchange and Trading Bank as used in Star
  Traders.


  This program is free software: you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation, either version 3 of the License, or (at your
  option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see https://www.gnu.org/licenses/.
*/


#include "trader.h"


/************************************************************************
*                  Module-specific function prototypes                  *
************************************************************************/

/*
  Function:   visit_bank - Visit the Interstellar Trading Bank
  Parameters: (none)
  Returns:    (nothing)

  This function allows the current player to borrow or repay money from
  the Interstellar Trading Bank.
*/
static void visit_bank (void);


/*
  Function:   trade_shares - Buy and sell shares in a particular company
  Parameters: num          - Company with which to trade
              bid_used     - Has the player used up their bid?
  Returns:    (nothing)

  This function allows the current player to buy and sell shares in
  company num.  The variable *bid_used is set to true if the player tries
  to bid for more shares to be released by the company (whether or not
  that bid was successful).
*/
static void trade_shares (int num, bool *bid_used);


/************************************************************************
*                  Stock Exchange function definitions                  *
************************************************************************/

// This function is documented in the file "exch.h"


/***********************************************************************/
// exchange_stock: Visit the Interstellar Stock Exchange

void exchange_stock (void)
{
    selection_t selection = SEL_NONE;
    bool bid_used = false;
    bool all_off_map;
    int w, i, line;


    if (quit_selected || abort_game || ! player[current_player].in_game) {
	return;
    }

    newtxwin(16, WIN_COLS, 1, WCENTER, false, 0);
    w = getmaxx(curwin);

    while (selection != SEL_EXIT) {
	selection = SEL_NONE;

	// Display (or refresh) the Stock Exchange window
	wbkgdset(curwin, attr_normal_window);
	werase(curwin);
	box(curwin, 0, 0);

	center(curwin, 1, 0, attr_title, 0, 0, 1,
	       _("  Interstellar Stock Exchange  "));
	center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
	       _("Player: ^{%ls^}"), player[current_player].name);

	all_off_map = true;
	for (i = 0; i < MAX_COMPANIES; i++) {
	    if (company[i].on_map) {
		all_off_map = false;
		break;
	    }
	}

	if (all_off_map) {
	    center(curwin, 8, 0, attr_normal, attr_highlight, 0, 1,
		   _("No companies on the map"));
	} else {
	    mvwhline(curwin, 4, 2, ' ' | attr_subtitle, w - 4);
	    mvwhline(curwin, 5, 2, ' ' | attr_subtitle, w - 4);

	    left(curwin, 4, 4, attr_subtitle, 0, 0, 2,
		 /* TRANSLATORS: "Company" is a two-line column label in
		    a table containing a list of companies. */
		 pgettext("subtitle", " \nCompany"));
	    right(curwin, 4, w - 4, attr_subtitle, 0, 0, 2,
		  /* TRANSLATORS: "Shares left" is a two-line column
		     label in a table containing the number of shares
		     left to be purchased in any given company.  The
		     maximum column width is 10 characters (see
		     STOCK_LEFT_COLS in src/intf.h). */
		  pgettext("subtitle", "Shares\nleft"));
	    right(curwin, 4, w - 6 - STOCK_LEFT_COLS, attr_subtitle, 0, 0, 2,
		  /* TRANSLATORS: "Shares issued" is a two-line column
		     label in a table containing the number of shares
		     already sold (ie, bought by all players) in any
		     given company.  The maximum column width is 10
		     characters (see STOCK_ISSUED_COLS in src/intf.h). */
		  pgettext("subtitle", "Shares\nissued"));
	    right(curwin, 4, w - 8 - STOCK_LEFT_COLS - STOCK_ISSUED_COLS,
		  attr_subtitle, 0, 0, 2,
		  /* TRANSLATORS: "Return" is a two-line column label in
		     a table containing the share return as a percentage
		     in any given company.  The maximum column width is
		     10 characters (see SHARE_RETURN_COLS in src/intf.h). */
		  pgettext("subtitle", "Return\n(%%)"));
	    right(curwin, 4, w - 10 - STOCK_LEFT_COLS - STOCK_ISSUED_COLS
		  - SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2,
		  /* TRANSLATORS: "Price per share" is a two-line column
		     label in a table containing the price per share in
		     any given company.  %ls is the currency symbol in
		     the current locale.  The maximum column width is 12
		     characters INCLUDING the currency symbol (see
		     SHARE_PRICE_COLS in src/intf.h). */
		  pgettext("subtitle", "Price per\nshare (%ls)"),
		  currency_symbol);

	    for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
		if (company[i].on_map) {
		    left(curwin, line, 2, attr_choice, 0, 0, 1, "%lc",
			 (wint_t) PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i)));
		    left(curwin, line, 4, attr_normal, 0, 0, 1, "%ls",
			 company[i].name);

		    right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%'ld  ",
			  company[i].max_stock - company[i].stock_issued);
		    right(curwin, line, w - 4 - STOCK_LEFT_COLS, attr_normal,
			  0, 0, 1, "%'ld  ", company[i].stock_issued);
		    right(curwin, line, w - 6 - STOCK_LEFT_COLS
			  - STOCK_ISSUED_COLS, attr_normal, 0, 0, 1, "%.2f  ",
			  company[i].share_return * 100.0);
		    right(curwin, line, w - 8 - STOCK_LEFT_COLS
			  - STOCK_ISSUED_COLS - SHARE_RETURN_COLS, attr_normal,
			  0, 0, 1, "  %!N  ", company[i].share_price);

		    line++;
		}
	    }
	}

	wrefresh(curwin);

	// Show menu of choices for the player
	newtxwin(7, WIN_COLS, 17, WCENTER, true, attr_normal_window);

	left(curwin, 3, 2, attr_normal, attr_keycode, 0, 1,
	     _("^{<1>^} Display stock portfolio"));
	left(curwin, 4, 2, attr_normal, attr_keycode, 0, 1,
	     /* TRANSLATORS: Each label may be up to 37 characters wide
		(for <1> and <2>) or 38 characters wide (for <3> and <4>). */
	     _("^{<2>^} Display galaxy map"));
	left(curwin, 3, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
	     _("^{<3>^} Visit the Trading Bank"));
	left(curwin, 4, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
	     _("^{<4>^} Exit the Stock Exchange"));

	center(curwin, 1, -1, attr_normal, attr_keycode, attr_highlight, 1,
	       _("Enter selection [^[Company letter^]/^{1^}-^{4^}]: "));

	curs_set(CURS_ON);
	wrefresh(curwin);

	// Get the actual selection made by the player
	while (selection == SEL_NONE) {
	    wint_t key;

	    if (gettxchar(curwin, &key) == OK) {
		// Ordinary wide character
		bool found;

		if (iswupper(*keycode_company)) {
		    key = towupper(key);
		} else if (iswlower(*keycode_company)) {
		    key = towlower(key);
		}

		for (i = 0, found = false; keycode_company[i] != L'\0'; i++) {
		    if (keycode_company[i] == (wchar_t) key) {
			found = true;
			if (company[i].on_map) {
			    selection = (selection_t) i;
			} else {
			    beep();
			}
			break;
		    }
		}

		if (! found) {
		    switch (key) {
		    case L'1':
			curs_set(CURS_OFF);
			show_status(current_player);
			curs_set(CURS_ON);
			break;

		    case L'2':
			curs_set(CURS_OFF);
			show_map(true);
			curs_set(CURS_ON);
			break;

		    case L'3':
			selection = SEL_BANK;
			break;

		    case L'4':
		    case L' ':
			selection = SEL_EXIT;
			break;

		    default:
			beep();
		    }
		}
	    } else {
		// Function or control key
		switch (key) {
		case KEY_ESC:
		case KEY_CANCEL:
		case KEY_EXIT:
		case KEY_CTRL('C'):
		case KEY_CTRL('G'):
		case KEY_CTRL('\\'):
		    selection = SEL_EXIT;
		    break;

		default:
		    beep();
		}
	    }
	}

	curs_set(CURS_OFF);

	deltxwin();		// "Enter selection" window
	txrefresh();

	if (selection == SEL_BANK) {
	    // Visit the Interstellar Trading Bank
	    visit_bank();
	} else if (selection == SEL_EXIT) {
	    // Exit the Stock Exchange: nothing more to do
	    ;
	} else {
	    trade_shares(selection, &bid_used);
	}
    }

    deltxwin();			// "Stock Exchange" window
    txrefresh();
}


/************************************************************************
*                  Module-specific function prototypes                  *
************************************************************************/

// These functions are documented at the start of this file


/***********************************************************************/
// visit_bank: Visit the Interstellar Trading Bank

void visit_bank (void)
{
    double credit_limit;
    double val, max;
    wint_t key;
    bool done;

    chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
    int x, width;


    credit_limit = (total_value(current_player) - player[current_player].debt)
	* CREDIT_LIMIT_RATE;
    if (credit_limit < 0.0) {
	credit_limit = 0.0;
    }

    // Show the informational part of the Bank
    newtxwin(10, WIN_COLS - 4, 5, WCENTER, true, attr_normal_window);

    center(curwin, 1, 0, attr_title, 0, 0, 1,
	   _("  Interstellar Trading Bank  "));

    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) - 4, &width,
	    1, pgettext("label", "Current cash:  "));
    x = (getmaxx(curwin) + width - (BANK_VALUE_COLS + 2)) / 2;

    rightch(curwin, 3, x, chbuf, 1, &width);
    right(curwin, 3, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
	  1, " ^{%N^} ", player[current_player].cash);

    right(curwin, 4, x, attr_normal, 0, 0, 1,
	  pgettext("label", "Current debt:  "));
    right(curwin, 4, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
	  1, " ^{%N^} ", player[current_player].debt);

    right(curwin, 5, x, attr_normal, 0, 0, 1,
	  pgettext("label", "Interest rate: "));
    right(curwin, 5, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
	  1, " ^{%.2f%%^} ", interest_rate * 100.0);

    right(curwin, 7, x, attr_highlight, 0, 0, 1,
	  /* TRANSLATORS: The "Total value", "Current cash", "Current
	     debt", "Interest rate" and "Credit limit" labels MUST all be
	     the same length (ie, right-padded with spaces as needed) and
	     must have at least one trailing space so that the display
	     routines work correctly.  The maximum length of each label
	     is 36 characters.

	     Note that some of these labels are used for both the Player
	     Status window and the Trading Bank window. */
	  pgettext("label", "Credit limit:  "));
    whline(curwin, ' ' | attr_title, BANK_VALUE_COLS + 2);
    right(curwin, 7, x + BANK_VALUE_COLS + 2, attr_title, 0, 0, 1,
	  " %N ", credit_limit);

    wrefresh(curwin);

    // Show menu of choices for the player
    newtxwin(7, WIN_COLS - 4, 15, WCENTER, true, attr_normal_window);

    center(curwin, 3, 0, attr_normal, attr_keycode, 0, 1,
	   /* TRANSLATORS: The "Borrow money", "Repay debt" and "Exit
	      from the Bank" menu options must all be the same length
	      (ie, padded with trailing spaces as required).  The maximum
	      length is 72 characters. */
	   _("^{<1>^} Borrow money      "));
    center(curwin, 4, 0, attr_normal, attr_keycode, 0, 1,
	   _("^{<2>^} Repay debt        "));
    center(curwin, 5, 0, attr_normal, attr_keycode, 0, 1,
	   _("^{<3>^} Exit from the Bank"));

    center(curwin, 1, 0, attr_normal, attr_keycode, 0, 1,
	   _("Enter selection [^{1^}-^{3^}]: "));

    curs_set(CURS_ON);
    wrefresh(curwin);

    done = false;
    while (! done) {
	if (gettxchar(curwin, &key) == OK) {
	    // Ordinary wide character
	    switch (key) {
	    case L'1':
	    case L'2':
	    case L'3':
		left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
		     0, 0, 1, "%lc", key);
		wrefresh(curwin);

		done = true;
		break;

	    case L' ':
		done = true;
		break;

	    default:
		beep();
	    }
	} else {
	    // Function or control key
	    switch (key) {
	    case KEY_ESC:
	    case KEY_CANCEL:
	    case KEY_EXIT:
	    case KEY_CTRL('C'):
	    case KEY_CTRL('G'):
	    case KEY_CTRL('\\'):
		done = true;
		break;

	    default:
		beep();
	    }
	}
    }

    curs_set(CURS_OFF);

    switch (key) {
    case L'1':
	// Borrow money from the Bank
	if (credit_limit == 0.0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  Insufficient Credit Limit  "),
		     _("The Bank will not lend you any more money."));
	} else {
	    chtype *chbuf_cursym;
	    int width_cursym;
	    int n, ret;


	    wbkgdset(curwin, attr_normal_window);
	    werase(curwin);
	    box(curwin, 0, 0);

	    n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;

	    mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
		    getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
		    currency_symbol);
	    chbuf_cursym = xchstrdup(chbuf);

	    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
		    - BANK_INPUT_COLS - width_cursym - 6, &width, 1,
		    _("How much do you wish to borrow? "));
	    x = (getmaxx(curwin) + width - BANK_INPUT_COLS - width_cursym
		 - n) / 2;
	    rightch(curwin, 3, x, chbuf, 1, &width);

	    // Show the currency symbol before or after the input field
	    if (lconvinfo.p_cs_precedes == 1) {
		leftch(curwin, 3, x, chbuf_cursym, 1, &width_cursym);
		x += width_cursym + n;
	    } else {
		leftch(curwin, 3, x + BANK_INPUT_COLS + n, chbuf_cursym, 1,
		       &width_cursym);
	    }

	    ret = gettxdouble(curwin, &val, 0.0, credit_limit + ROUNDING_AMOUNT,
			      0.0, credit_limit, 3, x, BANK_INPUT_COLS,
			      attr_input_field);

	    if (ret == OK && val > ROUNDING_AMOUNT) {
		player[current_player].cash += val;
		player[current_player].debt += val * (interest_rate + 1.0);
	    }

	    free(chbuf_cursym);
	}
	break;

    case L'2':
	// Repay a debt
	if (player[current_player].debt == 0.0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  No Debt  "),
		     _("You have no debt to repay."));
	} else if (player[current_player].cash == 0.0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  No Cash  "),
		     _("You have no cash with which to repay the debt!"));
	} else {
	    chtype *chbuf_cursym;
	    int width_cursym;
	    int n, ret;


	    wbkgdset(curwin, attr_normal_window);
	    werase(curwin);
	    box(curwin, 0, 0);

	    n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;

	    mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
		    getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
		    currency_symbol);
	    chbuf_cursym = xchstrdup(chbuf);

	    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
		    - BANK_INPUT_COLS - width_cursym - 6, &width, 1,
		    _("How much do you wish to repay? "));
	    x = (getmaxx(curwin) + width - BANK_INPUT_COLS - width_cursym
		 - n) / 2;
	    rightch(curwin, 3, x, chbuf, 1, &width);

	    // Show the currency symbol before or after the input field
	    if (lconvinfo.p_cs_precedes == 1) {
		leftch(curwin, 3, x, chbuf_cursym, 1, &width_cursym);
		x += width_cursym + n;
	    } else {
		leftch(curwin, 3, x + BANK_INPUT_COLS + n, chbuf_cursym, 1,
		       &width_cursym);
	    }

	    max = MIN(player[current_player].cash, player[current_player].debt);

	    ret = gettxdouble(curwin, &val, 0.0, max + ROUNDING_AMOUNT, 0.0,
			      max, 3, x, BANK_INPUT_COLS, attr_input_field);

	    if (ret == OK) {
		player[current_player].cash -= val;
		player[current_player].debt -= val;

		if (player[current_player].cash < ROUNDING_AMOUNT) {
		    player[current_player].cash = 0.0;
		}
		if (player[current_player].debt < ROUNDING_AMOUNT) {
		    player[current_player].debt = 0.0;
		}
	    }

	    free(chbuf_cursym);
	}
	break;

    default:
	break;
    }

    deltxwin();			// "Enter selection" window
    deltxwin();			// Trading Bank window
    txrefresh();

    free(chbuf);
}


/***********************************************************************/
// trade_shares: Buy and sell shares in a particular company

void trade_shares (int num, bool *bid_used)
{
    bool done;
    int ret, w, x, mid;
    long int maxshares, val;
    double ownership;
    chtype *chbuf;
    int width;
    wint_t key;


    assert(num >= 0 && num < MAX_COMPANIES);
    assert(company[num].on_map);

    chbuf = xmalloc(BUFSIZE * sizeof(chtype));

    ownership = (company[num].stock_issued == 0) ? 0.0 :
	((double) player[current_player].stock_owned[num]
	 / company[num].stock_issued);

    // Show the informational part of the trade window
    newtxwin(9, WIN_COLS - 4, 5, WCENTER, true, attr_normal_window);
    w = getmaxx(curwin);

    center(curwin, 1, 0, attr_title, 0, 0, 1,
	   /* TRANSLATORS: %ls represents the company name. */
	   _("  Stock Transaction in %ls  "), company[num].name);

    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w / 2, &width, 1,
	    /* TRANSLATORS: "Shares issued" represents the number of
	       shares already sold by the company to all players.

	       Note that the labels "Shares issued", "Shares left",
	       "Price per share" and "Return" must all be the same length
	       and must have at least one trailing space for the output
	       routines to work correctly.  The maximum length of each
	       label is 22 characters. */
	    pgettext("label|Stock A", "Shares issued:   "));
    leftch(curwin, 3, 2, chbuf, 1, &width);
    right(curwin, 3, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
	  0, 1, "^{%'ld^}", company[num].stock_issued);

    left(curwin, 4, 2, attr_normal, 0, 0, 1,
	 /* TRANSLATORS: "Shares left" is the number of shares that are
	    left to be purchased in the current company. */
	 pgettext("label|Stock A", "Shares left:     "));
    right(curwin, 4, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
	  0, 1, "^{%'ld^}", company[num].max_stock - company[num].stock_issued);

    left(curwin, 5, 2, attr_normal, 0, 0, 1,
	 /* TRANSLATORS: "Price per share" is the cost of each share in
	    the current company. */
	 pgettext("label|Stock A", "Price per share: "));
    right(curwin, 5, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
	  0, 1, "^{%N^}", company[num].share_price);

    left(curwin, 6, 2, attr_normal, 0, 0, 1,
	 /* TRANSLATORS: "Return" is the share return as a percentage. */
	 pgettext("label|Stock A", "Return:          "));
    right(curwin, 6, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
	  0, 1, "^{%.2f%%^}", company[num].share_return * 100.0);

    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w / 2, &width, 1,
	    /* TRANSLATORS: "Current holdings" is the number of shares
	       the current player owns in this particular company.

	       Note that the labels "Current holdings", "Percentage owned"
	       and "Current cash" MUST all be the same length and contain at
	       least one trailing space for the display routines to work
	       correctly.  The maximum length of each label is 18
	       characters. */
	    pgettext("label|Stock B", "Current holdings: "));
    mid = MIN(w / 2, w - width - TRADE_VALUE_COLS - 4);

    leftch(curwin, 3, mid, chbuf, 1, &width);
    right(curwin, 3, w - 2, attr_normal, attr_highlight, 0, 1, " ^{%'ld^} ",
	  player[current_player].stock_owned[num]);

    left(curwin, 4, mid, attr_normal, 0, 0, 1,
	 /* TRANSLATORS: "Percentage owned" is the current player's
	    percentage ownership in this particular company. */
	 pgettext("label|Stock B", "Percentage owned: "));
    right(curwin, 4, w - 2, attr_normal, attr_highlight, 0, 1, " ^{%.2f%%^} ",
	  ownership * 100.0);

    left(curwin, 6, mid, attr_highlight, 0, 0, 1,
	 pgettext("label|Stock B", "Current cash:     "));
    whline(curwin, ' ' | attr_title, TRADE_VALUE_COLS + 2);
    right(curwin, 6, w - 2, attr_title, 0, 0, 1, " %N ",
	  player[current_player].cash);

    wrefresh(curwin);

    // Show menu of choices for the player
    newtxwin(7, WIN_COLS - 4, 14, WCENTER, true, attr_normal_window);

    left(curwin, 3, 2, attr_normal, attr_keycode, 0, 1,
	     /* TRANSLATORS: Each label may be up to 35 characters wide
		(for <1> and <2>) or 36 characters wide (for <3> and <4>). */
	 _("^{<1>^} Buy stock from company"));
    left(curwin, 4, 2, attr_normal, attr_keycode, 0, 1,
	 _("^{<2>^} Sell stock back to company"));
    left(curwin, 3, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
	 _("^{<3>^} Bid company to issue more shares"));
    left(curwin, 4, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
	 _("^{<4>^} Exit to the Stock Exchange"));

    center(curwin, 1, 0, attr_normal, attr_keycode, 0, 1,
	   _("Enter selection [^{1^}-^{4^}]: "));

    curs_set(CURS_ON);
    wrefresh(curwin);

    done = false;
    while (! done) {
	if (gettxchar(curwin, &key) == OK) {
	    // Ordinary wide character
	    switch (key) {
	    case L'1':
	    case L'2':
	    case L'3':
	    case L'4':
		left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
		     0, 0, 1, "%lc", key);
		wrefresh(curwin);

		done = true;
		break;

	    case L' ':
		done = true;
		break;

	    default:
		beep();
	    }
	} else {
	    // Function or control key
	    switch (key) {
	    case KEY_ESC:
	    case KEY_CANCEL:
	    case KEY_EXIT:
	    case KEY_CTRL('C'):
	    case KEY_CTRL('G'):
	    case KEY_CTRL('\\'):
		done = true;
		break;

	    default:
		beep();
	    }
	}
    }

    curs_set(CURS_OFF);

    switch (key) {
    case L'1':
	// Buy stock in company
	maxshares = player[current_player].cash / company[num].share_price;

	if (company[num].max_stock - company[num].stock_issued == 0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  No Shares Available  "),
		     _("No more shares are available for purchase."));
	} else if (maxshares <= 0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  Insufficient Cash  "),
		     _("You do not have enough cash\n"
		       "to purchase additional shares."));
	} else {
	    maxshares = MIN(maxshares, company[num].max_stock -
			    company[num].stock_issued);

	    wbkgdset(curwin, attr_normal_window);
	    werase(curwin);
	    box(curwin, 0, 0);

	    center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
		   ngettext("You can purchase ^{one^} share.",
			    "You can purchase up to ^{%'ld^} shares.",
			    maxshares), maxshares);

	    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1,
		    getmaxx(curwin) - TRADE_INPUT_COLS - 4, &width, 1,
		    _("How many shares do you wish to purchase? "));
	    x = (getmaxx(curwin) + width - TRADE_INPUT_COLS) / 2;
	    rightch(curwin, 4, x, chbuf, 1, &width);

	    ret = gettxlong(curwin, &val, 0, maxshares, 0, maxshares, 4, x,
			    TRADE_INPUT_COLS, attr_input_field);

	    if (ret == OK) {
		player[current_player].cash -= val * company[num].share_price;
		player[current_player].stock_owned[num] += val;
		company[num].stock_issued += val;
	    }
	}
	break;

    case L'2':
	// Sell stock back to company
	maxshares = player[current_player].stock_owned[num];
	if (maxshares == 0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  No Shares  "),
		     _("You do not have any shares to sell."));
	} else {
	    wbkgdset(curwin, attr_normal_window);
	    werase(curwin);
	    box(curwin, 0, 0);

	    center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
		   ngettext("You can sell ^{one^} share.",
			    "You can sell up to ^{%'ld^} shares.",
			    maxshares), maxshares);

	    mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1,
		    getmaxx(curwin) - TRADE_INPUT_COLS - 4, &width, 1,
		    _("How many shares do you wish to sell? "));
	    x = (getmaxx(curwin) + width - TRADE_INPUT_COLS) / 2;
	    rightch(curwin, 4, x, chbuf, 1, &width);

	    ret = gettxlong(curwin, &val, 0, maxshares, 0, maxshares, 4, x,
			    TRADE_INPUT_COLS, attr_input_field);

	    if (ret == OK) {
		company[num].stock_issued -= val;
		player[current_player].stock_owned[num] -= val;
		player[current_player].cash += val * company[num].share_price;
	    }
	}
	break;

    case L'3':
	// Bid company to issue more shares
	maxshares = 0;
	if (! *bid_used && randf() < ownership && randf() < BID_CHANCE) {
	    maxshares = randf() * ownership * MAX_SHARES_BIDDED;
	    company[num].max_stock += maxshares;
	}

	*bid_used = true;

	if (maxshares == 0) {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
		     attr_error_title, attr_error_highlight, 0, 0,
		     attr_error_waitforkey, _("  No Shares Issued  "),
		     /* TRANSLATORS: %ls represents the company name. */
		     _("%ls has refused\nto issue more shares."),
		     company[num].name);
	} else {
	    txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window,
		     attr_title, attr_normal, attr_highlight, 0,
		     attr_waitforkey, _("  Shares Issued  "),
		     /* TRANSLATORS: %ls represents the company name. */
		     ngettext("%ls has issued\n^{one^} more share.",
			      "%ls has issued\n^{%'ld^} more shares.",
			      maxshares), company[num].name, maxshares);
	}
	break;

    default:
	break;
    }

    deltxwin();			// "Enter selection" window
    deltxwin();			// Stock Transaction window
    txrefresh();

    free(chbuf);
}


/***********************************************************************/
// End of file
