/*
 * Copyright (c) 1999 - 2005 NetGroup, Politecnico di Torino (Italy)
 * Copyright (c) 2005 - 2006 CACE Technologies, Davis (California)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Politecnico di Torino, CACE Technologies 
 * nor the names of its contributors may be used to endorse or promote 
 * products derived from this software without specific prior written 
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>

#include "stdafx.h"
#include "netmeter.h"

#include "netmeterDoc.h"
#include "netmeterView.h"
#include "console.h"
#include "selectadapter.h"
#include "..\..\include\pcap.h"

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

pcap_t *fp;
pcap_dumper_t *dumpfile;
struct _timeb OldTime;

/////////////////////////////////////////////////////////////////////////////
// CNetmeterView

IMPLEMENT_DYNCREATE(CNetmeterView, CView)

BEGIN_MESSAGE_MAP(CNetmeterView, CView)
	//{{AFX_MSG_MAP(CNetmeterView)
	ON_COMMAND(seladapter, OnSelAdapter)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CNetmeterView construction/destruction

CNetmeterView::CNetmeterView()
{
	//init the values
	time=0;
	TimeSlice=1000;
	delta=5;
	lastval1=0;
	lastval2=0;
	Adapter="";
	BytesCaptured=0;
	gridpen.CreatePen(PS_SOLID,0,RGB(0,150,0));
	diagrampen1.CreatePen(PS_SOLID,0,RGB(255,255,0));
	diagrampen2.CreatePen(PS_SOLID,0,RGB(0,255,255));
	InitializeCriticalSection(&Crit);

	//begin the capture

	CString result;
	if ((result=StartCapture())!=""){
		AfxMessageBox("Error initializing the capture:\n"+result); 
		exit(0);
	}

}

CNetmeterView::~CNetmeterView()
{
//stop the capture and close libpcap 
	StopCapture();
}

BOOL CNetmeterView::PreCreateWindow(CREATESTRUCT& cs)
{
	return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// function that starts the capture

CString CNetmeterView::StartCapture()
{
char *ebuf;
char *myAdapter;
pcap_if_t *alldevs;


	CCapPars *pars=new CCapPars;

	pars->prg=this;

	ebuf=(char*)malloc(PCAP_ERRBUF_SIZE);

	if (Adapter=="")
	{
		//If no adapter is defined choose the first

		/* Retrieve the device list on the local machine */
		/* Don't check for errors */
		pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, ebuf);

		myAdapter= alldevs->name;

		//open the adapter
		//note: the snaplen is 1 byte because we don't need the data
		//and the header of the packet, but only the length.
		//Snaplen is not set to 0 because with this value the filter
		//drops all the packet without copying them. Snaplen=0 can be 
		//used to count the packets with the minimun overhead on the system.

		if ( (fp= pcap_open((char*)myAdapter, 1, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, ebuf) ) == NULL)
		{
			return "PCAP error: Error opening the adapter";
		}

		pcap_freealldevs(alldevs);
	}
	else
	{
		if ( (fp= pcap_open(Adapter.GetBuffer(0), 1, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, ebuf) ) == NULL)
		{
			return "PCAP error: Error opening the adapter";
		}
	}



	//this example shows a correct diagram only on 10Mbit Ethernets
	if(pcap_datalink(fp)!=1/*Ether10*/)
	{
			AfxMessageBox("Warning: the nework adapter '"+Adapter+"' is not supported correctly by netmeter.\nNetmeter works correcly only with 10Mbit ethernet adapters.");
	}


	//set mode 1 (statistics mode).
	//When set in this mode, the capture driver DOES'T capture anything.
	//It only counts the packets and the number of bytes that satisfy the BPF filter,
	//and returns to the application this values every Mon.delay milliseconds.
	if(pcap_setmode(fp,MODE_STAT)==-1){
		pcap_close(fp);
		return "PCAP error setting count mode";
	}


	//We set a 256 bytes kernel buffer, big enough to contain the statistical summary created by the driver
	if(pcap_setbuff(fp, 256)<0){
		pcap_close(fp);
		return "Not enough memory to allocate the capture buffer\nTry to set a smaller buffer.";
	}

	pars->fp=fp;

	//start the capture thread
	thr=AfxBeginThread(MyThreadProc, pars);
	if(!thr) {
		pcap_close(fp);
		return "Error launching the capture thread.";
	}

	//get the time of the beginning of the capture
	_ftime( &OldTime );
	return "";


}

/////////////////////////////////////////////////////////////////////////////
// function that stops the capture

void CNetmeterView::StopCapture()
{

	HANDLE thread=thr->m_hThread;

	//kill the capture thread.
	BOOL res=TerminateThread(thread,0);
	
	if(res==false){
		AfxMessageBox("Fatal Error: cannot terminate the capture thread"); 
		exit(0);
	}
	//close the adapter
	pcap_close(fp);

	//wait the end of the capture thread
	WaitForSingleObject(thread,INFINITE);

}

/////////////////////////////////////////////////////////////////////////////
// Create a new blackboard

void CNetmeterView::CreateBoard(CDC* pDC,CDC *DrawBuff,RECT rett)
{
		int i;
		CDC dc;

		DrawBuff->CreateCompatibleDC(pDC);

		hBitmap=CreateCompatibleBitmap(*pDC,rett.right-rett.left,rett.bottom-rett.top);
		DrawBuff->SelectObject(hBitmap);

		DrawBuff->FillSolidRect(0,0,rett.right-rett.left,rett.bottom-rett.top,0);
		DrawBuff->SelectObject(gridpen);

		for(i=1;i<10;i++){
			DrawBuff->MoveTo(0,(rett.bottom-rett.top)*i/10);
			DrawBuff->LineTo(rett.right-rett.left,(rett.bottom-rett.top)*i/10);
		}
		for(i=1;i<=10;i++){
			DrawBuff->MoveTo((rett.right-rett.left)*i/10-1,0);
			DrawBuff->LineTo((rett.right-rett.left)*i/10-1,rett.bottom-rett.top);
		}

		DrawBuff->LineTo(rett.right-rett.left,rett.bottom-rett.top);

		DrawBoard(DrawBuff,rett,0,0);
		pDC->BitBlt( rett.left, rett.top, rett.right-rett.left, rett.bottom-rett.top, DrawBuff, 0, 0, SRCCOPY);

}

/////////////////////////////////////////////////////////////////////////////
// Scroll the blackboard and insert a new value

void CNetmeterView::DrawBoard(CDC* pDC,RECT rett,int height1,int height2)
{
	int i;
	int x,y;


	
	pDC->SelectObject(gridpen);
	pDC->BitBlt( 0, 0, rett.right-rett.left, rett.bottom-rett.top, pDC, delta, 0, SRCCOPY);
	pDC->FillSolidRect(rett.right-delta,0,rett.right,rett.bottom-rett.top,0);



	for(i=1;i<10;i++){
		pDC->MoveTo(rett.right-rett.left-delta,(rett.bottom-rett.top)*i/10);
		pDC->LineTo(rett.right-rett.left,(rett.bottom-rett.top)*i/10);
	}

	if(((time)*delta)/((rett.right-rett.left)/10) > ((time-1)*delta)/((rett.right-rett.left)/10))
	{
		pDC->MoveTo((rett.right-rett.left)*i/10-1,0);
		pDC->LineTo((rett.right-rett.left)*i/10-1,rett.bottom-rett.top);
	}

	// draw the line for the new PPS
	pDC->SelectObject(diagrampen2);

	x=rett.right-rett.left-delta-1;
	y=rett.bottom-rett.top-lastval2*(rett.bottom-rett.top)/100-1;
	pDC->MoveTo(x,y);
	pDC->SetPixelV(x,y,RGB(255,255,255));
	x=rett.right-rett.left-1;
	y=rett.bottom-rett.top-height2*(rett.bottom-rett.top)/100-1;
	pDC->LineTo(x,y);
	pDC->SetPixelV(x,y,RGB(255,255,255));
	lastval2=height2;

	// draw the line for the new BPS
	pDC->SelectObject(diagrampen1);

	x=rett.right-rett.left-delta-1;
	y=rett.bottom-rett.top-lastval1*(rett.bottom-rett.top)/100-1;
	pDC->MoveTo(x,y);
	pDC->SetPixelV(x,y,RGB(255,255,255));
	x=rett.right-rett.left-1;
	y=rett.bottom-rett.top-height1*(rett.bottom-rett.top)/100-1;
	pDC->LineTo(x,y);
	pDC->SetPixelV(x,y,RGB(255,255,255));
	lastval1=height1;
	
	//redraw
	if(time>0){
		Invalidate( FALSE );
		UpdateWindow();
	}
	time++;
}
/////////////////////////////////////////////////////////////////////////////
// CNetmeterView drawing

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

	CWnd* wind=pDC->GetWindow();
	wind->GetClientRect(&wrett);


	if(time==0){
		
		//write the name of the adapter in the title bar of the program's window
		CWnd *pater=this->GetParent();
		CString titlestr;
		CString tmpstr;

		tmpstr=Adapter.Right(12);
		if(tmpstr.GetLength()==Adapter.GetLength())
		 titlestr.Format("netmeter-%s",Adapter);
		else
		 titlestr.Format("netmeter-...%s",tmpstr);
		pater->SetWindowText(titlestr);
		
		CreateBoard(pDC,&DrawBuffer,wrett);
		time++;
	}

	if(time>0)pDC->BitBlt( wrett.left, wrett.top, wrett.right-wrett.left, wrett.bottom-wrett.top, &DrawBuffer, 0, 0, SRCCOPY);

}

/////////////////////////////////////////////////////////////////////////////
// CNetmeterView diagnostics

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

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

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



void CNetmeterView::OnSelAdapter() 
{
	CSelectAdapter dlg;

	//stop the current capture process
	StopCapture();

	//show the selectadapter dialog
	dlg.m_Adapter=Adapter;
	if (dlg.DoModal()==IDOK) Adapter=dlg.m_Adapter;
	
	//write the name of the adapter in the title bar of the program's window
	CWnd *pater=this->GetParent();
	CString titlestr;
	CString tmpstr;

	tmpstr=Adapter.Right(12);
	if(tmpstr.GetLength()==Adapter.GetLength())
	 titlestr.Format("netmeter-%s",Adapter);
	else
	 titlestr.Format("netmeter-...%s",tmpstr);
	pater->SetWindowText(titlestr);
	
	//start the new capture process
	StartCapture();
}
