// Copyright (C) 2000
//          by the Massachusetts Institute of Technology
//
//    Export of this software from the United States of America may
//    require a specific license from the United States Government.  It
//    is the responsibility of any person or organization contemplating
//    export to obtain such a license before exporting.
//
// WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
// distribute this software and its documentation for any purpose and
// without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both that copyright notice and
// this permission notice appear in supporting documentation, and that
// the name of M.I.T. not be used in advertising or publicity pertaining
// to distribution of the software without specific, written prior
// permission.  M.I.T. makes no representations about the suitability of
// this software for any purpose.  It is provided "as is" without express
// or implied warranty.

// winsshView.cpp : implementation of the CWinsshView class
//

#include "stdafx.h"
#include "winssh.h"
#include "Errors.h"

#include "DisplayPropertiesDlg.h"
#include "winsshDoc.h"
#include "winsshView.h"
#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWinsshView

IMPLEMENT_DYNCREATE(CWinsshView, CView)

BEGIN_MESSAGE_MAP(CWinsshView, CView)
	//{{AFX_MSG_MAP(CWinsshView)
	ON_WM_CHAR()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_SIZE()
	ON_COMMAND(ID_DISP_PROPERTIES, OnDisplayProperties)
	ON_WM_PAINT()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CWinsshView construction/destruction

CWinsshView::CWinsshView()
{
	// initialization work
	m_usTermY = 24;
	m_usTermX = 80;
	m_usCursorSaveY = 1;
	m_usCursorSaveX = 1;
	m_usFontY = 0;
	m_usFontX = 0;
	m_isSystemKey = FALSE;
	m_isCtrlKey = FALSE;
	pDoc = NULL;
	m_bScreenInit = false;
}

CWinsshView::~CWinsshView()
{
	// Zero out all character buffers before exit
	memset(m_uszDisplayBuffer, NULL, sizeof(m_uszDisplayBuffer));
	memset(m_uszDisplayASCIILine, NULL, sizeof(m_uszDisplayASCIILine));

	m_font.DeleteObject();
	m_brush.DeleteObject();
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshView diagnostics

#ifdef _DEBUG
void CWinsshView::AssertValid() const
{
	CView::AssertValid();
}

void CWinsshView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CWinsshDoc* CWinsshView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CWinsshDoc)));
	return (CWinsshDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CWinsshView creation

BOOL CWinsshView::PreCreateWindow(CREATESTRUCT& cs)
{
	BOOL bRes = CView::PreCreateWindow(cs);
	cs.style |= ES_SELECTIONBAR | WS_VSCROLL | ES_READONLY;
	return bRes;
}

BOOL CWinsshView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	m_brush.CreateSolidBrush(GetApp->m_crBack);

	LPCTSTR	lpszWinsshViewClass =
		AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_OWNDC, 
							LoadCursor(NULL, IDC_ARROW), 
							(HBRUSH)m_brush,
							NULL);

	return CWnd::Create(lpszWinsshViewClass, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshView drawing
//
void CWinsshView::OnInitialUpdate() 
{
	CDC* pDC = GetDC();
	ABCFLOAT	myABC;

	// Reset Color/Display related stuff
	pDC->SetTextColor(GetApp->m_crText);
	pDC->SetBkColor(GetApp->m_crBack);
	pDC->SetBkMode(OPAQUE);
	pDC->SetMapMode(MM_TEXT);
	
	m_font.DeleteObject();
	m_font.CreateFontIndirect(&GetApp->m_lf);

	pDC->SelectObject(&m_font);

	//Calculate width of Normal font
	pDC->GetCharABCWidths('a', 'a', &myABC);
	m_usFontY = (unsigned short) (-GetApp->m_lf.lfHeight);
	m_usFontX = (unsigned short) (myABC.abcfA + myABC.abcfB + myABC.abcfC);

	for (int i=0; i < SSH_MAX_TERM_COLS; i++)
		m_iSpacingArray[i] = m_usFontX;

	// retrieve max row and column counts
	UpdateTermXY();

	m_usScrollRegionTop = 1; // default to top row
	m_usScrollRegionBottom = m_usTermY;	//default to bottom row

	//place cursor on the top left
	m_usCursorY = 1; 
	m_usCursorX = 1;

	//flood fill the background
	RECT	myClientRect;
	myClientRect.left = 0;
	myClientRect.top = 0;
	myClientRect.right = m_usWndX;
	myClientRect.bottom = m_usWndY;
	pDC->FillSolidRect(&myClientRect, GetApp->m_crBack);

	//Clear the Display Buffer
	memset(m_uszDisplayBuffer, NULL, sizeof(m_uszDisplayBuffer));
	m_usDisplayBufferTop = 0;

	// Set the appropriate flags
	m_ulLastCommand		= TC_UNKNOWN;
	m_ulSecLastCommand	= TC_UNKNOWN;
	m_isSystemKey = FALSE;
	m_isCtrlKey = FALSE;
	m_bFreshReconnection = FALSE;
	m_isAttribBold = false;
	m_isAttribUndl = false;
	m_isAttribRevs = false;
	m_bScreenInit = TRUE;
#ifdef DEBUG_SSH
		Debug("CWinsshView::OnUpdate()");
#endif
}

void CWinsshView::OnDraw(CDC* pDC)
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);

}

void CWinsshView::OnPaint() 
{
	unsigned short usTopRow, usBottomRow, usLeftCol, usRightCol;
	PAINTSTRUCT	psMissingArea;
	memset(&psMissingArea, NULL, sizeof(psMissingArea));
	BeginPaint(&psMissingArea);

	if (m_bScreenInit)
	{
		usTopRow	= ( psMissingArea.rcPaint.top / m_usFontY ) + 1;
		usBottomRow	= ( psMissingArea.rcPaint.bottom / m_usFontY ) + 1;

		usLeftCol	= (	psMissingArea.rcPaint.left / m_usFontX) + 1;
		usRightCol	= ( psMissingArea.rcPaint.right / m_usFontX) + 1;

		if (usTopRow > m_usTermY)		usTopRow = m_usTermY;
		if (usBottomRow > m_usTermY)	usBottomRow = m_usTermY;
		if (usLeftCol > m_usTermX)		usLeftCol = m_usTermX;
		if (usRightCol > m_usTermX)		usRightCol = m_usTermX;

		RepaintMissingArea(usTopRow, usBottomRow, usLeftCol, usRightCol);
	}
#ifdef DEBUG_SSH
		Debug("CWinsshView::OnPaint() area-rect top%3d bottom%3d left%3d right%3d", psMissingArea.rcPaint.top, psMissingArea.rcPaint.bottom, psMissingArea.rcPaint.left, psMissingArea.rcPaint.right);
		Debug("CWinsshView::OnPaint() area-term top%3d bottom%3d left%3d right%3d", usTopRow, usBottomRow, usLeftCol, usRightCol);
#endif
	EndPaint(&psMissingArea);
}

void CWinsshView::OnKillFocus(CWnd* pNewWnd) 
{
	CView::OnKillFocus(pNewWnd);
	
	DestroyCaret();	
}

void CWinsshView::OnSetFocus(CWnd* pOldWnd) 
{
	CView::OnSetFocus(pOldWnd);
	pDoc = GetDocument();
	if (pDoc == NULL) return;
	
	if (pDoc->m_iAppState == APP_STATE_CONNECTED)
		DisplayCaret();	
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshView Message handlers

void CWinsshView::OnSize(UINT nType, int cx, int cy) 
{
#ifdef DEBUG_SSH
		Debug("CWinsshView::OnSize() x%d, y%d, type%d", cx, cy, nType);
#endif

	// Update WndX and WndY
	m_usWndX = (unsigned short) cx;
	m_usWndY = (unsigned short) cy;

	// Consequently TermX and TermY gets changed
	UpdateTermXY();

	pDoc = GetDocument();
	if (pDoc != NULL)
	{
		if (pDoc->m_iAppState == APP_STATE_CONNECTED)
		{
//			pDoc->RequestNewPty(m_usTermY, m_usTermX);
		}
	}
/*	
	// resize message sending
	WPARAM	myWParam = 0;
	myWParam = ((short int)CWnd::GetScrollLimit(SB_VERT) );
	myWParam <<= 16 ;
	myWParam += SB_THUMBTRACK;
	PostMessage(WM_VSCROLL, myWParam, NULL);
	myWParam ^= SB_THUMBTRACK;
	myWParam += SB_THUMBPOSITION;
	PostMessage(WM_VSCROLL, myWParam, NULL);
	myWParam ^= SB_THUMBPOSITION;
	myWParam += SB_ENDSCROLL;
	PostMessage(WM_VSCROLL, myWParam, NULL);
*/
	CView::OnSize(nType, cx, cy);
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshView message handlers to intercept all System key inputs
//
//

void CWinsshView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// Check if we even have to process keystrok at all.
	if (pDoc->m_iAppState == APP_STATE_DISCONNECTED)
		return;

	UCHAR	uszSendText[10];
	int		iOutLen = 0;

	m_isSystemKey = FALSE;

	if (nChar == VK_CONTROL)
		m_isCtrlKey = TRUE;
	else if ((nChar < VK_NUMLOCK) && (pDoc->m_pTC != NULL))
	{
		iOutLen = pDoc->m_pTC->VtSendProcess(nChar, &uszSendText[0]);
		if (iOutLen <= 0)
			CView::OnKeyDown(nChar, nRepCnt, nFlags);
	}
	else if (!m_isCtrlKey)
		CView::OnKeyDown(nChar, nRepCnt, nFlags);

	if (iOutLen > 0)
	{
		pDoc->SocketEncryptSend(uszSendText, iOutLen);
		m_isSystemKey = TRUE;
	}
}

void CWinsshView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// Check if we even have to process keystrok at all.
	if (pDoc->m_iAppState != APP_STATE_CONNECTED)
		return;

	if (nChar == VK_CONTROL)
		m_isCtrlKey = FALSE;
	else
		CView::OnKeyUp(nChar, nRepCnt, nFlags);
}

// This intercepts non-system keys input
void CWinsshView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	// Check if we even have to process keystrok at all.
	if (pDoc->m_iAppState != APP_STATE_CONNECTED)
		return;

	if (!m_isSystemKey)
		pDoc->SocketEncryptSend((UCHAR*)(&nChar), 1);
	//CView::OnChar(nChar, nRepCnt, nFlags);
}

BOOL CWinsshView::OnCommand(WPARAM wParam, LPARAM lParam) 
{
	// We need to ignore the cut command.
	if (HIWORD(wParam) == 1)
		return TRUE;
	return CView::OnCommand(wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////
// CWinsshView misc functions
//
//

void CWinsshView::UpdateTermXY()
{
	if ( (m_usFontY > 0) && (m_usFontX > 0) ) //just to guard divide by zero errors
	{
		m_usTermY = m_usWndY / m_usFontY;
		m_usTermX = m_usWndX / m_usFontX;
		if (m_usTermY > SSH_MAX_TERM_ROWS)
			m_usTermY = SSH_MAX_TERM_ROWS;
		if (m_usTermX > SSH_MAX_TERM_COLS)
			m_usTermX = SSH_MAX_TERM_COLS;
	}

	//validating cursor is in fact within the screen area
	if (m_usCursorY > m_usTermY)
		m_usCursorY = m_usTermY;
	if (m_usCursorX > m_usTermX)
		m_usCursorX = m_usTermX;
}

unsigned short CWinsshView::GetCursorCoordinateX()
{
	return ( (m_usCursorX - 1) * m_usFontX );
}

unsigned short CWinsshView::GetCursorCoordinateY()
{
	return ( (m_usCursorY - 1) * m_usFontY);
}

void CWinsshView::ReplyDeviceStatus()
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);

	if (pDoc->m_iAppState != APP_STATE_CONNECTED)
		return;

	UCHAR* uszSendString = (UCHAR*)"\x01b[0n";
	pDoc->SocketEncryptSend(uszSendString,4);
}

void CWinsshView::ReplyDeviceCursor()
{
	pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	CString szSendString;

	if (pDoc->m_iAppState != APP_STATE_CONNECTED)
		return;

	szSendString.Format("\x01b[%d;%dR", (int)m_usTermY, (int)m_usTermX);
	pDoc->SocketEncryptSend(szSendString);
}


/////////////////////////////////////////////////////////////////////////////
// CWinsshView DisplayProperties Dialog function
//
//
void CWinsshView::OnDisplayProperties() 
{
	CDisplayPropertiesDlg	DispPropDlg;

	// pre-popup initialization
	DispPropDlg.m_lf	= GetApp->m_lf;
	DispPropDlg.m_crText		= GetApp->m_crText;
	DispPropDlg.m_crBack		= GetApp->m_crBack;

	if ( DispPropDlg.DoModal() == IDOK )
	{
		GetApp->m_lf			= DispPropDlg.m_lf;
		GetApp->m_crText		= DispPropDlg.m_crText;
		GetApp->m_crBack		= DispPropDlg.m_crBack;

		//now signal the change in screen info
		ChangeDisplayFont();
	}
}
