/***************************************************************************
 *   Copyright (C) 2005 by yodor   *
 *   yodor@developer.bg  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   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.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <kconfig.h>

#include <kiconloader.h>
#include <kpopupmenu.h>
#include <kmessagebox.h>
#include <qcombobox.h>
#include <qstring.h>
#include <qpainter.h>

#include <unistd.h>

#include "mydock.h"

int a;
int b;

mydock::mydock() : KSystemTray()
{
	
	c_icon=4;
	msgSeq = 0;
	connect(this,SIGNAL(quitSelected()),this,SLOT(saveState()));

	ifupdater = new QTimer(this);
	connect(ifupdater,SIGNAL(timeout()),this,SLOT(update()));

	netset=new NetSettingsDialog();
	
	connect(this, SIGNAL(updateStats(const net_device_stats *, const IfStat *)), netset, SLOT(updateStats(const net_device_stats *, const IfStat *)));
	connect(this,SIGNAL(updateInfo(const AddrStat*)), netset,SLOT(updateInfo(const AddrStat*)));
	connect(this,SIGNAL(updateWiFi(const WiFiStat*)), netset, SLOT(updateWiFi(const WiFiStat*)));

	connect(netset->cboDevices,SIGNAL(activated(const QString&)),this,SLOT(setIface(const QString&)));
	connect(netset->cboDevices,SIGNAL(activated(const QString&)),this,SLOT(saveState()));

	
	contextMenu()->insertItem( "&Settings",  this, SLOT(showSettings()) );
	nlsock=0;
	fdsock=0;
	stats = new iw_statistics;

	//popup = new KPassivePopup( this );
	//popup = new InfoPopup();
// 	popup->setBackgroundMode(NoBackground);
// 	popup->resize(300,100);
// 	popup->show();
	
	//ttip = new InfoPopup(popup);
	//ttip->setBackgroundMode(NoBackground);
    	//popup->setView( ttip );
	a++;
	r_index=a;

	popup = new TTip();
	//QPoint ps = KickerLib::popupPosition(0,popup,this);
}


mydock::~mydock()
{
	delete netset;
	delete allifs;
	delete stats;
	delete ifupdater;
	delete popup;
	
}
int mydock::getBack()
{
return b;
}
int mydock::getAhead()
{
return a;
}

void mydock::enterEvent(QEvent *e)
{
/*
	int grabx = mapToGlobal(e->pos()).x();
        int graby = mapToGlobal(e->pos()).y();
	popup->move(grabx,graby);
*/
	if (popup->isShown() || contextMenu()->isShown() || netset->isShown())return;
	QPoint loc = mapToGlobal(mapFromParent(QPoint(0,0)));
	QPoint np = QPoint(loc.x()-popup->width(),loc.y()-popup->height());
	if (np.x()<0)
	{
		np = QPoint(loc.x(),np.y());
	}
	if (np.y()<0)
	{
		np = QPoint(np.x(),loc.y());
	}
	popup->move(np);
	popup->show();

}
void mydock::mouseEnterEvent(QMouseEvent *e)
{
	
}
void mydock::hideInstance()
{
	QWidget::hide();
	b++;
	saveState();
	close(true);
}
void mydock::leaveEvent(QEvent *e)
{
	popup->hide();	
}
void mydock::saveState()
{
	KConfig *cfg = new KConfig("knetdockapprc");
	cfg->setGroup("Interface"+QString("%1").arg(r_index));
	cfg->writeEntry("name",netset->cboDevices->currentText());
	cfg->writeEntry("index",netset->cboDevices->currentItem());
	cfg->setGroup("Instances");
	cfg->writeEntry("number",a-b);
	
	cfg->sync();
}
void mydock::start()
{
	queryDevices();
	KConfig *cfg = new KConfig("knetdockapprc");
	cfg->setGroup("Interface"+QString("%1").arg(r_index));
	int sel_index=cfg->readNumEntry("index",0);
	QString sel_name=cfg->readEntry("name","lo");
	netset->cboDevices->setCurrentItem(sel_index);
	setIface(sel_name);
	saveState();
	
}

void mydock::paintEvent (QPaintEvent *)
{
   if (isHidden())return;
   QPainter qpainter (this);
   
   switch (c_icon)
   {
    case 0: 
		qpainter.drawPixmap (0, 0, UserIcon("off"));	
		break;
    case 1: 
		qpainter.drawPixmap (0, 0, UserIcon("off"));
		qpainter.drawPixmap (7, 2, UserIcon("ifled"));	
		break;
    case 2:
		qpainter.drawPixmap (0, 0, UserIcon("off"));
		qpainter.drawPixmap (7, 14, UserIcon("ifled"));	
		break;
    case 3:
	qpainter.drawPixmap (0, 0, UserIcon("off"));
	qpainter.drawPixmap (7, 2, UserIcon("ifled"));	
	qpainter.drawPixmap (7, 14, UserIcon("ifled"));	
	break;
    case 4:
	qpainter.drawPixmap (0, 0, UserIcon("linkdown"));	
	break;
    case 5:
	qpainter.drawPixmap (0,0, UserIcon("off"));
	qpainter.fillRect(7,2,10,8,Qt::gray);
	qpainter.fillRect(7,14,10,8,Qt::gray);
	break;
    }
}

void mydock::updateTip(const QString& tip, double speed, const QString& lbl)
{
//+QString("%1").arg(r_index)
		popup->updateTip(tip,speed,lbl);
}

void mydock::updateDockIcon(int mode)
{
    c_icon=mode; 
    repaint();
}

void mydock::mousePressEvent(QMouseEvent *e)
{
    
    if (e->button() == RightButton) 
    {
	KSystemTray::mousePressEvent(e);
    }
    else
    {
		if (netset->isShown())
		{
			netset->hide();
		}
		else
		{
			//parent->setWindowState(parent->windowState() & ~WindowMinimized | WindowActive);
			netset->showNormal();
		}
    }
	
}
void mydock::mouseReleaseEvent(QMouseEvent *e)
{
	popup->hide();
}
void mydock::showSettings()
{
	netset->showNormal();
}
void mydock::setIface(const QString& iface)
{
    if (iface==QString::null)return;

    if_name = iface;
    if (!ifupdater->isActive())	ifupdater->start(1000);
    
	emit ifacechange();
	netset->cboDevices->setCurrentText(iface);
}

bool mydock::queryDevices()
{
	netset->cboDevices->clear();

	fdsock = socket(AF_INET, SOCK_DGRAM, 0);
	if(fdsock == -1)
	{
		return false;
	}

	struct nlmsghdr *nlMsg;
	struct ifinfomsg *ifInfo;
	struct rtattr *rtAttr;

	
	int len, rtLen, msgSeq = 0;

	//used later in updates
	if((nlsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
	{
		return false;
	}

	memset(msgBuf, 0, NL_BUFSIZE);

	nlMsg = (struct nlmsghdr *)msgBuf;
	
	nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
	nlMsg->nlmsg_type = RTM_GETLINK;
	nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
	nlMsg->nlmsg_seq = msgSeq++;
	nlMsg->nlmsg_pid = getpid();
	
	if(write(nlsock, nlMsg, nlMsg->nlmsg_len) < 0){
		
		return false;
	}

	if((len = readSock(nlsock, msgBuf, msgSeq, getpid())) < 0){
		
		return false;
	}
	
	int count=0;

	for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len))
	{
		ifInfo = (struct ifinfomsg *)NLMSG_DATA(nlMsg);
	
		rtAttr = (struct rtattr *)IFLA_RTA(ifInfo);
		rtLen = IFLA_PAYLOAD(nlMsg);
		for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen))
		{
			if (rtAttr->rta_type == IFLA_IFNAME)
			{
				netset->cboDevices->insertItem((const char *)RTA_DATA(rtAttr));
				count++;
			}
		}	
	}
	
	struct iwreq wireq;
	
	allifs=new KNetDockIf[count];


	for (int a=0;a<count;a++)
	{
			allifs[a].total = new IfStat;
			allifs[a].oldstat = new IfStat;
			allifs[a].wstat = new WiFiStat;
			allifs[a].addrstat= new AddrStat;

			const char *ifname=netset->cboDevices->text(a);
			strncpy(wireq.ifr_name, ifname, IFNAMSIZ);

			if(ioctl(fdsock, SIOCGIWNAME, &wireq) == -1)
			{
				allifs[a].wr=false;
			}
			else
			{
				allifs[a].wr=true;
			}
			
			allifs[a].oldstat->bytes=0ULL;
			allifs[a].oldstat->bytes_in=0ULL;
			allifs[a].oldstat->bytes_out=0ULL;

			allifs[a].total->bytes=0ULL;
			allifs[a].total->bytes_in=0ULL;
			allifs[a].total->bytes_out=0ULL;

			allifs[a].addrstat->ip="N/A";
			allifs[a].addrstat->mask="N/A";
			allifs[a].addrstat->mac="N/A";
		
			allifs[a].wstat->link_qual=0;
			allifs[a].wstat->signal_level=0;
			allifs[a].wstat->noise_level=0;
			
	}
	return true;
}
void mydock::disableDevice()
{
	updateTip(if_name + "\nUnable to qurey device");
	updateDockIcon(4);
	netset->clearInfo();
	netset->clearStats();
	netset->clearWiFi();
}

void mydock::update()
{
	int iconstatus=0;
	bool m_link_down=false;
	bool found=false;
	
	struct ifreq devifreq;

	QString essid="";


	int v = netset->cboDevices->currentItem();
	allifs[v].wstat->link_qual=0;
	allifs[v].wstat->noise_level=0;
	allifs[v].wstat->signal_level=0;	
	netset->frmWrl->setEnabled(false);
	
    if (if_name =="")
    {
		disableDevice();
		return;
    }

	struct ifconf ifc;
	char buffer[8192];
	ifc.ifc_len = sizeof(buffer);
	ifc.ifc_buf = buffer;

	if(ioctl(fdsock, SIOCGIFCONF, &ifc) == -1)
	{
		disableDevice();
		
		return;
	}
	
	int count = ifc.ifc_len / sizeof(ifreq);

	for(int i = 0; i < count; i++)
	{

			strcpy(devifreq.ifr_name, ifc.ifc_req[i].ifr_name);
	
			if (QString(devifreq.ifr_name) == if_name)
			{
				found=true;
					
				

				if (ioctl(fdsock,SIOCGIFFLAGS,&devifreq) < 0)
				{
					m_link_down=true;
					updateTip(if_name + "\nUnable to qurey flags");
					iconstatus=4;
				}
				else
				{
					
					
					if ((devifreq.ifr_flags & IFF_UP)!=0)
					{
						if ((devifreq.ifr_flags & IFF_RUNNING)!=0 )
						{
							m_link_down=false;
							
						}
						else
						{
							m_link_down=true;
							updateTip(if_name + " no link");
							iconstatus=4;
						}
					}
					else
					{
						m_link_down=true;
						updateTip(if_name + " device down");
						iconstatus=5;
						
					}
				}
			
				if (ioctl(fdsock, SIOCGIFADDR, &devifreq)<0) 
				{
					
					allifs[v].addrstat->ip="N/A";
				}
				else
				{
					sockaddr_in sin = ((sockaddr_in&)devifreq.ifr_addr);
					allifs[v].addrstat->ip=QString(inet_ntoa(sin.sin_addr));
				}	
				if (ioctl(fdsock, SIOCGIFNETMASK, &devifreq)<0) 
				{
					allifs[v].addrstat->mask="N/A";
				}
				else
				{
					sockaddr_in mask = ((sockaddr_in&)devifreq.ifr_netmask);
					allifs[v].addrstat->mask=QString(inet_ntoa(mask.sin_addr));
				}
				
	
				if(ioctl(fdsock, SIOCGIFHWADDR,  &devifreq)<0)
				{
					
					allifs[v].addrstat->mac="N/A";
				}
				else
				{
					unsigned char *hw = (unsigned char*)devifreq.ifr_hwaddr.sa_data;
	
					QString mac = QString("%1:%2:%3:%4:%5:%6").arg(hw[0],2,16)
							.arg(hw[1],2,16)
							.arg(hw[2],2,16)
							.arg(hw[3],2,16)
							.arg(hw[4],2,16)
							.arg(hw[5],2,16);
					allifs[v].addrstat->mac=mac;
				}
				emit updateInfo(allifs[v].addrstat);


				if (allifs[v].wr)
				{
					struct iwreq wireq;
					struct iw_range *range;

					const char *ifname=if_name;
					strncpy(wireq.ifr_name, ifname, IFNAMSIZ);
	
					wireq.u.data.pointer = (caddr_t) stats;
					wireq.u.data.length = sizeof(struct iw_statistics);
					wireq.u.data.flags = 1;
					
					if(ioctl(fdsock, SIOCGIWSTATS, &wireq) < 0)
					{
						
					}
					else
					{
						
						allifs[v].wstat->link_qual=stats->qual.qual;
						allifs[v].wstat->noise_level=stats->qual.noise;
						allifs[v].wstat->signal_level=stats->qual.level;	
						
						netset->frmWrl->setEnabled(true);
						
						memset(rngbuf, 0, sizeof(rngbuf));
						
						wireq.u.data.pointer = (caddr_t) rngbuf;
						wireq.u.data.length = sizeof(rngbuf);
						wireq.u.data.flags = 0;

						allifs[v].wstat->essid="N/A";

						if(ioctl(fdsock, SIOCGIWRANGE, &wireq) < 0)
						{
							
						}
						else
						{
							/* Copy stuff at the right place, ignore extra */
							range = (struct iw_range *) rngbuf;	
								
							if(ioctl(fdsock,  SIOCGIWESSID, &wireq) < 0)
							{

							}
							else
							{
								const char * nm = (const char *)wireq.u.essid.pointer;
								allifs[v].wstat->essid=QString(nm);
							}
						}
					}
					

				}
				
				emit updateWiFi(allifs[v].wstat);
				essid = allifs[v].wstat->essid;
				break;
			}
		
	}
	if (!found)
	{		
		iconstatus=5;
		updateDockIcon(iconstatus);
		updateTip(if_name + " device not found");
		disableDevice();
		return;
	}
	

	if (m_link_down==false)
	{
		
		int len;

		struct nlmsghdr *nlMsg;
		//char msgBuf[NL_BUFSIZE];
		memset(msgBuf, 0, NL_BUFSIZE);
		
		nlMsg = (struct nlmsghdr *)msgBuf;
		
		
		/* For getting interface addresses */
		nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
		nlMsg->nlmsg_type = RTM_GETLINK;
		nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
		nlMsg->nlmsg_seq = msgSeq++;
		nlMsg->nlmsg_pid = getpid();
	
	
		if(write(nlsock, nlMsg, nlMsg->nlmsg_len) < 0)
		{
				disableDevice();
				return;
		}
		
		if((len = readSock(nlsock, msgBuf, msgSeq, getpid())) < 0)
		{
				disableDevice();
				return;
		}
		
		struct ifinfomsg *ifInfo;
		struct rtattr *rtAttr;
		int rtLen;

		
		unsigned long long int byte_sum = 0ULL;

		bool recv_up=false;
		bool send_up=false;
		bool fnd=false;
		const char *nm;

		struct net_device_stats *stats;

		for(;NLMSG_OK(nlMsg,len);nlMsg = NLMSG_NEXT(nlMsg,len))
		{
			ifInfo = (struct ifinfomsg *)NLMSG_DATA(nlMsg);
		
			rtAttr = (struct rtattr *)IFLA_RTA(ifInfo);
		
			rtLen = IFLA_PAYLOAD(nlMsg);
			
			for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen))
			{
				switch (rtAttr->rta_type)
				{
					case IFLA_IFNAME:
						nm = (const char *)RTA_DATA(rtAttr);
						if (nm == if_name)
						{
						    fnd=true;
						}
						break;
					case IFLA_STATS:
						if (fnd)
						{
							stats = (struct net_device_stats*)RTA_DATA(rtAttr);
							byte_sum = (unsigned long long int)(stats->rx_bytes + stats->tx_bytes);
							//			Y,X
	
							if (allifs[v].oldstat->bytes_in>stats->rx_bytes)
								allifs[v].oldstat->bytes_in=0ULL;
							if (allifs[v].oldstat->bytes_out>stats->tx_bytes)
								allifs[v].oldstat->bytes_out=0ULL;
							
							unsigned long long int indelta=stats->rx_bytes-allifs[v].oldstat->bytes_in;
							unsigned long long int outdelta=stats->tx_bytes-allifs[v].oldstat->bytes_out;
							
							if (indelta > 0)
							{
								recv_up=true;
								iconstatus=1;
							}
							if (outdelta > 0)
							{
								send_up=true;
								iconstatus=2;
							}
							if (recv_up && send_up) iconstatus=3;
							
							allifs[v].total->bytes_in+=indelta;
							allifs[v].total->bytes_out+=outdelta;
	
							allifs[v].oldstat->bytes_in=stats->rx_bytes;
							allifs[v].oldstat->bytes_out=stats->tx_bytes;
						
							allifs[v].total->speed=(indelta+outdelta);
							allifs[v].total->speed_in=indelta;
							allifs[v].total->speed_out=outdelta;
	
							emit updateStats(stats, allifs[v].total);
							QString c="";
							
							QString str = "\nMonitoring: " + if_name;
								str += QString("\nRX Bytes: %1").arg(netset->getScaleString(allifs[v].total->bytes_in,&c),0,'f',2)+c;
								str += QString("\nTX Bytes: %1").arg(netset->getScaleString(allifs[v].total->bytes_out,&c),0,'f',2)+c;
							if (allifs[v].wr)
							{
								str+=QString("\nESSID: "+essid);
							}
							double ret = netset->getScaleString(allifs[v].total->speed,&c);
							updateTip( str , ret, c);
						}
						break;
				} //switch 
				
			} // rtAttr
			if (fnd)
			{
				break;
			}
		} //nlMsg
	}
	else
	{
		netset->clearStats();
	}
	
	updateDockIcon(iconstatus);
}


int mydock::readSock(int sockFd, char *bufPtr, int seqNum, int pId)
{
        struct nlmsghdr *nlHdr;
        int readLen = 0, flag = 0, msgLen = 0;

        do{
                /* Recieve response from kernel */
                if((readLen = recv(sockFd, bufPtr, NL_BUFSIZE - msgLen, 0)) < 0){
                        perror("SOCK READ: ");
                        return -1;
                }
                nlHdr = (struct nlmsghdr *)bufPtr;

                /* Check if header is valid */
                if((NLMSG_OK(nlHdr, readLen) == 0) || (nlHdr->nlmsg_type == NLMSG_ERROR)){
                        perror("Error in recieved packet");
                        return -1;
                }

                /* Check if its the last message */
                if(nlHdr->nlmsg_type == NLMSG_DONE)     {
                        flag = 1;
                        break;
                }
                else{
                        /* Move the buffer pointer appropriately */
                        bufPtr += readLen;
                        msgLen += readLen;
                }
                if((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0){
                        flag = 1;
                        break;
                }
        } while((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId) || (flag == 0));
        return msgLen;
}
