/**
 * Processing code
 * (C) Nettle developers 2000-2003
 *
 * $Id: process,v 1.91 2004/02/01 09:50:26 archifishal Exp $
 */

#include <stddef.h>

#include "generic.h"
#include "globals.h"

#include "graphics.h"
#include "keyboard.h"
#include "lineedit.h"
#include "main.h"
#include "messages.h"
#include "misc.h"
#include "mouse.h"
#include "nettle.h"
#include "process.h"
#include "seln.h"
#include "socket.h"
#include "spool.h"
#include "wimp.h"
#include "wimputil.h"
#include "zapredraw.h"

static struct session_struct *changedsession;
static struct coordspair changedbox;
char csi_sequence[8];

void changedbox_init (struct session_struct *session)
{
  changedsession = session;
  changedbox.tl.x = 255;
  changedbox.tl.y = 255;
  changedbox.br.x = 0;
  changedbox.br.y = 0;
}

void changedbox_update (int cx0, int cy0, int cx1, int cy1)
{
  if (cx0 < changedbox.tl.x) changedbox.tl.x = cx0;
  if (cy0 < changedbox.tl.y) changedbox.tl.y = cy0;
  if (cx1 > changedbox.br.x) changedbox.br.x = cx1;
  if (cy1 > changedbox.br.y) changedbox.br.y = cy1;
}


void changedbox_update_char (int cx0, int cy0)
{
  if (cx0 < changedbox.tl.x) changedbox.tl.x = cx0;
  if (cy0 < changedbox.tl.y) changedbox.tl.y = cy0;
  if (cx0 > changedbox.br.x) changedbox.br.x = cx0;
  if (cy0 > changedbox.br.y) changedbox.br.y = cy0;
}


void changedbox_update_offset (int calc)
{
  int x = calc % changedsession->terminal_size.x;
  int y = calc / changedsession->terminal_size.x;
  changedbox_update_char (x, y);
}


void changedbox_update_whole (void)
{
  changedbox_update (0, changedsession->scrollback,
                    changedsession->terminal_size.x - 1,
                    changedsession->scrollback + changedsession->terminal_size.y - 1);
}


void force_redraw_changedbox (void)
{
  force_redraw (changedsession->window_handle,
                changedbox.tl.x * redraw.r_charw << eig.x,
                (-changedbox.br.y - 1) * redraw.r_charh << eig.y,
                (changedbox.br.x + 1) * redraw.r_charw << eig.x,
                -changedbox.tl.y * redraw.r_charh << eig.y);
}


void primary_da_request(struct session_struct *session)
{
  /* Primary DA Request */
  char block[16];

  switch (session->terminal_type)
  {
    case NETTLE_TERMINAL_VT100:
      sprintf(block, "%s?1;0c",csi_sequence);
      break;
    case NETTLE_TERMINAL_VT102:
      sprintf(block, "%s?6c",csi_sequence);
      break;
    default:
      sprintf(block,"%s?6x;1;6c",csi_sequence);
      block[2+strlen(csi_sequence)]=session->terminal_mode+48;
      break;
  }

  nettle_senddata(session,block,strlen(block));
}

void shuffle_up(struct session_struct *session, int start, int end)
{
  struct term_char *wrapped_line;
  char wrapped_line_flags=0;
  int loop;

  /* copy the wrapped line to a temporary buffer */
  wrapped_line=session->assigned_area[start];

  if (session->line_flags)
  {
    wrapped_line_flags=session->line_flags[start];
  }

  for (loop=start+1; loop<end+1; loop++)
  {
    if (valid_y(session, loop-1) && valid_y(session, loop))
    {
      session->assigned_area[loop-1]=session->assigned_area[loop];
      if (session->line_flags)
        session->line_flags[loop-1]=session->line_flags[loop];
    }
  }

  session->assigned_area[end]=wrapped_line;

  if (session->line_flags)
  {
    session->line_flags[end]=wrapped_line_flags;
  }
}

void shuffle_down(struct session_struct *session, int start, int end)
{
  struct term_char *wrapped_line;
  char wrapped_line_flags=0;
  int loop;

  wrapped_line=session->assigned_area[end];

  if (session->line_flags)
  {
    wrapped_line_flags=session->line_flags[end];
  }

  for (loop=end; loop>start; loop--)
  {
    if (valid_y(session, loop) && valid_y(session, loop-1))
    {
      session->assigned_area[loop]=session->assigned_area[loop-1];
      if (session->line_flags)
        session->line_flags[loop]=session->line_flags[loop-1];
    }
  }

  session->assigned_area[start]=wrapped_line;

  if (session->line_flags)
  {
    session->line_flags[start]=wrapped_line_flags;
  }
}

void scroll_up(struct session_struct *session, bool wrap)
{
  if (selection_session == session)
  {
    selection_start-=session->terminal_size.x;
    selection_end-=session->terminal_size.x;
    if (selection_start<0)
      selection_start=0;
    if (selection_end<0)
      selection_end=0;
  }

  if (session->scroll_start==1 &&
      (session->other_session_flags & NETTLE_OTHER_SCREEN_MODE) == 0)
  {
    shuffle_up(session, 0, session->scrollback+session->scroll_end-1);
  }
  else
  {
    shuffle_up(session, session->scrollback+session->scroll_start-1,
                        session->scrollback+session->scroll_end-1);
  }

  /* and clear or restore the new line */
  if (!wrap)
  {
    /* wrap is false, clear the wrapped line */
    int count;

    for (count=0; count<session->terminal_size.x; count++)
    {
      write_assigned(session,
                     count,
                     session->scroll_end+session->scrollback-1,
                     7, 0, 0, 32);
    }

    session->line_flags[session->scroll_end+session->scrollback-1]=0;
  }

  if (session->scroll_start == 1 &&
      (session->other_session_flags & NETTLE_OTHER_SCREEN_MODE) == 0)
    changedbox_update (0, 0,
                       session->terminal_size.x - 1,
                       session->scrollback + session->scroll_end - 1);
  else
    changedbox_update (0, session->scrollback + session->scroll_start - 1,
                       session->terminal_size.x - 1,
                       session->scrollback + session->scroll_end - 1);
}

void scroll_down (struct session_struct *session, bool wrap)
{
  if (selection_session == session)
  {
    selection_start+=session->terminal_size.x;
    selection_end+=session->terminal_size.x;
    if (selection_start>(session->terminal_size.x*
			 (session->terminal_size.y+session->scrollback)))
    {
        selection_start=(session->terminal_size.x*
			 (session->terminal_size.y+session->scrollback));
    }

    if (selection_end>(session->terminal_size.x*
		       (session->terminal_size.y+session->scrollback)))
    {
      selection_end=(session->terminal_size.x*
		     (session->terminal_size.y+session->scrollback));
    }
  }

  shuffle_down(session, session->scrollback+session->scroll_start-1,
                        session->scrollback+session->scroll_end-1);

  /* and clear or restore the new line */
  if (!wrap)
  {
    /* wrap is false, clear the wrapped line */
    int count;

    for (count=0; count<session->terminal_size.x; count++)
    {
      write_assigned(session,
                     count,
                     session->scroll_start+session->scrollback-1,
                     7, 0, 0, 32);
    }

    session->line_flags[session->scroll_start+session->scrollback-1]=0;
  }


  changedbox_update (0, session->scrollback + session->scroll_start - 1,
                     session->terminal_size.x - 1,
                     session->scrollback + session->scroll_end - 1);
}

void reprocess_position_scroll(struct session_struct *session)
{
  /* scroll if y>scroll_end */

  /*while (session->pos.y>(session->scroll_end+session->scrollback-1))*/
  if (session->pos.y==session->scroll_end+session->scrollback)
  {
    session->pos.y--;
    scroll_up (session, false);
  }

  /* scroll if Y<scroll_start */

  /*while (session->pos.y<(session->scroll_start+session->scrollback-1))*/
  if (session->pos.y==session->scroll_start+session->scrollback-2)
  {
    session->pos.y++;
    scroll_down (session, false);
  }
}


void reprocess_position(struct session_struct *session)
{
  /* this reshuffles everything so that everything is back on screen, this allows the cursor */
  /* to sit at  (81,24) without causing a newline (which would break pine and things) */
  /* It also shuffles up the selection areas */

  if (session->pos.x >= session->terminal_size.x)
  {
    if (session->other_session_flags & NETTLE_OTHER_WRAP_MODE)
    {
      session->pos.x=0;
      session->line_flags[session->pos.y]=NETTLE_LINE_WRAP;
      session->pos.y++;
      reprocess_position_scroll(session);
    }
    else
    {
      session->pos.x = session->terminal_size.x - 1;
    }
  }
}


void row_shift_right (struct session_struct *session, int chars)
{
  int loop;

  /* don't do too much */
  if (chars > session->terminal_size.x - session->pos.x)
    chars = session->terminal_size.x - session->pos.x;

  /* do nothing if cursor is at/beyond RHS */
  if (session->pos.x >= session->terminal_size.x)
    return;

  changedbox_update (session->pos.x, session->pos.y,
                     session->terminal_size.x - 1, session->pos.y);

  for (loop=session->terminal_size.x-1; loop>session->pos.x+chars-1; loop--)
  {
    session->assigned_area[session->pos.y][loop]=
             session->assigned_area[session->pos.y][loop-chars];
  }
}

void work_out_character_character_set(struct session_struct *session,int character_set,
                                             int xpos, int ypos, unsigned char byte)
{
  switch (character_set)
  {
    case NETTLE_CHSET_ASCII:
      write_assigned_character(session, xpos, ypos, byte);
      break;
    case NETTLE_CHSET_DEC_SUPPLEMENTAL_GRAPHICS:
      write_assigned_character(session, xpos, ypos, byte | 0x80);
      break;
    case NETTLE_CHSET_UK_NATIONAL:
      if (byte!='#')
      {
        write_assigned_character(session, xpos, ypos, byte);
      }
      else
      {
        write_assigned_character(session, xpos, ypos, '');
      }
      break;
    case NETTLE_CHSET_DEC_SPECIAL_GRAPHICS:
      {
        int graphics_byte=dec_graphics_table[byte];

        if (graphics_byte<0x100)
        {
          write_assigned_character(session, xpos, ypos, (char) graphics_byte);
        }
        else
        {
          write_assigned_flags(session, xpos, ypos,
                               read_assigned_flags(session, xpos, ypos) | NETTLE_FLAG_PLUS100);
          write_assigned_character(session, xpos, ypos, (char) graphics_byte-0x100);
        }
      }
      break;
    case NETTLE_CHSET_DOWNLINE_LOADABLE:
      write_assigned_character(session, xpos, ypos, byte);
      break;
  }
}

void work_out_character(struct session_struct *session, int xpos, int ypos,
                               unsigned char byte)
{
  int character_set_gl;
  int character_set_gr;

  switch (session->character_set_gl)
  {
    case NETTLE_CHSET_G0:
    default:
      character_set_gl=session->character_set_g0;
      break;

    case NETTLE_CHSET_G1:
      character_set_gl=session->character_set_g1;
      break;

    case NETTLE_CHSET_G2:
      character_set_gl=session->character_set_g2;
      break;

    case NETTLE_CHSET_G3:
      character_set_gl=session->character_set_g3;
      break;
  }

  switch (session->character_set_gr)
  {
    case NETTLE_CHSET_G0:
    default:
      character_set_gr=session->character_set_g0;
      break;

    case NETTLE_CHSET_G1:
      character_set_gr=session->character_set_g1;
      break;

    case NETTLE_CHSET_G2:
      character_set_gr=session->character_set_g2;
      break;

    case NETTLE_CHSET_G3:
      character_set_gr=session->character_set_g3;
      break;
  }

  if (byte<0x80)
  {
    work_out_character_character_set(session, character_set_gl, xpos, ypos, byte);
  }
  else
  {
    work_out_character_character_set(session, character_set_gr, xpos, ypos, byte & 0x7f);
  }
}


void byte_null(struct session_struct *session)
{
  NOT_USED(session);
}

void byte_enquiry(struct session_struct *session)
{
  NOT_USED(session);
}

void byte_bell(struct session_struct *session)
{
  NOT_USED(session);

  /* Bell */
  _swi(OS_WriteI + 7, 0);
}

void byte_backspace(struct session_struct *session)
{
  /* Backspace */
  if (session->pos.x>0)
    session->pos.x--;
}

void byte_horizontal_tabulation(struct session_struct *session)
{
  /* Tab */
  int loop;
  int current_tab=session->terminal_size.x;

  for (loop=0; loop<session->number_of_tabs; loop++)
  {
    if (session->tabs[loop]>session->pos.x &&
        session->tabs[loop]<current_tab)
    {
      current_tab=session->tabs[loop];
    }
  }

  if (current_tab<session->terminal_size.x)
  {
    session->pos.x=current_tab;
  }
  else
  {
    session->pos.x=session->terminal_size.x - 1;
  }
}

void byte_reverse_horizontal_tabulation(struct session_struct *session)
{
  /* Tab */
  int loop=session->number_of_tabs-1;

  while (loop>0 && session->tabs[loop]>session->pos.x)
  {
    loop--;
  }

  session->pos.x = session->tabs[loop];
}

void byte_linefeed(struct session_struct *session)
{
  /* LF,VT,FF */
  bool are_we_in_scroll_area=false;

  if (session->pos.y>=session->scroll_start+session->scrollback-1 &&
      session->pos.y<=session->scroll_end+session->scrollback-1)
  {
    are_we_in_scroll_area=true;
  }

  session->pos.y++;

  if (session->other_session_flags & NETTLE_OTHER_LINEFEED_MODE)
  {
    session->pos.x=0;
  }

  if (are_we_in_scroll_area)
  {
    /*reprocess_position(session);*/
    reprocess_position_scroll(session);
  }
}

void byte_carriage_return(struct session_struct *session)
{
  /* CR */
  session->pos.x=0;
}

void byte_shift_out(struct session_struct *session)
{
  session->character_set_gl=NETTLE_CHSET_G1;
}

void byte_shift_in(struct session_struct *session)
{
  session->character_set_gl=NETTLE_CHSET_G0;
}

void byte_device_control_1(struct session_struct *session)
{
  NOT_USED(session);
}

void byte_device_control_3(struct session_struct *session)
{
  NOT_USED(session);
}

void byte_cancel(struct session_struct *session)
{
  write_assigned(session, session->pos.x, session->pos.y,
                 session->current_fg,
  		 session->current_bg,
  		 session->current_flags,
  		 '?');

  session->escape_state=NETTLE_ESCAPE_NONE;
  session->escape_string_length=0;
}

void byte_substitute(struct session_struct *session)
{
  session->escape_state=NETTLE_ESCAPE_NONE;
  session->escape_string_length=0;
}

void byte_escape(struct session_struct *session)
{
  /* ESCAPE */
  if (session->escape_state==NETTLE_ESCAPE_ESCAPE)
  {
    if (session->escape_string_length>=2)
    {
      if (session->escape_string[0]=='\x1B')
      {
        switch (session->escape_string[1])
        {
          case 'P': case '^': case '_': case ']':
            return;
            break;
        }
      }
    }
  }

  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string_length=1;
}

void byte_delete(struct session_struct *session)
{
  NOT_USED(session);
}

void byte_index(struct session_struct *session)
{
  /* Index */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='D';
  session->escape_string_length=2;
}

void byte_next_line(struct session_struct *session)
{
  /* Next line */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='E';
  session->escape_string_length=2;
}

void byte_horizontal_tab_set(struct session_struct *session)
{
  /* Horizontal tab set */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='H';
  session->escape_string_length=2;
}

void byte_reverse_index(struct session_struct *session)
{
  /* Reverse index */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='M';
  session->escape_string_length=2;
}

void byte_single_shift_g2(struct session_struct *session)
{
  /* Single shift G2 */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='N';
  session->escape_string_length=2;
}

void byte_single_shift_g3(struct session_struct *session)
{
  /* Single shift G3 */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='O';
  session->escape_string_length=2;
}

void byte_device_control_string(struct session_struct *session)
{
  /* Device control string */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='P';
  session->escape_string_length=2;
}

void byte_control_sequence_intro(struct session_struct *session)
{
  /* Control sequence introducer */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\x1B';
  session->escape_string[1]='[';
  session->escape_string_length=2;
}

void byte_string_terminator(struct session_struct *session)
{
  /* String terminator */
  if (session->escape_state==NETTLE_ESCAPE_ESCAPE)
  {
    if (session->escape_string_length>=2)
    {
      if (session->escape_string[0]=='\x1B')
      {
        if (session->escape_string[1]=='[')
        {
          session->escape_string[session->escape_string_length++]='\x1B';
          session->escape_string[session->escape_string_length++]='\\';
        }
      }
    }
  }
}


void byte_telnet(struct session_struct *session)
{
  /* Telnet negotiation */
  session->escape_state=NETTLE_ESCAPE_ESCAPE;
  session->escape_string[0]='\xFF';
  session->escape_string_length=1;
}

void byte_default(struct session_struct *session, char byte)
{
  if (session->escape_state==NETTLE_ESCAPE_NONE)
  {
    if (session->spool_file_name!=NULL && !(session->spool_raw))
    {
      spool_byte(session, byte);
    }

    reprocess_position(session);

    if (session->other_session_flags & NETTLE_OTHER_INSERT_MODE)
    {
      /* If we're inserting a character, then shuffle all the characters along a byte */
      row_shift_right (session, 1);
    }

    changedbox_update_char (session->pos.x, session->pos.y);

    /* write out the data */
    write_assigned(session, session->pos.x, session->pos.y,
                   session->current_fg,
                   session->current_bg,
                   session->current_flags,
                   '?');

    work_out_character(session, session->pos.x, session->pos.y, byte);

    session->pos.x++;

    /* If we have a login handler, call it */
    if (session->login_data)
      session->login_data(session,byte);
  }
}

void clear_screen(struct session_struct *session, int calc, int calc2, char fg, char bg,
                  char flags, char character)
{
  int loop;

  /* clear the specified area with the specified fg,bg,flags and character */
  for (loop=calc; loop<calc2; loop++)
  {
    write_assigned(session,
                   loop % session->terminal_size.x,
                   loop / session->terminal_size.x,
                   fg, bg, flags, character);

    if (loop % session->terminal_size.x==0)
    {
      if (loop>calc && ((loop / session->terminal_size.x)-1)>=0)
      {
        session->line_flags[(loop / session->terminal_size.x)-1]=0;
      }
    }
  }
}

void clear_alt_screen(struct session_struct *session, int calc, int calc2, char fg,
                             char bg,char flags, char character)
{
  int loop;

  /* clear the specified area with the specified fg,bg,flags and character */
  for (loop=calc; loop<calc2; loop++)
  {
    write_alt_assigned(session,
                      loop % session->terminal_size.x,
                      loop / session->terminal_size.x,
                      fg, bg, flags, character);

    if (loop % session->terminal_size.x==0)
    {
      if (loop>calc && ((loop / session->terminal_size.x)-1)>=0)
      {
        session->line_flags[(loop / session->terminal_size.x)-1]=0;
      }
    }
  }
}

void snap_cursor_to_terminal(struct session_struct *session)
{
  /* snaps the cursor inside the terminal */
  if (session->pos.x<0)
  {
    session->pos.x=0;
  }
  if (session->pos.x>=session->terminal_size.x)
  {
    session->pos.x=session->terminal_size.x-1;
  }

  if (session->other_session_flags & NETTLE_OTHER_ORIGIN_MODE)
  {
    if (session->pos.y<session->scroll_start+session->scrollback-1)
    {
      session->pos.y=session->scroll_start+session->scrollback-1;
    }
    if (session->pos.y>session->scroll_end+session->scrollback-1)
    {
      session->pos.y=session->scroll_end+session->scrollback-1;
    }
  }
  else
  {
    if (session->pos.y<session->scrollback)
    {
      session->pos.y=session->scrollback;
    }
    if (session->pos.y>session->terminal_size.y+session->scrollback-1)
    {
      session->pos.y=session->terminal_size.y+session->scrollback-1;
    }
  }
}

void save_cursor_position (struct session_struct *session)
{
  session->old_pos.x=session->pos.x;
  session->old_pos.y=session->pos.y-session->scrollback;
  session->old_current_flags=session->current_flags;
  session->old_current_fg=session->current_fg;
  session->old_current_bg=session->current_bg;
  session->old_other_session_flags=session->other_session_flags & ~NETTLE_OTHER_CURSOR_VIS;
  session->old_character_set_gl=session->character_set_gl;
  session->old_character_set_gr=session->character_set_gr;
  session->old_character_set_g0=session->character_set_g0;
  session->old_character_set_g1=session->character_set_g1;
  session->old_character_set_g2=session->character_set_g2;
  session->old_character_set_g3=session->character_set_g3;
  session->cursor_saved=true;
  session->position_saved=true;
}

void restore_cursor_position (struct session_struct *session)
{
  if (session->cursor_saved && session->position_saved)
  {
    session->pos.x=session->old_pos.x;
    session->pos.y=session->old_pos.y+session->scrollback;
    session->current_flags=session->old_current_flags;
    session->current_fg=session->old_current_fg;
    session->current_bg=session->old_current_bg;
    session->other_session_flags|=session->old_other_session_flags & ~NETTLE_OTHER_CURSOR_VIS;
    session->character_set_gl=session->old_character_set_gl;
    session->character_set_gr=session->old_character_set_gr;
    session->character_set_g0=session->old_character_set_g0;
    session->character_set_g1=session->old_character_set_g1;
    session->character_set_g2=session->old_character_set_g2;
    session->character_set_g3=session->old_character_set_g3;
    session->cursor_saved=false;
    session->position_saved=false;
    snap_cursor_to_terminal(session);
  }
}

void swap_alternate_main(struct session_struct *session)
{
  int xpos;
  int ypos;

  if (!session->alternate_area)
    return;

  for (ypos=0; ypos<session->terminal_size.y; ypos++)
  {
    for (xpos=0; xpos<session->terminal_size.x; xpos++)
    {
      char fg       =read_alt_assigned_fg(session, xpos, ypos);
      char bg       =read_alt_assigned_bg(session, xpos, ypos);
      char flags    =read_alt_assigned_flags(session, xpos, ypos);
      char character=read_alt_assigned_character(session, xpos, ypos);

      write_alt_assigned(session, xpos, ypos,
                         read_assigned_fg(session, xpos, ypos+session->scrollback),
                         read_assigned_bg(session, xpos, ypos+session->scrollback),
                         read_assigned_flags(session, xpos, ypos+session->scrollback),
                         read_assigned_character(session, xpos, ypos+session->scrollback));

      write_assigned(session, xpos, ypos+session->scrollback, fg, bg, flags, character);
    }
  }

  if (session->line_flags && session->alternate_line_flags)
  {
    for (ypos=0; ypos<session->terminal_size.y; ypos++)
    {
      char state=session->line_flags[ypos+session->scrollback];

      session->line_flags[ypos+session->scrollback]=session->alternate_line_flags[ypos];

      session->alternate_line_flags[ypos]=state;
    }
  }
}

void copy_alternate_main(struct session_struct *session)
{
  int xpos;
  int ypos;

  if (!session->alternate_area)
    return;

  for (ypos=0; ypos<session->terminal_size.y; ypos++)
  {

    for (xpos=0; xpos<session->terminal_size.x; xpos++)
    {
      char fg       =read_alt_assigned_fg(session, xpos, ypos);
      char bg       =read_alt_assigned_bg(session, xpos, ypos);
      char flags    =read_alt_assigned_flags(session, xpos, ypos);
      char character=read_alt_assigned_character(session, xpos, ypos);
      write_assigned(session, xpos, ypos+session->scrollback, fg, bg, flags, character);
    }
  }

  if (session->line_flags && session->alternate_line_flags)
  {
    for (ypos=0; ypos<session->terminal_size.y; ypos++)
    {
      session->line_flags[ypos+session->scrollback]=session->alternate_line_flags[ypos];
    }
  }
}

void copy_main_alternate(struct session_struct *session)
{
  int xpos;
  int ypos;

  if (!session->alternate_area)
    return;

  for (ypos=0; ypos<session->terminal_size.y; ypos++)
  {
    for (xpos=0; xpos<session->terminal_size.x; xpos++)
    {
      char fg       =read_assigned_fg(session, xpos, ypos+session->scrollback);
      char bg       =read_assigned_bg(session, xpos, ypos+session->scrollback);
      char flags    =read_assigned_flags(session, xpos, ypos+session->scrollback);
      char character=read_assigned_character(session, xpos, ypos+session->scrollback);
      write_alt_assigned(session, xpos, ypos, fg, bg, flags, character);
    }
  }

  if (session->line_flags && session->alternate_line_flags)
  {
    for (ypos=0; ypos<session->terminal_size.y; ypos++)
    {
      session->alternate_line_flags[ypos]=session->line_flags[ypos+session->scrollback];
    }
  }
}

void clear_main_screen(struct session_struct *session)
{
  clear_screen(session, session->scrollback*session->terminal_size.x,
                        session->terminal_size.x*
                                 (session->terminal_size.y+session->scrollback),
                        session->current_fg,
                        session->current_bg,
                        session->current_flags,
                        32);
}

void clear_alternate_screen(struct session_struct *session)
{
  clear_alt_screen(session, 0,
                            session->terminal_size.x*session->terminal_size.y,
                            session->current_fg,
                            session->current_bg,
                            session->current_flags,
                            32);
}


void determine_character_set(struct session_struct *session)
{
  switch (session->escape_string[2])
  {
    case 'B':
      /* ASCII */
      switch (session->escape_string[1])
      {
        case '(':
          session->character_set_g0=NETTLE_CHSET_ASCII;
          break;
        case ')':
          session->character_set_g1=NETTLE_CHSET_ASCII;
          break;
        case '*':
          if (session->terminal_mode==2)
          {
            session->character_set_g2=NETTLE_CHSET_ASCII;
          }
          break;
        case '+':
          if (session->terminal_mode==2)
          {
            session->character_set_g3=NETTLE_CHSET_ASCII;
          }
          break;
      }
      break;
    case '<':
      /* DEC Supplemental graphics */
      if (session->terminal_mode==2)
      {
        switch (session->escape_string[2])
        {
          case '(':
            session->character_set_g0=NETTLE_CHSET_DEC_SUPPLEMENTAL_GRAPHICS;
            break;
          case ')':
            session->character_set_g1=NETTLE_CHSET_DEC_SUPPLEMENTAL_GRAPHICS;
            break;
          case '*':
            session->character_set_g2=NETTLE_CHSET_DEC_SUPPLEMENTAL_GRAPHICS;
            break;
          case '+':
            session->character_set_g3=NETTLE_CHSET_DEC_SUPPLEMENTAL_GRAPHICS;
            break;
        }
      }
      break;
    case 'A':
      /* UK National */
      if (session->terminal_mode==1)
      {
        switch (session->escape_string[1])
        {
          case '(':
            session->character_set_g0=NETTLE_CHSET_UK_NATIONAL;
            break;
          case ')':
            session->character_set_g1=NETTLE_CHSET_UK_NATIONAL;
            break;
        }
      }
      break;
    case '0':
      /* DEC Special Graphics */
      switch (session->escape_string[1])
      {
        case '(':
          session->character_set_g0=NETTLE_CHSET_DEC_SPECIAL_GRAPHICS;
          break;
        case ')':
          session->character_set_g1=NETTLE_CHSET_DEC_SPECIAL_GRAPHICS;
          break;
        case '*':
          if (session->terminal_mode==2)
          {
            session->character_set_g2=NETTLE_CHSET_DEC_SPECIAL_GRAPHICS;
          }
          break;
        case '+':
          if (session->terminal_mode==2)
          {
            session->character_set_g3=NETTLE_CHSET_DEC_SPECIAL_GRAPHICS;
          }
          break;
      }
      break;
  }
}


void process_data(struct session_struct *session, char byte)
{
  bool done=false;

  if (session->bit_controls)
    /* 8-bit */
    strcpy(csi_sequence,"\x9B");
  else
    /* 7-bit */
    strcpy(csi_sequence,"\x1B[");

  if (session->escape_state==NETTLE_ESCAPE_ESCAPE)
  {
    if (session->escape_string_length==255)
    {
      printf("%s\n",lookup_static ("esctoolong"));
      session->escape_state=NETTLE_ESCAPE_NONE;
      session->escape_string_length=0;
    }
    else
    {
      if ((session->escape_string[0]==TELNET_IAC) ||
          (session->escape_string[0]==27 && (byte<8 || byte>13)))
      {
        session->escape_string[session->escape_string_length]=byte;
        session->escape_string_length++;
        done = process_escape(session);
      }
    }
  }

  if (session->spool_file_name!=NULL && session->spool_raw)
    spool_byte(session, byte);

  if (session->escape_state==NETTLE_ESCAPE_ESCAPE)
  {
    if (session->escape_string[0]==TELNET_IAC)
    {
      return;
    }
  }

  if (!done)
  {
    if (session->spool_file_name!=NULL && !(session->spool_raw))
    {
      if (byte>=9 && byte<=12)
      {
        spool_byte(session, byte);
      }
    }

    switch (byte)
    {
      /* VT codes */
      case 0:   byte_null                  (session); break;
      case 1:                                         break;
      case 2:                                         break;
      case 3:                                         break;
      case 4:                                         break;
      case 5:   byte_enquiry               (session); break;
      case 7:   byte_bell                  (session); break;
      case 8:   byte_backspace             (session); break;
      case 9:   byte_horizontal_tabulation (session); break;
      case 10:
      case 11:
      case 12:  byte_linefeed              (session); break;
      case 13:  byte_carriage_return       (session); break;
      case 14:  byte_shift_out             (session); break;
      case 15:  byte_shift_in              (session); break;
      case 16:                                        break;
      case 17:  byte_device_control_1      (session); break;
      case 18:                                        break;
      case 19:  byte_device_control_3      (session); break;
      case 20:                                        break;
      case 21:                                        break;
      case 22:                                        break;
      case 23:                                        break;
      case 24:  byte_cancel                (session); break;
      case 25:                                        break;
      case 26:  byte_substitute            (session); break;
      case 27:  byte_escape                (session); break;
      case 28:                                        break;
      case 29:                                        break;
      case 30:                                        break;
      case 31:                                        break;
      case 127: byte_delete                (session); break;

      /* Telnet codes */
      case 255: byte_telnet                (session); break;

      /* Other codes */
      default:
        if (session->bit_controls)
        {
          /* 8-bit control sequences */
          switch (byte)
          {
            /* VT C1 codes */
            case 128:                                       break;
            case 129:                                       break;
            case 130:                                       break;
            case 131:                                       break;
            case 132: byte_index                 (session); break;
            case 133: byte_next_line             (session); break;
            case 134:                                       break;
            case 135:                                       break;
            case 136: byte_horizontal_tab_set    (session); break;
            case 137:                                       break;
            case 138:                                       break;
            case 139:                                       break;
            case 140:                                       break;
            case 141: byte_reverse_index         (session); break;
            case 142: byte_single_shift_g2       (session); break;
            case 143: byte_single_shift_g3       (session); break;
            case 144: byte_device_control_string (session); break;
            case 145:                                       break;
            case 146:                                       break;
            case 147:                                       break;
            case 148:                                       break;
            case 149:                                       break;
            case 150:                                       break;
            case 151:                                       break;
            case 152:                                       break;
            case 153:                                       break;
            case 154:                                       break;
            case 155: byte_control_sequence_intro(session); break;
            case 156: byte_string_terminator     (session); break;
            case 157:                                       break;
            case 158:                                       break;
            case 159:                                       break;
            case 160:                                       break;

            /* Not a 7 or 8-bit control sequence, byte_default */
            default:  byte_default         (session, byte); break;
          }
        }
        else
        {
          /* Not a 7-bit control sequence (8-bit sequences N/A), byte_default */
                      byte_default         (session, byte); break;
        }
    }
  }
}

void append_paste_block (struct session_struct *session,
                                struct paste_buffer *paste, size_t size)
{
  paste->next = NULL;
  paste->length = size;

  if (! session->paste)
    session->paste = paste;
  if (session->paste_head)
    session->paste_head->next = paste;
  session->paste_head = paste;

  main_requirenull = true;
}


void process_wimp_key(struct session_struct *session, int key, int extra)
{
  struct paste_buffer *send_block=malloc(264); /* Keys shouldn't be bigger than 256,
                                                  + 4 for *next, +4 for length - better way? */

  int size_of_send_block=0;

  if (!send_block)
    return;

  size_of_send_block = decode_key (session, key, extra, send_block->data);

  process_handle_buffer(session, send_block, size_of_send_block);
}

void process_line_editor(struct session_struct *session)
{
  char *icon_text;
  char data[MAX_LINEEDITOR_LENGTH];
  int line_editor_icon=0;

  read_icon_data(session->pane_handle, line_editor_icon, data, sizeof(data));

  icon_text = malloc(strlen(data) + 3);
  assert(icon_text != NULL);

  strcpy(icon_text, data);
  strcat(icon_text, "\r");

  process_data_transmission(session, icon_text, strlen(icon_text));

  {
    int loop;

    for (loop=line_editor_size-1; loop>1; loop--)
    {
      strcpy(session->line_editor_history+(loop*MAX_LINEEDITOR_LENGTH),
                      session->line_editor_history+((loop-1)*MAX_LINEEDITOR_LENGTH));
    }
  }

  strcpy(session->line_editor_history+(1*MAX_LINEEDITOR_LENGTH), icon_text);

  set_icon_data(session->pane_handle,line_editor_icon,"");

  set_caret_position(session->pane_handle, line_editor_icon, -1, 0);

  session->line_editor_position=0;
  session->line_editor_total++;

  if (session->line_editor_total>line_editor_size)
  {
    session->line_editor_total=line_editor_size;
  }

  free(icon_text);
}

void process_data_transmission(struct session_struct *session,
                               const char *icon_text, int size_of_data)
{
  char block[256];
  int size_of_block=0;
  struct paste_buffer *send_block=0;
  int size_of_send_block=0;
  int allocated_send_size=0;
  int loop;

  for (loop=0; loop<size_of_data; loop++)
  {
    int key=icon_text[loop];

    size_of_block = decode_key (session, key, 0, block);

    while (size_of_send_block + size_of_block + offsetof (struct paste_buffer, data) > allocated_send_size)
    {
      struct paste_buffer *new_send_block;

      allocated_send_size += 16384;
      new_send_block = realloc (send_block, allocated_send_size);
      if (!new_send_block)
      {
        free(send_block);
        generror ("CantAlloc", true);
        return;
      }
      send_block=new_send_block;
    }
    memcpy(send_block->data+size_of_send_block,block,size_of_block);
    size_of_send_block+=size_of_block;
  }

  process_handle_buffer(session, send_block, size_of_send_block);
}

void process_handle_buffer(struct session_struct *session,
                                  struct paste_buffer *send_block,
                                  int size_of_send_block)
{
  char cursor_flags;
  struct coords pos;

  if (session->other_session_flags & NETTLE_OTHER_KEYLOCK_MODE)
    return;

  /* Just for safety */
  if (session->socket_state!=NETTLE_SESSION_CONNECTED)
    return;

  pos=get_cursor_position(session);

  /* get the cursor flags */
  cursor_flags=read_assigned_flags(session, pos.x, pos.y)
                    & (NETTLE_FLAG_CURSOR | NETTLE_FLAG_NO_INPUT);

  /* if the cursor flags don't have cursor set, make it so */
  cursor_flags |= NETTLE_FLAG_CURSOR;

  /* remove the cursor from the position */
  changedbox_init (session);
  changedbox_update_char (pos.x, pos.y);
  write_assigned_flags(session, pos.x, pos.y, read_assigned_flags(session, pos.x, pos.y)
						 & ~(NETTLE_FLAG_CURSOR | NETTLE_FLAG_NO_INPUT));
  force_redraw_changedbox ();

  changedbox_init (session);

  if (session->local_echo == true) /* session->session_flags[1]==false) */
  {
    int loop;

    for (loop=0; loop<size_of_send_block; loop++)
    {
      switch (send_block->data[loop])
      {
        case 7: byte_bell                  (session); break;
        case 8: byte_backspace             (session);

          changedbox_update_char (session->pos.x, session->pos.y);
          write_assigned(session, session->pos.x, session->pos.y,
                         session->current_fg,
          	         session->current_bg,
          	         session->current_flags,
            		 32);
          break;

        case 9:  byte_horizontal_tabulation(session); break;
        case 10: byte_linefeed             (session); break;
        case 13: byte_carriage_return      (session);
                 #ifdef KEYBOARD_RETURN_BODGE
                 byte_linefeed(session);
                 #endif
                 break;

        default:
          reprocess_position(session);

          if (send_block->data[loop]<32)
          {
            changedbox_update_char (session->pos.x, session->pos.y);
            write_assigned(session, session->pos.x, session->pos.y,
                           session->current_fg,
            		   session->current_bg,
              		   session->current_flags,
              		   '^');

            session->pos.x++;
            reprocess_position(session);
          }

          /* write out the data */
          changedbox_update_char (session->pos.x, session->pos.y);
          write_assigned(session, session->pos.x, session->pos.y,
                         session->current_fg,
                         session->current_bg,
              		 session->current_flags,
              		 '?');

          if (send_block->data[loop]<32)
          {
            write_assigned_character(session, session->pos.x, session->pos.y,
                                     send_block->data[loop]+64);
          }
          else
          {
            work_out_character(session, session->pos.x, session->pos.y, send_block->data[loop]);
          }
          session->pos.x++;

          break;
      }
    }
  }

  pos=get_cursor_position(session);

  /* OR back in the cursor flags */
  if (session->other_session_flags & NETTLE_OTHER_CURSOR_VIS)
  {
    write_assigned_flags(session, pos.x, pos.y,
                         read_assigned_flags(session, pos.x, pos.y) | cursor_flags);
  }

  /* redraw the window */
  force_redraw_changedbox ();

  /* force the selection back on (it may have got overwritten by new data) */
  force_redraw_selection();

  /* redraw the cursor, in case it isn't in the main redraw block */
  if (session->other_session_flags & NETTLE_OTHER_CURSOR_VIS)
  {
    changedbox_init (session);
    changedbox_update_char (pos.x, pos.y);
    force_redraw_changedbox ();
  }

  /* queue the data for sending... */
  if (send_block)
    append_paste_block (session, send_block, size_of_send_block);
}

/* get the current cursor position */
struct coords get_cursor_position(struct session_struct *session)
{
  struct coords pos;

  pos.x = session->pos.x;
  pos.y = session->pos.y;

  /* get a sanitised x coordinate */
  if (pos.x >= session->terminal_size.x)
    pos.x = session->terminal_size.x - 1;

  /* return real offset or, if outside terminal, the last available offset */
  if (pos.y >= session->terminal_size.y + session->scrollback)
  {
    pos.x = session->terminal_size.x;
    pos.y = session->terminal_size.y + session->scrollback - 1;
  }

  return pos;
}
