/**********************************************************************

	--- Dlgedit generated file ---

	File: DCC.cpp
	Last generated: Thu Oct 30 23:25:06 1997

 *********************************************************************/

#include "DCC.h"
#include "DCC.moc"
#include "DCCData.moc"
#include "main.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <kmsgbox.h>
#include <qfiledlg.h>
#include <iostream.h>
#include <sys/ioctl.h>
#include <kapp.h>
#include <qdir.h>
#include <qdatetm.h>

#define Inherited DCCData

#define BLOCK_SIZE 8192
#define ATTEMPT_DELAY 500 //500 milliseconds

DCC::DCC
(
	QWidget* parent,
	const char* name
)
	:
	Inherited( parent, name )
{
    timer = new QTimer(this);
    speedTimer = new QTimer(this);
    rsn = NULL;
    wsn = NULL;
    stdIn = NULL;
    
    if (Settings::mode == "CHAT")
    {
        connect(timer, SIGNAL(timeout()), SLOT(Listen()));
        timer->start(ATTEMPT_DELAY); // check every half a second
        stdIn = new QSocketNotifier(0, QSocketNotifier::Read);
        QObject::connect(stdIn, SIGNAL(activated(int)), SLOT(ReadStdin()));
        if (Settings::port == "0")
            Listen();
        else
            Connect(Settings::address, atoi(Settings::port.data()));
        return;
    }
    
    QString caption;
    caption.sprintf("DCC %s", Settings::mode.data());
    setCaption(caption);
    setIconText(caption);
    secondsElapsed = 0;
    bytesRead = bytesWritten = bytesReceived = 0;
    multiplier = 100/(float)(atoi(Settings::fileSize.data()));
    progressBar->setTotalSteps(100);
    progressBar->setProgress(0);
    connect(speedTimer, SIGNAL(timeout()), SLOT(ShowSpeed()));
    //  debug("Total steps: %d", progressBar->totalSteps());
    
    if (Settings::mode == "SEND")
    {
        QString txt;
        txt.sprintf("Sending %s to %s.", Settings::fileName.data(), Settings::nick.data());
        fileLabel->setText(txt);
        file = new QFile(Settings::fileName);
        if (!file->open(IO_ReadOnly))
        {
            QString txt;
            txt.sprintf("Could not open file %s for reading!", Settings::fileName.data());
            fileLabel->setText(txt);
            Done();
            return;
        }
        connect(timer, SIGNAL(timeout()), SLOT(Listen()));
        timer->start(ATTEMPT_DELAY); // check every half a second
       Listen();
    }

    if (Settings::mode == "GET")
    {
        file = new QFile(Settings::fileName);
        debug("hello");
        int slash = Settings::fileName.findRev('/');
        QString dir = Settings::fileName.left(slash);
        int fileNotOK = file->exists();
        while (fileNotOK)
        {
            QString out;
            out.sprintf("The file %s already exists.", file->name());
            int ans = KMsgBox::yesNoCancel(NULL, "DCC GET", out, 0, "Rename", "Overwrite", "Cancel");
            switch (ans)
            {
                case 1:
                    file->setName(QFileDialog::getSaveFileName(dir));
                    fileNotOK = file->exists();
                    break;
                case 2:
                    fileNotOK = 0;
                    break;
                case 3:
                    exit(0);
                    break;
            };
        }
        //   debug("file does not exist");
        while (!(file->open(IO_WriteOnly)))
        {
            debug("could not open file for writing");
            file->setName(QFileDialog::getSaveFileName(dir));
        }
        QString txt;
        txt.sprintf("Getting %s from %s.", file->name(), Settings::nick.data());
        debug(txt);
        fileLabel->setText(txt);
// start at the current working directory and with *.cpp as filter
          /*  QString f = QFileDialog::getSaveFileName( 0, 0, this);
              if ( !f.isEmpty() )
              {
              Settings::fileName = f.copy();
              }
              else
              {
              exit(0);
              }*/
        if(Connect(Settings::address, atoi(Settings::port.data())) == -1)
        {
            QString txt;
            txt.sprintf("Could not connect to %s.", Settings::address.data());
            fileLabel->setText(txt);
            Done();
            return;
        }
    }

   
}


DCC::~DCC()
{
//    delete timer;
    //   delete speedTimer;
}

void DCC::cancelPressed()
{
    close(true);
}

int DCC::Connect(const char *address, int port)
{
    //debug("Address %s, Port %d", address, port); 
   int fd;
   struct sockaddr_in sin;
   struct hostent* hostent;
   struct protoent* p;
   
   if ((hostent = gethostbyname(address)) == NULL)
       return -1;
   // debug("got host name");
   memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);
    memcpy(  &sin.sin_addr, hostent->h_addr, hostent->h_length);
    p = getprotobyname("tcp");

    fd = socket(AF_INET, SOCK_STREAM, 0);
    fcntl(fd, F_SETFL, O_NONBLOCK); // make the socket non-blocking
    ::connect(fd, (struct sockaddr *) &sin, sizeof(sin));
    if (fd != -1)
    {
        //    debug("connected");
        speedTimer->start(1000); // fire the timer every second
        rsn = new QSocketNotifier(fd, QSocketNotifier::Read);
        wsn = new QSocketNotifier(fd, QSocketNotifier::Write);
        
        QObject::connect(rsn, SIGNAL(activated(int)), this, SLOT(ReadData()));
        QObject::connect(wsn, SIGNAL(activated(int)), this, SLOT(WriteData()));
        
        rsn->setEnabled(TRUE);
        wsn->setEnabled(TRUE);
        if (Settings::mode == "CHAT")
        {
            debug(">connect %s %u", Settings::nick.data(), fd);
        }
    }
    return fd;
}

int DCC::Listen()
{
    
//    debug(Settings::address);
    int fd = atoi(Settings::address.data());
    
    struct sockaddr_in newsock;
    int socksize = sizeof(struct sockaddr_in);
    //   debug("Connection attempt %d", numAttempts);
    int newfd = ::accept(fd, (struct sockaddr *) &newsock, &socksize); 
    if (newfd != -1)
    {
        // debug("connection established");
        // close(fd); // close the old connnection
        timer->stop(); // stop the timer
        speedTimer->start(1000); // start the speed timer
        rsn = new QSocketNotifier(newfd, QSocketNotifier::Read);
        wsn = new QSocketNotifier(newfd, QSocketNotifier::Write);
        
        QObject::connect(rsn, SIGNAL(activated(int)), this, SLOT(ReadData()));
        QObject::connect(wsn, SIGNAL(activated(int)), this, SLOT(WriteData()));
        
        rsn->setEnabled(TRUE);
        wsn->setEnabled(TRUE);
        if (Settings::mode == "CHAT")
        {
            debug(">connect %s %u", Settings::nick.data(), newfd);
        }
    }    
    return newfd;
}

void DCC::ReadData()
{
    if (!(rsn->isEnabled()))
        return;
    if (Settings::mode == "SEND")
    {
        int r;
        int len = read(rsn->socket(), (char *)&r, 4); // should be four bytes
        if (len != 4) // should be four bytes
        {
            QString out;
            out.sprintf("Lost DCC Send to %s", Settings::nick.data());
            fileLabel->setText(out);
            Done();
            return;
        }
        bytesReceived = ntohl(r);
        progressBar->setProgress((int)(bytesReceived*multiplier));
        if (bytesReceived == atoi(Settings::fileSize.data()))// have we sent the whole file?
        {
            QString txt;
            txt.sprintf("File %s succesfully sent to %s.", Settings::fileName.data(), Settings::nick.data());
            fileLabel->setText(txt);
            progressBar->setProgress(100);
            rsn->setEnabled(false);
            wsn->setEnabled(false);
            shutdown(rsn->socket(), 2);
            Done();
        }
        if (bytesWritten > bytesReceived)
        {
            //   debug("missed packet");
            return;
        }
        char buff[BLOCK_SIZE +1];
        bzero(buff, BLOCK_SIZE+1);
        if (file)
        {
            int read = file->readBlock(buff, BLOCK_SIZE);
            write(wsn->socket(), buff, read); // send it out to the socket
            bytesWritten += read;
        }
        else
            debug("warning: attempt to read when file is NULL");
    }
    else
    {
        char dat[8192];
        bzero(dat, 8192);
        int len = read(rsn->socket(), dat, 8192);
        if (Settings::mode == "GET")
        {
            if (len <= 0)
            {
                QString txt;
                txt.sprintf("Received EOF from %s", Settings::nick.data());
                fileLabel->setText(txt);
                Done();
                return;
            }
            bytesRead += len;
            progressBar->setProgress((int)(bytesRead*multiplier));
            file->writeBlock(dat, len); // write the data to the file
            int br = htonl(bytesRead); // we have to convert to network order
            write(wsn->socket(), (char *)&br, sizeof(int));
            if (bytesRead == atoi(Settings::fileSize.data()))
            {
                QString txt;
                txt.sprintf("File %s succesfully received from %s.", Settings::fileName.data(), Settings::nick.data());
                fileLabel->setText(txt);
                progressBar->setProgress(100);
                rsn->setEnabled(false);
                wsn->setEnabled(false);
                shutdown(rsn->socket(), 2);
                Done();
                return;
            }
        }
        else if (Settings::mode == "CHAT")
        {
            if (len <= 0)
            {
                debug(">closed %s", Settings::nick.data());
                close(true);
                return;
            }
            if (len > 0)
            debug("%s %s", Settings::nick.data(), dat);
            return;
        }            
        
//        debug(dat);
    }
    

}

void DCC::WriteData() 
{
    if (Settings::mode == "SEND")
    {
        char buff[BLOCK_SIZE +1];
        bzero(buff, BLOCK_SIZE+1);
        if (file)
        {
            int read = file->readBlock(buff, BLOCK_SIZE);
            write(wsn->socket(), buff, read); // send it out to the socket
            bytesWritten += read;
        }
        else
            debug("warning: attempt to read when file is NULL");
    }
    wsn->setEnabled(FALSE);
    
}

void DCC::ReadStdin()
{
    char dat[8192];
    bzero(dat, 8192);
    int len = read(stdIn->socket(), dat, 8192);
    write(wsn->socket(), dat, len);
}

void DCC::Send()
{
}

void DCC::Get()
{


}

void DCC::ShowSpeed()
{
    secondsElapsed++;
    int bps, bytesLeft;
    if (Settings::mode == "SEND")
    {
        bps = bytesWritten / secondsElapsed;
        bytesLeft = atoi(Settings::fileSize.data()) - bytesWritten;
    }
    else
    {
        bps = bytesRead / secondsElapsed;
        bytesLeft = atoi(Settings::fileSize.data()) - bytesRead;
    }
    if (bps == 0)
        return; // HATE divide by zero erros
    QString out;
    out.sprintf("%d cps", bps);
    speedLabel->setText(out);
    int seconds =  seconds = bytesLeft / bps;
    int hours=0, minutes=0;
    if (seconds >= 3600)
    {
        hours =3600/seconds;
        seconds = seconds%3600;
    }
    if (seconds >= 60)
    {
        minutes= seconds/60;
        seconds = seconds%60;
    }
    out = "";
    out.sprintf("ETA: %d : %d : %d", hours, minutes, seconds);
    timeRemainingLabel->setText(out);
}

void DCC::Done()
{
    speedTimer->stop();
    timer->stop();
    cancelButton->setText("Ok");
    if (rsn)
        rsn->setEnabled(FALSE);
    if (wsn)
        wsn->setEnabled(FALSE);
    file->close();
}

