#include <iostream>

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <getopt.h>

#include <signal.h>
#include <sys/param.h>
#include <sys/resource.h>

#include "../CUIMessageHandler.h"
#include "DaemonDM/MessageListener.h"

#include "Logger.h"

const char* const c_MessageListenerLog = "MessageListener";

using namespace std;

using namespace NS_DM_Client;

//------------------------------------------------------------------------------------------------------
volatile sig_atomic_t gGracefulShutdown = 0;
volatile sig_atomic_t gCaughtHupSignal  = 0;
int gLockFileDesc = -1;

const char* const gLockFilePath = "/tmp/funambol_messageListener.pid";
const char* cApplicationName    = "Funambol Message listener test";


const char* const c_clSmallOptions = "hasfi:c:o:d:";
const struct option LongOptions[] =
{
 {"help",       0,      NULL,       c_clSmallOptions[0]},
 {"alive",      0,      NULL,       c_clSmallOptions[1]},
 {"stop",       0,      NULL,       c_clSmallOptions[2]},
 {"fork",       0,      NULL,       c_clSmallOptions[3]},
 {"input",      0,      NULL,       c_clSmallOptions[4]},
 {"confirm",    0,      NULL,       c_clSmallOptions[5]},
 {"choise",     0,      NULL,       c_clSmallOptions[6]},
 {"download",   0,      NULL,       c_clSmallOptions[7]},
 {NULL,         0,      NULL,       0}
};

int HandleSignals[] = {SIGUSR1, SIGHUP, SIGTERM};

volatile int iMaxFileDescriptors = 0;
volatile pid_t ListenerPID = 0;

//------------------------------------------------------------------------------------------------------
const size_t c_max_len = 256 + 1;

bool isDefaultUserInput = false;
char userInput[c_max_len];
bool isDefaultUserChoiseSet = false;
char userChoiseSet[c_max_len];
bool isDefaultUserConfirm = false;
char userConfirm[2];

bool cancelDownload = false;

bool useFork = false;
//------------------------------------------------------------------------------------------------------
bool IsStarted(pid_t& pid)
{
    bool res = false;
    int fd;
    if ((fd = open(gLockFilePath, O_RDONLY)) > 0)
    {
        const size_t c_procPreffixSize = 6;
        const size_t c_maxPidSize = 10;

        char pid_buf[c_procPreffixSize + c_maxPidSize] = {'/','p','r','o','c','/'};
        int len = read(fd, &pid_buf[c_procPreffixSize], 8);
        pid_buf[c_procPreffixSize + len - 1] = 0;
        pid = atoi(&pid_buf[c_procPreffixSize]);
        close(fd);

        #ifdef __MACH__ // mac os x
            res = false;    // todo: under construction
        #else // linux
            // try to find process in /proc
            struct stat st;
            res = (stat(pid_buf, &st) == 0);
        #endif
    }
    return res;
}
//------------------------------------------------------------------------------------------------------
void print_usage(FILE* stream, const char* prog_name)
{
    fprintf(stream, "Usage: %s options \n", prog_name);

    fprintf(stream,
        "   -h              --help                  Display this usage information\n"
        "   -a              --alive                 Check if listener alive\n"
        "   -s              --stop                  Stop listener\n"
        "   -f              --fork                  Use fork for detach listener from current console\n"
        "   -i text         --input text            Set default user input\n"
        "   -c y|n          --confirm y|n           Set default user confirm. y - if confirm, n (or other) if not confirm\n"
        "   -o choise set   --choise choise set     Set default user choise set separated by \'-\'\n"
        "   -d cancel       --download cancel       Imitation of download cancelling by user\n"
    );
}
//------------------------------------------------------------------------------------------------------
void ProcessCommandLine(int argc, char** argv)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> ProcessCommandLine");

    int next_option;

    if (argc > 1)
    {
        // we have parameters in command line
        do
        {
            next_option = getopt_long(argc, argv, c_clSmallOptions, LongOptions, 0);
            switch (next_option)
            {
                case 'h':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -h");
                    print_usage(stdout, argv[0]);
                    exit (EXIT_SUCCESS);
                }
                case 'a':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -a");
                    pid_t pid;
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "status: %s", IsStarted(pid) ? "UI API listener is alive" : "UI API listener is not alive");
                    fprintf(stdout, (IsStarted(pid) ? "UI API listener is alive\n" : "UI API listener is not alive\n"));
                    exit (EXIT_SUCCESS);
                }
                case 's':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -s");
                    pid_t pid;
                    if (IsStarted(pid))
                    {
                        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "send  kill signal for stopping UI API listener");
                        fprintf (stdout, "start UI API listener stopping\n");
                        kill(pid, SIGUSR1);
                        exit (EXIT_SUCCESS);
                    }
                    else
                    {
                        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "UI API listener is not runnign. Can't stop listener");
                        fprintf(stdout, "UI API listener is not runnign. Can't stop listener\n");
                        exit (EXIT_SUCCESS);
                    }
                    break;
                }
                case 'f':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -f. swap to non console mode");
                    useFork = true;
                    break;
                }
                case 'i':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -i");
                    String user_input = optarg;
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "defailt user input: %s", user_input.c_str());
                    if (!user_input.empty())
                    {
                        isDefaultUserInput = false;
                        if (user_input.size() > c_max_len)
                        {
                            fprintf (stdout, "UIAPIClientTest: default user input is too long. Max length = %d", c_max_len);
                        }
                        else
                        {
                            memcpy(userInput, user_input.c_str(), user_input.size());
                            userInput[user_input.size()] = '\0';
                            isDefaultUserInput = true;
                        }
                    }
                    else
                    {
                        fprintf (stdout, "UIAPIClientTest: default user input is empty. Fill correct argument list. Use -h for help\n");
                    }
                    break;
                }
                case 'c':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -c");
                    String user_confirm = optarg;
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "defailt user confirm: %s", user_confirm.c_str());
                    if (!user_confirm.empty())
                    {
                        isDefaultUserConfirm = false;
                        if (user_confirm.size() > c_max_len)
                        {
                            fprintf (stdout, "UIAPIClientTest: default user confirm is too long. Max length = %d", c_max_len);
                        }
                        else
                        {
                            userConfirm[0] = user_confirm[0];
                            userConfirm[1] = '\0';
                            isDefaultUserConfirm = true;
                        }
                    }
                    else
                    {
                        fprintf (stdout, "UIAPIClientTest: default user confirm is empty. Fill correct argument list. Use -h for help\n");
                    }
                    break;
                }
                case 'o':
                {
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "parse command line option: -o");
                    String user_choise = optarg;
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "default user choise: %s", user_choise.c_str());
                    if (!user_choise.empty())
                    {
                        isDefaultUserChoiseSet = false;
                        if (user_choise.size() > c_max_len)
                        {
                            fprintf (stdout, "UIAPIClientTest: default user choise is too long. Max length = %d", c_max_len);
                        }
                        else
                        {
                            memcpy(userChoiseSet, user_choise.c_str(), user_choise.size());
                            userChoiseSet[user_choise.size()] = '\0';
                            isDefaultUserChoiseSet = true;
                        }
                    }
                    else
                    {
                        fprintf (stdout, "UIAPIClientTest: default user input is empty. Fill correct argument list. Use -h for help\n");
                    }
                    break;
                }
                case 'd':
                {
                    String cancel_download = optarg;
                    if (cancel_download.compare("cancel") == 0)
                    {
                        cancelDownload = true;
                    }
                    break;
                }

                default:
                {
                    //fprintf (stdout, "No valid command line argument. Use -h for help\n");
                    //break;
                }
            }
        }
        while (next_option != -1);
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << ProcessCommandLine");
}
//------------------------------------------------------------------------------------------------------
void CheckIfSecondaryExecute()
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> CheckIfSecondaryExecute");
    pid_t pid;
    if (IsStarted(pid))
    {
        fprintf(stdout, "Try to start UI API listener second time. Allowed only one instance of UI API listener\n");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "Try to start UI API listener second time. Allowed only one instance of UI API listener");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << CheckIfSecondaryExecute. Exit from process");
        exit (EXIT_FAILURE);
    }
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << CheckIfSecondaryExecute");
}
//------------------------------------------------------------------------------------------------------
void FinalizeInstance();
//------------------------------------------------------------------------------------------------------
void Handler(int Signal)
{
    fprintf(stdout, "UIAPIClientTest: Received signal %d", Signal);
    switch (Signal)
    {
        case SIGUSR1:
            fprintf(stdout, "stop signal caught by UIAPIClientTest");
            gGracefulShutdown = 1;
            break;
        case SIGHUP:
            fprintf(stdout, "HUP signal caught by UIAPIClientTest");
            gGracefulShutdown = gCaughtHupSignal = 1;
            break;
        case SIGTERM:
            FinalizeInstance();
            exit (EXIT_SUCCESS);
            break;
        default:
            #ifdef _GNU_SOURCE
            fprintf(stdout, "UIAPIClientTest: Caught signal %s - exiting", strsignal(Signal));
            #else
            fprintf(stdout, "UIAPIClientTest: Caught signal %d - exiting", Signal);
            #endif
            FinalizeInstance();
            exit (0);
            break;
    }
}
//------------------------------------------------------------------------------------------------------
bool ConfigureSignalHandlers(void)
{
    sigset_t SigMask;
    if (sigemptyset(&SigMask) != 0)
    {
        fprintf(stdout, "UIAPIClientTest: Failed to clear signal mask");
        return false;
    }

    struct sigaction SignalAction;
    SignalAction.sa_handler = Handler;
    SignalAction.sa_mask    = SigMask;
    SignalAction.sa_flags    = 0;

    if (sigaction (SIGUSR1, &SignalAction, NULL) != 0)
    {
        fprintf(stdout, "UIAPIClientTest: Failed to set disposition for SIGUSR1 signal");
        return false;
    }

    if (sigaction (SIGHUP, &SignalAction, NULL) != 0)
    {
        fprintf(stdout, "UIAPIClientTest: Failed to set disposition for SIGHUP signal");
        return false;
    }

    if (sigaction (SIGTERM, &SignalAction, NULL) != 0)
    {
        fprintf(stdout, "UIAPIClientTest: Failed to set disposition for SIGTERM signal");
        return false;
    }

    return true;
}
//------------------------------------------------------------------------------------------------------
bool StartInstance()
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> StartInstance");

    char cPIDString[7];
    int iCurrentPID;
    int LockResult;
    struct flock Lock;

    iMaxFileDescriptors = sysconf (_SC_OPEN_MAX);

    if (useFork)
    {
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "we are in fork mode. Create new process");
        switch (iCurrentPID = fork ())
        {
            case 0:
                //Child process
                break;
            case -1:
                fprintf (stderr, "UIAPIClientTest: Error: initial fork failed: %s\n", strerror (errno));
                return -1;
                break;
            default:
                exit(0);
                break;
        };
    }

    ListenerPID = (int) getpid();
    sprintf (cPIDString, "%d\n", ListenerPID);
    if ((gLockFileDesc = creat (gLockFilePath, 0644)) < 0)
    {
        fprintf(stdout, "UIAPIClientTest: Couldn't open lock file, ERROR #%d", errno);
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << StartInstance. Result: fals. Reason: Couldn't open lock file");
        return false;
    }

    Lock.l_type = F_WRLCK;
    Lock.l_whence = SEEK_SET;
    Lock.l_start = Lock.l_start = 0;
    Lock.l_pid = 0;

    if ((LockResult = fcntl (gLockFileDesc, F_SETLK, &Lock)) < 0)
    {
        fprintf(stdout, "UIAPIClientTest: Couldn't set lock to file %s, ERROR #%d", gLockFilePath, errno);
    }

    if (write (gLockFileDesc, cPIDString, strlen(cPIDString)) <= 0)
    {
        fprintf(stdout, "UIAPIClientTest: Couldn't write PID to lock file, ERROR #%d", errno);
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << StartInstance. Result: true");
    return true;
}
//------------------------------------------------------------------------------------------------------
void FinalizeInstance()
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> FinalizeInstance");

    if(gLockFileDesc != -1)
    {
        close(gLockFileDesc);
        unlink(gLockFilePath);
        gLockFileDesc = -1;
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << FinalizeInstance");
}
//------------------------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
/*
     int t_argc = 2;

     char prName[c_max_len];
     memset(prName, '\0', c_max_len);
     memcpy(prName, "Message listener Test", 21);

     char t_i[c_max_len];
     memset(t_i, '\0', c_max_len);
     memcpy(t_i, "-dcancel", 9);

     char t_c[c_max_len];
     memset(t_c, '\0', c_max_len);
     memcpy(t_c, "-cy", 3);

     char t_o[c_max_len];
     memset(t_o, '\0', c_max_len);
     memcpy(t_o, "-oaaa-ccc", 9);

     char* t_argv[3];
     t_argv[0] = prName;
     t_argv[1] = t_i;
//     t_argv[2] = t_c;
//     t_argv[3] = t_o;
     t_argv[3] = 0;

     //-i def_user_choise -c y -o aaa-ccc
*/
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> main");

    ProcessCommandLine(argc, argv /*t_argc, t_argv*/);

    CheckIfSecondaryExecute();

    int result;

    if (!StartInstance())
    {
        fprintf(stdout, "UIAPIClientTest: Failed to initialize UI API listener instance");
        exit (EXIT_FAILURE);
    }

    if ((result = ConfigureSignalHandlers()) < 0)
    {
        fprintf(stdout, "UIAPIClientTest: Failed to configure signal handlers");
        unlink (gLockFilePath);
        exit (EXIT_FAILURE);
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "create message handler");

    CUIMessageHandler* handler = new CUIMessageHandler(
        isDefaultUserInput ? userInput : ((const char*)0),
        isDefaultUserConfirm ? userConfirm : ((const char*)0),
        isDefaultUserChoiseSet ? userChoiseSet : ((const char*)0),
        cancelDownload ? (&cancelDownload) : ((bool*)0)
    );

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "Init message handler");

    handler->Init();

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "create message listener");

    MessageListener* listener = CreateMessageListener(handler);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "call WaitMessages()");

    if (listener->WaitMessages() == e_Ok)
    {
        // main daemon cycle
        while (true)
        {
            // do something in cycle
            sleep(500);

            if( gGracefulShutdown == 1 )
            {
                break;
            }
        }

        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "call StopWaitingMessages()");
        listener->StopWaitingMessages();
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "release message listener");

    listener->Release();

    FinalizeInstance();

    fprintf(stdout, "UI API listener stopped");

    if (gCaughtHupSignal == 1)
    {  // need restart listener (now not used)
        gGracefulShutdown = gCaughtHupSignal = 0;
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << main");

    exit(EXIT_SUCCESS);
}
