// TextView.cpp : implementation file
//

#include "stdafx.h"
#include "thing.h"
#include "TextView.h"
#include "ThingDoc.h"

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

/////////////////////////////////////////////////////////////////////////////
// CTextView

IMPLEMENT_DYNCREATE(CTextView, CAlexView)

CTextView::CTextView()
{
	clickmode=0;
	editmode=0;
	
}

CTextView::~CTextView()
{
	
}


BEGIN_MESSAGE_MAP(CTextView, CAlexView)
	//{{AFX_MSG_MAP(CTextView)
	ON_WM_KEYDOWN()
	ON_WM_SYSKEYDOWN()
	ON_WM_KEYUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
	ON_WM_MOUSEWHEEL()
	ON_WM_SIZE()
	ON_WM_ERASEBKGND()
	ON_WM_CHAR()
	ON_WM_TIMER()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTextView drawing

void CTextView::Redraw()
{
	CAlexView::Redraw();
	if (alexdc)
	{
	
		CThingDoc *doc = (CThingDoc*)GetDocument();

		int numrows = h/8-2;
		int lastrow = doc->scrollpos+numrows;
		doc->numrows=numrows;
		doc->lastrow=lastrow;

		int numx= w/8 -4;
		

		if (doc->curpos<doc->scrollpos) doc->scrollpos=doc->curpos;
		if (doc->curpos>lastrow) doc->scrollpos=doc->curpos-numrows;
		if (doc->channels[doc->curchan].x<4) doc->scrollx+=doc->channels[doc->curchan].x-4;
		if (doc->channels[doc->curchan].x+doc->channels[doc->curchan].width>=numx) doc->scrollx+=doc->channels[doc->curchan].x+doc->channels[doc->curchan].width-numx;

		doc->scrollx=bound(0,doc->scrollx,1000);
		doc->scrollpos/=doc->zoom;
		doc->scrollpos*=doc->zoom;
		if (doc->scrollpos<0) doc->scrollpos=0;

		doc->CalcChanX();
		
		char buf[256];
		
		for (int c1=0;c1<(int)doc->channels.size();c1++)
		{
			int row=doc->scrollpos;
			int y=8;
			DataIt it = doc->channels[c1].data.begin();
			while (y<h)
			{
				int fcol=0x404040;
				while (it!=doc->channels[c1].data.end() && it->time < row) ++it;
				if (it!=doc->channels[c1].data.end() && it->time == row)
				{
					it->Format(buf,doc->channels[c1].type,doc->channels[c1].width);
					fcol=0x808080;
					strcat(buf,"         ");
					++it;
				}
				else
				{
					strcpy(buf,".");					
				}
				int col=0;
				if (c1>=doc->selchan1 && c1<doc->selchan2 && row>=doc->selpos1 && row<doc->selpos2)
				{					
					strcat(buf,"         ");
					col=0x390020;
					fcol|=0x202020;
				}
								
				if (row==doc->curpos && c1==doc->curchan) 
				{
					col = 0xff;
					fcol = 0xffffff;
					if (editmode)
					{
						fcol = 0xff;
						col = 0xffffff;
						strcpy(buf,editstr);
						strcat(buf,"         ");
					}
				}
				buf[doc->channels[c1].width]=0;
				alexdc->Text8(buf,doc->channels[c1].x*8,y,C32TO8(fcol),C32TO8(col));
				if (editmode && row==doc->curpos && c1==doc->curchan)
				{
					sprintf(buf,"%c",editstr[editpos] ? editstr[editpos] : ' ');
					alexdc->Text8(buf,(doc->channels[c1].x+editpos)*8,y,C32TO8(fcol),C32TO8(0xff8080));
				}
				

				y+=8;
				row+=doc->zoom;
			}
		}
		int row=doc->scrollpos;
		
		for (c1=0;c1<(int)doc->channels.size();c1++)
		{
			int col = (c1&1) ? 0x880000 : 0xcc2020;
			if (c1==doc->curchan) col = 0xff;
			sprintf(buf,"%s           ",doc->channels[c1].name);
			buf[doc->channels[c1].width]=0;
			alexdc->Text8(buf,doc->channels[c1].x*8,0,C32TO8(0xffffff),C32TO8(col));
			alexdc->VLine((doc->channels[c1].width+doc->channels[c1].x)*8,8,h,C32TO8(0x404040));
		}
		for (int y=8;y<h;y+=8)
		{
			sprintf(buf,"%04x",row);
			int col = (row & 7) ? 0x480000 : 0xcc2020;			
			if ((row&63)==0) col = 0xff8080;
			if (row==doc->curpos) col = 0xff;
			alexdc->Text8(buf,0,y,C32TO8((row & 7) ? 0xeeeeee : 0xffffff ),C32TO8(col));
			int orow=row;
			row+=doc->zoom;
			col=0;
			int col2=0;
			if (orow/64!=row/64) col=0x808080; else
			if (orow/16!=row/16) col=0x404040; else
			if (orow/4!=row/4) col=0x202020;
			if (orow==doc->playpos) 
				col=col2=doc->playing?0x00cc00 : 0x006000;
			if (col)
			{
				alexdc->HLine(4*8,y+7,w,C32TO8(col));
			}
			if (col2)
			{
				alexdc->HLine(4*8,y,w,C32TO8(col2));
			}
		}

		alexdc->Text8("POS ",0,0,C32TO8(0x707070),0);
		
	}
}

void CTextView::OnInitialUpdate()
{
	CAlexView::OnInitialUpdate();

	CSize sizeTotal;
	// TODO: calculate the total size of this view
	sizeTotal.cx = sizeTotal.cy = 100;
	SetScrollSizes(MM_TEXT, sizeTotal);

	static first=1;
	if (first)
	{
		first=0;
		SetTimer(23,100,NULL);
	}
	
}


/////////////////////////////////////////////////////////////////////////////
// CTextView diagnostics

#ifdef _DEBUG
void CTextView::AssertValid() const
{
	CAlexView::AssertValid();
}

void CTextView::Dump(CDumpContext& dc) const
{
	CAlexView::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CTextView message handlers


void __cdecl Report3D(const char *pFormat,... )
{
	char debugText[2048];
	va_list	parameter;
	va_start(parameter,pFormat);
	vsprintf(debugText,pFormat,parameter);
	OutputDebugString(debugText);
}

void CTextView::LeaveEditMode(bool cancel)
{
	if (!editmode) return;
	if (!cancel && editvol==0) 
	{
		CThingDoc *doc = (CThingDoc*)GetDocument();

		Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
		//d.IncDec(doc->channels[doc->curchan].type,1,GetAsyncKeyState(VK_MENU)<0);
		d.Scan(editstr,doc->channels[doc->curchan].type);
		doc->channels[doc->curchan].SetVal(d);
	}
	editmode=0;
}

void CTextView::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	OnKeyDown(nChar,nRepCnt,nFlags);
}
void CTextView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	int redraw=0;
	int nc=doc->channels.size();
	int opos = doc->curpos;
	int ochan = doc->curchan;
	if (editmode) editpos=bound(0,editpos,strlen(editstr));
	switch (nChar)
	{
	default:
		#ifdef _DEBUG
		Report3D("unknown key %d %02x\n",nChar,nChar);		
		#endif
		break;
	case VK_TAB:
		if (realtime) RenderFrame();
		break;
	case VK_SHIFT:
		break;

	//case VK_ENTER:
	case VK_RETURN:
		if (editmode)
		{
			LeaveEditMode();
			doc->curpos+=doc->zoom;
		}
		else
		{
			//doc->curpos+=doc->zoom;
			editmode=1;
			Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
			d.Format(editstr,doc->channels[doc->curchan].type, doc->channels[doc->curchan].width);
			editpos=strlen(editstr);
			editvol=1;
		}
		redraw=1;
		break;

	case VK_F8:
		doc->StopPlaying();
		break;
	case VK_F5:
		doc->StartPlaying(GetAsyncKeyState(VK_CONTROL)<0 ? 0 : doc->curpos);
		break;
	case VK_ESCAPE:
		if (editmode) LeaveEditMode(true); else
		{
			if (doc->playing)
			{
				doc->StopPlaying();
			}
			else doc->selchan1=doc->selchan2=0;
		}
		redraw=1;
		break;
	case VK_LEFT:
		if (GetAsyncKeyState(VK_CONTROL)<0) doc->scrollx++; else
		if (editmode)
		{
			if (editpos>0) editpos--;
			editvol=0;
		}
		else
		{
			doc->curchan=(doc->curchan+nc-1)%nc;
		}		
		redraw=1;
		break;
	case VK_RIGHT:
		if (GetAsyncKeyState(VK_CONTROL)<0) doc->scrollx--; else
		if (editmode)
		{
			if (editpos<strlen(editstr)) editpos++;
			editvol=0;
		}	
		else
		{
			doc->curchan=(doc->curchan+1)%nc;
		}
		redraw=1;
		break;
	case VK_HOME:
		if (editmode)
		{
			editpos=0;
			editvol=0;
		}
		else
		{
			doc->curpos--;
			doc->curpos/=64;
			doc->curpos*=64;
		}		
		redraw=1;
		break;
	case VK_END:
		if (editmode)
		{
			editpos=strlen(editstr);
			editvol=0;
		}
		else
		{
			doc->curpos++;
			doc->curpos/=64;
			doc->curpos*=64;
			doc->curpos+=63;
		}
		
		redraw=1;
		break;
	case VK_UP:
		LeaveEditMode();
		if (GetAsyncKeyState(VK_CONTROL)<0)
		{
			if (doc->scrollpos>0) doc->scrollpos--;
		}
		else doc->curpos-=doc->zoom;
		redraw=1;
		break;
	case VK_DOWN:
		LeaveEditMode();
		if (GetAsyncKeyState(VK_CONTROL)<0)
		{
			if (doc->scrollpos<doc->maxtime-doc->numrows) doc->scrollpos++;
		}
		else doc->curpos+=doc->zoom;
		redraw=1;
		break;
	case VK_PRIOR:
		LeaveEditMode();
		if (GetAsyncKeyState(VK_CONTROL)<0)
		{
			if (doc->zoom>1) doc->zoom/=2;
		}
		else doc->curpos-=doc->zoom*16;
		redraw=1;
		break;
	case VK_NEXT:
		LeaveEditMode();
		if (GetAsyncKeyState(VK_CONTROL)<0)
		{
			if (doc->zoom<16) doc->zoom*=2;
		}
		else doc->curpos+=doc->zoom*16;
		redraw=1;
		break;
	case VK_ADD:
		{
			LeaveEditMode();
			Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
			d.IncDec(doc->channels[doc->curchan].type,1,GetAsyncKeyState(VK_MENU)<0);
			doc->channels[doc->curchan].SetVal(d);
			redraw=1;
		}
		break;
	case VK_SUBTRACT:
		{
			LeaveEditMode();
			Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
			d.IncDec(doc->channels[doc->curchan].type,-1,GetAsyncKeyState(VK_MENU)<0);
			doc->channels[doc->curchan].SetVal(d);
			redraw=1;
		}
		break;
	case 0xbe: // > .
		if (editmode || editpos>0) break;
	case 0xbc: // < ,
	
	case 0xbb: // equals
		{
			//LeaveEditMode();
			if (editmode)
			{
				if (editstr[0]=='=') editstr[0]='>'; else
				if (editstr[0]=='>') editstr[0]='='; else
				{
					memmove(editstr+1,editstr,strlen(editstr)+1);
					editstr[0]='=';
					editpos++;
				}
			}
			else
			{
				
				Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
				d.ramp=!d.ramp;
				doc->channels[doc->curchan].SetVal(d);
			}
			redraw=1;
		}
		break;
	case VK_SPACE:
		if (!editmode) {			
			doc->channels[doc->curchan].Clear(doc->curpos,1);
			doc->curpos++;
			redraw=1;
		}
		break;
	case VK_INSERT:
		{
			LeaveEditMode();
			if (GetAsyncKeyState(VK_SHIFT)<0)
			{
				for (int c=0;c<doc->channels.size();c++) doc->channels[c].InsertBlank(doc->curpos,1);
			}
			else
			{
				doc->channels[doc->curchan].InsertBlank(doc->curpos,1);
			}
			redraw=1;
		}
		break;
	case VK_DELETE:
		{
			if (GetAsyncKeyState(VK_SHIFT)<0)
			{
				LeaveEditMode();
				for (int c=0;c<doc->channels.size();c++) doc->channels[c].Delete(doc->curpos,1);
			}
			else if (editmode)
			{
				memmove(editstr+editpos,editstr+editpos+1,strlen(editstr)-editpos+1);
			}
			else
			{
				doc->channels[doc->curchan].Delete(doc->curpos,1);
			}
			redraw=1;
		}
		break;
	case VK_BACK:
		{
			if (editmode)
			{
				if (editvol)
				{
					editpos=0;
					editstr[0]=0;
				}
				if (editpos>0) 
				{
					memmove(editstr+editpos-1,editstr+editpos,strlen(editstr)-editpos+1);
					editpos--;
				}			
				editvol=0;
			}
			else
			{
				doc->channels[doc->curchan].Clear(doc->curpos,1);
				doc->curpos--;
			}			
			redraw=1;
		}
		break;
	}
	if (editmode) editpos=bound(0,editpos,strlen(editstr));
	if (doc->curpos<0) doc->curpos=0;
	if (doc->curpos>=doc->maxtime) doc->curpos=doc->maxtime-1;
	doc->curpos= (doc->curpos/doc->zoom) * doc->zoom;
	if (doc->curpos != opos || doc->curchan != ochan)
	{
		if (GetAsyncKeyState(VK_SHIFT)<0)
		{
			doc->selpos2--;
			doc->selchan2--;
			if (ochan==doc->selchan2 && opos==doc->selpos2)
			{
				doc->selchan2=doc->curchan;
				doc->selpos2=doc->curpos;
			}
			else
			if (ochan==doc->selchan1 && opos==doc->selpos1)
			{
				doc->selchan1=doc->curchan;
				doc->selpos1=doc->curpos;
			}
			else
			{
				doc->selchan1=ochan;
				doc->selpos1=opos;
				doc->selchan2=doc->curchan;
				doc->selpos2=doc->curpos;
			}
			if (doc->selchan1>doc->selchan2) 
			{
				int t=doc->selchan2;doc->selchan2=doc->selchan1;doc->selchan1=t;
			}
			if (doc->selpos1>doc->selpos2) 
			{
				int t=doc->selpos2;doc->selpos2=doc->selpos1;doc->selpos1=t;
			}
			doc->selpos2++;
			doc->selchan2++;
		}
	}
	if (redraw) InvalidateRect(NULL);
	CAlexView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CTextView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	
	CAlexView::OnKeyUp(nChar, nRepCnt, nFlags);
}

void CTextView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	clickmode=1;
	clickpos=point;
	SetCapture();
	clickrow=doc->GetRow(point.y);
	clickchan=doc->GetChan(point.x);
	OnMouseMove(nFlags,point);
	
	CAlexView::OnLButtonDown(nFlags, point);
}

void CTextView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	clickmode=0;
	ReleaseCapture();
	
	CAlexView::OnLButtonUp(nFlags, point);
}

void CTextView::OnMouseMove(UINT nFlags, CPoint point) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	if (clickmode)
	{
		int row =doc->GetRow(point.y);
		int chan=doc->GetChan(point.x);
		doc->curpos=row;
		doc->curchan=chan;
		if (clickchan!=chan || clickrow!=row)
		{		
			doc->selchan1=min(clickchan,chan);
			doc->selchan2=max(clickchan,chan)+1;
			doc->selpos1=min(clickrow,row);
			doc->selpos2=max(clickrow,row)+1;
		}
		InvalidateRect(NULL);
	}
	
	CAlexView::OnMouseMove(nFlags, point);
}

BOOL CTextView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	doc->scrollpos-=zDelta * doc->zoom / 30;
	if (doc->scrollpos>doc->maxtime-h/8-4) doc->scrollpos=doc->maxtime-h/8-4;
	if (doc->scrollpos<0) doc->scrollpos=0;
	InvalidateRect(NULL);
	
	return CAlexView::OnMouseWheel(nFlags, zDelta, pt);
}

void CTextView::OnSize(UINT nType, int cx, int cy) 
{
	CAlexView::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here
	
}

BOOL CTextView::OnEraseBkgnd(CDC* pDC) 
{
	// TODO: Add your message handler code here and/or call default
	
	return CAlexView::OnEraseBkgnd(pDC);
}

void CTextView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	int redraw=0;
	CThingDoc *doc = (CThingDoc*)GetDocument();
	if (isalnum(nChar) || nChar=='.' || nChar=='-' || nChar=='#' || nChar=='+' || nChar==' ')
	{
		if (!editmode && !(nChar=='>' || nChar=='=' || nChar=='.' || nChar==' '))
		{
			editmode=1;
			Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
			d.Format(editstr,doc->channels[doc->curchan].type, doc->channels[doc->curchan].width);
			editpos=strlen(editstr);
			editvol=1;
		}
		if (editmode)
		{				
			if (editvol)
			{
				if (!(nChar=='>' || nChar=='='))
				{
					Data d=doc->channels[doc->curchan].GetVal(doc->curpos);
					d.Format(editstr,doc->channels[doc->curchan].type, doc->channels[doc->curchan].width);
					if (d.ramp)
					{
						editpos=1;
						editstr[0]='>';
						editstr[1]=0;
					}
					else
					{
						editpos=0;
						editstr[0]=0;
					}
				}
				else
				{
				
					editpos=0;
					editstr[0]=0;
				}
			}
			memmove(editstr+editpos+1,editstr+editpos,strlen(editstr)-editpos+1);
			editstr[editpos]=nChar;
			editpos++;
			redraw=1;
			editvol=0;
		}
	}
	if (redraw) InvalidateRect(NULL);
	
	CAlexView::OnChar(nChar, nRepCnt, nFlags);
}

void CTextView::OnTimer(UINT nIDEvent) 
{
	CThingDoc *doc = (CThingDoc*)GetDocument();
	static int count=0;
	count++;
	if (doc->playing || count>8)
	{
		count=0;
		InvalidateRect(NULL);
	}	
	CAlexView::OnTimer(nIDEvent);
}

void CTextView::OnDestroy() 
{
	KillTimer(23);
	CAlexView::OnDestroy();
	
	
	
}
