#include "scurl.h"

#include <QFileInfo>
#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QNetworkAddressEntry>

int SCURL::m_nUsage = 0;

quint64 SCURL::m_nFileSize = 0;
quint64 SCURL::m_nCurrentRead = 0;

int SCURL::m_nPrevCount = 0;
int SCURL::m_nErrorCount = 0;


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif

#include <sys/socket.h>

#include "mainwindow.h"

/* <DESC>
 * Performs an FTP upload and renames the file just after a successful
 * transfer.
 * </DESC>
 */

SCURL* g_pThis = NULL;


typedef struct _GlobalInfo
{
  CURLM *multi;
  int still_running;
} GlobalInfo;

/* Information associated with a specific easy handle */
typedef struct _ConnInfo
{
  CURL *easy;
  char *url;
  GlobalInfo *global;
  char error[CURL_ERROR_SIZE];
} ConnInfo;



static curl_socket_t opensocket(void *clientp,
                                curlsocktype purpose,
                                struct curl_sockaddr *address)
{
  g_pThis->m_SocketFD = *(curl_socket_t *)clientp;
  /* the actual externally set socket is passed in via the OPENSOCKETDATA
     option */
  return g_pThis->m_SocketFD;
}

static int sockopt_callback(void *clientp, curl_socket_t curlfd,
                            curlsocktype purpose)
{
  /* This return code was added in libcurl 7.21.5 */
  return CURL_SOCKOPT_ALREADY_CONNECTED;
}





//static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
size_t SCURL::read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
    unsigned long nread;
    /* in real-world cases, this would probably get this data differently
     as this fread() stuff is exactly what the library already would do
     by default internally */
    size_t retcode = fread(ptr, size, nmemb, (FILE*)stream);

    m_nCurrentRead += retcode;

    g_pThis->SetProgressExportPercent(m_nCurrentRead, m_nFileSize);

    usleep(10);

    if(retcode > 0)
    {
        nread = (unsigned long)retcode;
        //fprintf(stderr, "*** We read %lu bytes from file\n", nread);
    }

    return retcode;
}

int SCURL::CurlProgress(void *clientp,   curl_off_t dltotal,   curl_off_t dlnow,   curl_off_t ultotal,   curl_off_t ulnow)
{

    if(g_pThis->m_bStop==true)
    {
        curl_socket_t sockfd;
        auto res = curl_easy_getinfo(g_pThis->m_pCURL, CURLINFO_ACTIVESOCKET, &sockfd);
        // ASSERT_EQ(res, CURLE_OK); - no point in doing anything if the socket is not there
        close(sockfd);
        curl_easy_reset( g_pThis->m_pCURL );
        return CURLE_UNRECOVERABLE_POLL;
    }

    if( dltotal > 0 || dlnow > 0 || ultotal > 0 || ulnow > 0)
    {
        g_pThis->m_nFileSize = ultotal;
        g_pThis->m_nCurrentRead = ulnow;

        if(g_pThis->m_nCurrentRead==g_pThis->m_nPrevCount)
        {
            g_pThis->m_nErrorCount++;

            if(g_pThis->m_nErrorCount>5)
            {
                curl_socket_t sockfd;
                auto res = curl_easy_getinfo(g_pThis->m_pCURL, CURLINFO_ACTIVESOCKET, &sockfd);
                // ASSERT_EQ(res, CURLE_OK); - no point in doing anything if the socket is not there
                close(sockfd);
                curl_easy_reset( g_pThis->m_pCURL );
                return CURLE_UNRECOVERABLE_POLL;
            }
        }
        else
        {
            g_pThis->m_nErrorCount = 0;
        }

        g_pThis->m_nPrevCount = ulnow;


        //static int m_nPrevCount;
        //static int m_nErrorCount;
        //fprintf(stderr, "Up: %d/%d\n", ulnow, ultotal);
        //CryLogAlways("UP: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T "  DOWN: %" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T "\r\n", ulnow, ultotal, dlnow, dltotal);
    }
    return CURLE_OK;
}

SCURL::SCURL()
{
    m_nUsage++;
    g_pThis = this;

    m_bStop = false;

    connect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimer()));
}

SCURL::~SCURL()
{
    m_nUsage--;

    disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(OnTimer()));
}

void SCURL::OnTimer()
{
    if(m_nPrevCount==m_nCurrentRead)
    {
        m_nErrorCount++;

        if(m_nErrorCount>2)
        {
            curl_easy_reset( m_pCURL );
            m_Timer.stop();
        }
    }
    else
    {
        m_nErrorCount = 0;
    }
    m_nPrevCount = m_nCurrentRead;
}

void SCURL::Init()
{
    curl_global_init(CURL_GLOBAL_ALL);
}

void SCURL::Release()
{
    curl_global_cleanup();
}

int SCURL::UploadFTP2(QString strFilename)
{
    return 0;
}

int SCURL::UploadFTP(QString strFilename)
{
    if(IsConnectNetwork()==false)
    {
        return -11;
    }

    CURLcode res;
    FILE *hd_src;
    struct stat file_info;
    unsigned long fsize;

    struct curl_slist *headerlist = NULL;
    //static const char buf_1 [] = "RNFR " UPLOAD_FILE_AS;
    //static const char buf_2 [] = "RNTO " RENAME_FILE_TO;

    QFileInfo fi(strFilename);
    QString strFile = fi.fileName();

    QString strPath = fi.path();

    strPath = strPath.replace("/data/video/", "");

    int nRet = 0;

    CommonData* pCommonData = MainWindow::GetCommonData();
    NetworkShareInfo* pShareInfo = pCommonData->GetNetworkShareInfo();

    QString strID;
    QString strPassword;
    int nPort = 21;
    bool bUseSSL = false;

    strID = pShareInfo->m_strID;
    strPassword = pShareInfo->m_strPassword;
    nPort = pShareInfo->m_strIP.toInt();
    QString strTargetFolder = pShareInfo->m_strShareFolder;

    if(pShareInfo->m_strVersion.contains("SSL")==true)
    {
        bUseSSL = true;
    }


    //QString strUrl = QString("ftp://192.168.1.19/OK/%1/%2").arg(strPath).arg(strFile);
    QString strUrl = QString("%1/%2/%3").arg(strTargetFolder).arg(strPath).arg(strFile);



    /* get the file size of the local file */
    if(stat(strFilename.toStdString().c_str(), &file_info)) {
        printf("Couldn't open '%s': %s\n", strFilename.toStdString().c_str(), strerror(errno));
        return -1;
    }
    fsize = (unsigned long)file_info.st_size;

    m_nFileSize = fsize;
    m_nCurrentRead = 0;
    m_nErrorCount = 0;
    m_nPrevCount = -1;

    SetProgressExportPercent(m_nCurrentRead, m_nFileSize);

    printf("Local file size: %lu bytes.\n", fsize);

    /* get a FILE * of the same file */
    hd_src = fopen(strFilename.toStdString().c_str(), "rb");


    /* get a curl handle */
    m_pCURL = curl_easy_init();


    if(m_pCURL) {

        /* build a list of commands to pass to libcurl */
        //headerlist = curl_slist_append(headerlist, strFileFrom.toStdString().c_str());
        //headerlist = curl_slist_append(headerlist, strFileTo.toStdString().c_str());

        /* call this function to get a socket */
        //curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket);
        //curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &m_SocketFD);

        /* call this function to set options for the socket */
        //curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);

        curl_easy_setopt(m_pCURL, CURLOPT_CONNECTTIMEOUT_MS, 1000L);  //1 Second
        curl_easy_setopt(m_pCURL, CURLOPT_TIMEOUT_MS, 1000*60*10); //10 Minutes

        curl_easy_setopt(m_pCURL, CURLOPT_PROTOCOLS, CURLPROTO_FTP);

        /* we want to use our own read function */
        curl_easy_setopt(m_pCURL, CURLOPT_READFUNCTION, read_callback);

        /* enable uploading */
        curl_easy_setopt(m_pCURL, CURLOPT_UPLOAD, 1L);

        /* specify target */
        curl_easy_setopt(m_pCURL, CURLOPT_URL, strUrl.toStdString().c_str());

        /* now specify which file to upload */
        curl_easy_setopt(m_pCURL, CURLOPT_READDATA, hd_src);

        /* Set the size of the file to upload (optional).  If you give a *_LARGE
           option you MUST make sure that the type of the passed-in argument is a
           curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must
           make sure that to pass in a type 'long' argument. */
        curl_easy_setopt(m_pCURL, CURLOPT_INFILESIZE_LARGE,
                         (curl_off_t)fsize);


        curl_easy_setopt(m_pCURL, CURLOPT_FTP_CREATE_MISSING_DIRS,
                           (long)CURLFTP_CREATE_DIR);

        //curl_easy_setopt(curl, CURLOPT_USERNAME, "birdhead");
        //curl_easy_setopt(curl, CURLOPT_PASSWORD, "1");
        curl_easy_setopt(m_pCURL, CURLOPT_USERNAME, strID.toStdString().c_str());
        curl_easy_setopt(m_pCURL, CURLOPT_PASSWORD, strPassword.toStdString().c_str());
        curl_easy_setopt(m_pCURL, CURLOPT_PORT, nPort);
        //curl_easy_setopt(m_pCURL, CURLOPT_VERBOSE, 1L);
        curl_easy_setopt(m_pCURL, CURLOPT_VERBOSE, 0L);
        curl_easy_setopt(m_pCURL, CURLOPT_LOGIN_OPTIONS, "AUTH=LOGIN");

        if(bUseSSL==true)
        {
            //curl_easy_setopt(m_pCURL, CURLOPT_FTP_USE_EPSV, 0L);

            /* require use of SSL for this, or fail */
            curl_easy_setopt(m_pCURL, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);

            //curl_easy_setopt(m_pCURL, CURLOPT_FTPPORT, nPort);

            curl_easy_setopt(m_pCURL, CURLOPT_SSL_VERIFYPEER, 0L);
            curl_easy_setopt(m_pCURL, CURLOPT_SSL_VERIFYHOST, 0L);


            //curl_easy_setopt(m_pCURL, CURLOPT_FTP_SSL, CURLFTPSSL_ALL);
            curl_easy_setopt(m_pCURL, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
            curl_easy_setopt(m_pCURL, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
            curl_easy_setopt(m_pCURL, CURLOPT_NOPROGRESS, 0L);

            //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            //curl_setopt($ch, CURLOPT_FTP_SSL, true);
            //curl_setopt($ch, CURLOPT_VERBOSE, true);
        }
        else
        {
            curl_easy_setopt(m_pCURL, CURLOPT_NOPROGRESS, 0L);
        }

        //curl_easy_setopt(m_pCURL, CURLOPT_TIMEOUT, 1L);

        curl_easy_setopt(m_pCURL, CURLOPT_XFERINFOFUNCTION, CurlProgress);

        //m_Timer.setSingleShot(true);
        //m_Timer.start(500);
        /* Now run off and do what you have been told! */
        res = curl_easy_perform(m_pCURL);
        //m_Timer.stop();
        /* Check for errors */
        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

            nRet = -res;
        }

        /* clean up the FTP commands list */
        curl_slist_free_all(headerlist);

        /* always cleanup */
        curl_easy_cleanup(m_pCURL);
    }
    fclose(hd_src); /* close the local file */

    return nRet;
}


bool SCURL::IsConnectNetwork()
{
    bool bIsConnect = false;

    QList<QNetworkInterface> listNetworkInterface =  QNetworkInterface::allInterfaces();

    int nCount = listNetworkInterface.size();
    int i=0;
    for(i=0 ; i<nCount ; i++)
    {
        QNetworkInterface ni = listNetworkInterface[i];

        //int nIndex = ni.index();
        //int nMaximumTransmissionUnit = ni.maximumTransmissionUnit();
        QString strName = ni.name();
        //QString strHumanReadableName = ni.humanReadableName();
        //InterfaceFlags flags() const;
        QNetworkInterface::InterfaceType nType = ni.type();
        //QString strHardwareAddress = ni.hardwareAddress();
        QList<QNetworkAddressEntry> listNetworkAddresssEntry =  ni.addressEntries();

        if(nType==QNetworkInterface::Ethernet)
        {
            if(strName.contains("eth")==true)
            {
                if(listNetworkAddresssEntry.size()==0)
                {
                    bIsConnect = false;
                }
                else
                {
                    bIsConnect = true;
                    /*
                    int j=0;
                    for(j=0 ; j<listNetworkAddresssEntry.size() ; j++)
                    {
                        QNetworkAddressEntry nae = listNetworkAddresssEntry[j];

                        QHostAddress ha = nae.ip();
                        bool bIsLocal = ha.isLinkLocal();
                        bool bIsGlobal = ha.isGlobal();

                    }
                    */
                }
                break;
            }
        }
    }
    return bIsConnect;
}


void SCURL::StopCURL()
{
    m_bStop = true;
}
