//
// anyRemote
// a wi-fi or bluetooth remote for your PC.
//
// Copyright (C) 2006-2016 Mikhail Fedotov <anyremote@mail.ru>
//
// 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 3 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#ifdef __FreeBSD__
#include <signal.h>
#endif

#include "common.h"
#include "lib_wrapper.h"
#include "conf.h"
#include "dispatcher.h"
#include "executor.h"
#include "pr_frontend.h"
#include "utils.h"
#include "queue.h"
#include "thread.h"
#include "loop.h"
#include "alarm.h"
#include "avahi.h"

extern void freeDisplay();	// from xemulate.h

extern CONF	conf;
extern int      state;
extern boolean_t dispIsJoinable;

char tmp	    [MAXMAXLEN];

boolean_t        stillRun = BOOL_YES;

void aboutToExit()
{
    logger(L_DBG, "[ML]: aboutToExit");
    loopStop();

    //stillRun = BOOL_NO; do it after exiting from other threads (executor needs to handle exit event)

    logger(L_DBG, "[ML]: aboutToExit: join executor");
    sendEventToExecutor(0, ID_EVT_EXIT);

    threadJoin(T_EXEC);

    if (dispIsJoinable) {	// dispatcher can be blocked in accept()
        logger(L_DBG, "[ML]: aboutToExit: join dispatcher");

        dMessage* dm = allocDMessage();
        dm->type     = DM_EVENT;
        dm->subtype  = ID_EVENT_EXIT;

        sendToDispatcher(dm);

        threadJoin(T_DISP);
    }
    
    #ifdef USE_AVAHI
    stopAvahi(); 
    #endif

    stillRun = BOOL_NO;

    logger(L_DBG, "[ML]: aboutToExit: release main loop");
    loopDestroy();

    queueRemove(Q_DISP, freeDMessage);
    queueRemove(Q_EXEC, freeEMessage);
    queueRemove(Q_MAIN, freeMMessage);

    freeDisplay();

    logger(L_DBG, "[ML]: aboutToExit: EXIT");

    #ifdef USE_ICONV
    closeConvertor();
    #endif

    releaseLog();

    exit(0);
}

// handle signals only once (it is enough to send disconnect message to java client)
void sig_int(int i)
{
    signal(SIGINT, SIG_DFL);
    printf("\nProcess %d: got SIGINT\n",getpid());
    aboutToExit();
}

void sig_quit(int i)
{
    signal(SIGQUIT, SIG_DFL);
    printf("\nProcess %d: got SIGQUIT\n",getpid());
    aboutToExit();
}

void sig_kill(int i)
{
    signal(SIGKILL, SIG_DFL);
    printf("\nProcess %d: got SIGKILL\n",getpid());
    aboutToExit();
}

static void createDataDir()
{
    char dd[542];
    char *t = getenv("HOME");
    if (t) {
        strcpy(dd, t);
    } else {
        strcpy(dd, ".");
    }
    strcat(dd, "/.anyRemote");

    DIR *d = opendir(dd);
    if (d) {
        closedir(d);
    } else {
        mkdir(dd, S_IRWXU);
        if(getuid()==0 && conf.uid) { // do not create as superuser
#ifdef __cplusplus
            int dummy =
#endif
                chown(dd,conf.uid,conf.gid);
        }
    }
}

int main(int argc, char *argv[])
{
    if (argv[1] && (strcmp(argv[1],"-h")==0 || strcmp(argv[1],"--help")==0 || strcmp(argv[1],"-help")==0)) {
        //Just print help and exit
        printHelp();
        exit(0);
    } else if (argv[1] && (strcmp(argv[1],"-v")==0 || strcmp(argv[1],"--version")==0 || strcmp(argv[1],"-version")==0)) {
        printf("anyremote v%s\n", PACKAGE_VERSION);
        exit(0);
    }

    createDataDir();
    
    addInternalVars();

    if (parse_opts(argc, argv) != 1 ) {
        printf("Invalid command line option used. Exiting.\n");
        freeCfg();
        exit(1);
    }
    
    int i;
    for (i=1; i<argc; i++) {
        if (strcmp(argv[i],"-f")==0 && i+1<argc) {
            break;
        }
    }
    init_cfg_dir(i<argc ? argv[i+1] : NULL);

    if (init_cfg(i<argc ? argv[i+1] : NULL) != EXIT_OK) {
        printf("Can not initialize configuration. Exiting.\n");
        printHelp();
        freeCfg();
        exit(1);
    }

    threadInit();		// need to call it before initLog()

    initLog();
    manageAlarms(ALARM_CLEAN);

    // Not to core dump if connection will close
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);

    signal(SIGINT, sig_int);
    signal(SIGQUIT,sig_quit);
    signal(SIGKILL,sig_kill);

    //logger(L_DBG,"g_main_loop_new");


    queueNew(Q_MAIN);

    queueNew(Q_DISP);
    threadNew(T_DISP,dispatcherRoutine,NULL,JOINABLE);

    queueNew(Q_EXEC);
    threadNew(T_EXEC,executorRoutine,NULL,JOINABLE);
    
    #ifdef USE_AVAHI
    if (parsePortsForAvahi()) {
        threadNew(T_AVAHI,startAvahi,NULL,JOINABLE);
    }
    #endif

    loopStart();

    return 0;
}
