/* This file is part of cardwords
   (c) 1999 Tobias Peters
   see file COPYING for the copyright terms.
   
   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// cardwords_ncursesapp.cc
#include "cardwords_ncursesapp.hh"
#include <unistd.h>
#include <ncurses.h>
#include "cardwords_gettext.hh"
#include <signal.h>

CardWords_NCursesApp * CardWords_NCursesApp::_instance = 0;

int                    CardWords_NCursesApp::sighandler_pipe[2];

CardWords_NCursesApp::CardWords_NCursesApp(CardWords_Selector * selector)
{
  // only one CardWords_NCursesApp allowed:
  assert (instance() == 0);

  if (pipe(sighandler_pipe) < 0) {
    perror (gettext("CardWords_NCursesApp::CardWords_NCursesApp(): "
                    "creating pipe"));
    exit(1);
  }

  _instance = this;
  
  last_tag_in_use = 0;

  currently_marshalling_sigwinch = false;

  this->selector = selector;
  
  // add the SIGWINCH signal handler
  
  signal (SIGWINCH, &SIGWINCH_handler);

  // add the pipe to the selector:
  selector->add(this, sighandler_pipe[0], READ_MODE);
  
  // init ncurses
  initscr ();
  keypad  (stdscr, TRUE);
  nonl    ();
  cbreak  ();
  noecho  ();

  if (has_colors()) {
    start_color();
  }
}
  
CardWords_NCursesApp::~CardWords_NCursesApp()
{
  _instance = 0;
  close (sighandler_pipe[0]);
  close (sighandler_pipe[1]);
  endwin();
}

size_t
CardWords_NCursesApp::get_screen_width() const
{
  int y,x;
  getmaxyx(stdscr, y, x);
  return x;
}
size_t
CardWords_NCursesApp::get_screen_height() const
{
  int y,x;
  getmaxyx(stdscr, y, x);
  return y;
}

bool
CardWords_NCursesApp::has_colors() const
{
  return ::has_colors();
}

// Return a pointer to the CardWords_NCursesApp instance (only one allowed).
// If no instance exists, returns 0.
CardWords_NCursesApp *
CardWords_NCursesApp::instance(void)
{
  return CardWords_NCursesApp::_instance;
}

int //tag
CardWords_NCursesApp::
register_sigwinch_handler(CardWords_NCursesApp::SIGWINCH_Handler new_handler,
                          void * data)
{
  sigwinch_handlers[++last_tag_in_use] =
    pair<SIGWINCH_Handler, void*>(new_handler, data);
  return last_tag_in_use;
}

#ifdef erase // from ncurses
#undef erase
#endif

void
CardWords_NCursesApp::
deregister_sigwinch_handler(int tag)
{
  map<int/*tag*/,pair<SIGWINCH_Handler, void*> >::iterator iter;

  iter = sigwinch_handlers.find(tag);
  if (iter != sigwinch_handlers.end()) {
    if (currently_marshalling_sigwinch
        && iter == next_handler) {
      ++next_handler;
    }
    sigwinch_handlers.erase(iter);
  }
}

// SIGWINCH must be handled by an ncurses app:
void
CardWords_NCursesApp::SIGWINCH_handler(int) __attribute__(__reentrant__)
{
  // We can not call other functions here, this is a sighandler.
  // write one byte to a pipe, and hope the other end of the pipe
  // is listened to by select. It should then handle the changed window
  // size from there.
  char one_byte = 0; // byte to write
  if (write(sighandler_pipe[1], & one_byte, 1) != 1) {
    perror(gettext("CardWords_NCursesApp::SIGWINCH_handler(): "\
                   "unable to write to the pipe"));
    exit(1);
  }
}

void
CardWords_NCursesApp::SIGWINCH_marshaller()
{
  if (currently_marshalling_sigwinch) {
    cerr << gettext("WARNING: CardWords_NCursesApp::SIGWINCH_marshaller() "\
                    "called while already marshalling. Ignoring this.")
         << endl << flush;
    return;
  }
  for (current_handler = sigwinch_handlers.begin();
       current_handler != sigwinch_handlers.end();
       current_handler = next_handler) {
    next_handler = current_handler;
    ++next_handler;
    (*current_handler).second.first((*current_handler).second.second);
  }
}

void
CardWords_NCursesApp::being_selected (int fd, int mode)
{
  if (fd == sighandler_pipe[0]) {
    // There was a SIGWINCH signal
    assert (mode == READ_MODE);
    char buffer [100];
    int bytes_read = read(fd, buffer, 100);

    cout << bytes_read << " times SIGWINCH" << endl;
    SIGWINCH_marshaller();
  }
  else {
    assert (0);
  }
}
    
#ifdef DEBUG_CARDWORDS_NCURSESAPP
int
main(int, char**)
{
  CardWords_Selector * selector = new CardWords_Selector;
  CardWords_NCursesApp * app = new CardWords_NCursesApp(selector);

  selector->run();

  return 0;
}
#endif // DEBUG_CARDWORDS_NCURSESAPP
