// ---------------------------------------------------------------------------
// - InputTerm.cpp                                                           -
// - standard object library - terminal input stream class definition        -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - 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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2001 amaury darsch                                   -
// ---------------------------------------------------------------------------

#ifndef  ALEPH_INPUTTERM_HPP
#define  ALEPH_INPUTTERM_HPP

#ifndef  ALEPH_INPUT_HPP
#include "Input.hpp"
#endif

// control character generator
#ifndef CTRL
#define CTRL(c) (c - 'a' + 1)
#endif

namespace aleph {

  /// The InputTerm is the terminal input stream. The input terminal can be
  /// used in normal mode (with the read method) or in line editing mode
  /// (with the wread method). Special control characters are returned if they
  /// have a terminal capability definition. Thereset behaves like a normal
  /// input stream.
  /// @author amaury darsch

  class InputTerm : public Input {
  public:
    /// supported wide character code
    static const t_word WIDE_STDBS = CTRL('h');
    static const t_word WIDE_STDBL = CTRL('a');
    static const t_word WIDE_STDEL = CTRL('e');
    static const t_word WIDE_STDKL = CTRL('k');
    static const t_word WIDE_STDKB = CTRL('u');
    static const t_word WIDE_STDNL = CTRL('j');
    static const t_word WIDE_STDEF = CTRL('d');
    static const t_word WIDE_STDRF = CTRL('l');
    static const t_word WIDE_BS    = 0x1000;
    static const t_word WIDE_DEL   = 0x1001;
    static const t_word WIDE_UP    = 0x1002;
    static const t_word WIDE_DO    = 0x1003;
    static const t_word WIDE_LE    = 0x1004;
    static const t_word WIDE_RI    = 0x1005;
    static const t_word WIDE_INSCH = 0x1006;
    static const t_word WIDE_STDUP = 0x1007;
    static const t_word WIDE_STDDO = 0x1008;
    static const t_word WIDE_STDLE = 0x1009;
    static const t_word WIDE_STDRI = 0x100a;
    static const t_word WIDE_STDDE = 0x100b;
    static const t_word WIDE_STDIN = 0x100c;

  private:
    /// the stream id
    int  d_sid;
    /// the terminal data structure
    void* p_attr;
    /// the terminfo array
    char** p_tinfo;
    /// the maximum length in tinfo
    long d_mtlen;

  protected:
    /// the eof flag
    bool d_eof;

  public:
    /// Create a new terminal stream mapped to the standard input.
   InputTerm (void);

    /// destroy this terminal
   ~InputTerm (void);

    /// @return the class name
    String repr (void) const;

    /// @return the stream descriptor
    int getsid (void) const;

    /// @return the next available character
    char read (void);

    /// @return the new available character or a special one
    t_word wread (void);

    /// @return true if we are at the eof
    bool iseof (void) const;

    /// check if we can read a character
    /// @param tout the timeout value
    bool valid (const long tout) const;

    /// @return true if the input stream is a tty
    bool istty (void) const;

    /// save the terminal attributes
    void save (void);

    /// restore the terminal attribute
    void restore (void);

    /// turn the terminal in non canonical mode
    /// @return true if the operation succeeds
    bool nocanon (void);

    /// @return true if we have a wide character
    static bool iswide (const t_word w) {
      if ((w & 0xff00) == 0) return false;
      return true;
    }

    /// @return the base character from a wide character
    static char wtoc (const t_word w) {
      return (char) (0x00ff & w);
    }

    /// @return true if we have a backspace character
    static bool isbs (const t_word w) {
      if (w == InputTerm::WIDE_BS)    return true;
      if (w == InputTerm::WIDE_STDBS) return true;
      return false;
    }

    /// @return true if we have a delete character
    static bool isdel (const t_word w) {
      if (w == InputTerm::WIDE_DEL)   return true;
      if (w == InputTerm::WIDE_STDDE) return true;
      return false;
    }

    /// @return true if we have an insert character
    static bool isins (const t_word w) {
      if (w == InputTerm::WIDE_INSCH) return true;
      if (w == InputTerm::WIDE_STDIN) return true;
      return false;
    }

    /// @return true if we have a arrow up character
    static bool isup (const t_word w) {
      if (w == InputTerm::WIDE_UP)    return true;
      if (w == InputTerm::WIDE_STDUP) return true;
      return false;
    }

    /// @return true if we have a arrow down character
    static bool isdo (const t_word w) {
      if (w == InputTerm::WIDE_DO)    return true;
      if (w == InputTerm::WIDE_STDDO) return true;
      return false;
    }

    /// @return true if we have a arrow left character
    static bool isle (const t_word w) {
      if (w == InputTerm::WIDE_LE)    return true;
      if (w == InputTerm::WIDE_STDLE) return true;
      return false;
    }
    
    /// @return true if we have a arrow right character
    static bool isri (const t_word w) {
      if (w == InputTerm::WIDE_RI)    return true;
      if (w == InputTerm::WIDE_STDRI) return true;
      return false;
    }

    /// @return true if we have a printable character
    static bool isprintable (const t_word w) {
      // accept eof/eol
      if (w == InputTerm::WIDE_STDNL) return true;
      if (w == InputTerm::WIDE_STDEF) return true;
      
      // reject wide characters
      if (InputTerm::iswide (w) == true) return false;
      char c = InputTerm::wtoc (w);
      
      // check for letters or digits
      if ((c >= 'a') && (c<= 'z')) return true;
      if ((c >= 'A') && (c<= 'Z')) return true;
      if ((c >= '0') && (c<= '9')) return true;
      
      // check for special
      if ((c >= '\040') && (c <= '\057')) return true;
      if ((c >= '\072') && (c <= '\100')) return true;
      if ((c >= '\133') && (c <= '\140')) return true;
      if ((c >= '\173') && (c <= '\176')) return true;
      
      // we accept 8 bit characters too
      if ((c & 0x80) != '\0') return true;
      
      // not valid
      return false;
    }

    /// generate a new input term
    /// @param argv the argument vector
    static Object* mknew (Vector* argv);

    /// apply this input term with a set of arguments and a quark
    /// @param robj   robj the current runnable
    /// @param nset   the current nameset    
    /// @param quark  the quark to apply these arguments
    /// @param argv   the arguments to apply
    Object* apply (Runnable* robj, Nameset* nset, const long quark,
		   Vector* argv);

  private:
    // make copy constructor private
    InputTerm (const InputTerm&);
    // make the assignment operator private
    InputTerm& operator = (const InputTerm&);
  };
}

#endif
