/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2022  Ladislav Vaiz <ok1zia@nagano.cz>
    and authors of web browser Links 0.96

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/
#include "header.h"

#include "ac.h"
#include "alsa.h"
#include "charsets.h"
#ifdef Z_HAVE_SDL
#include "cordata.h"
#endif
#include "cwdaemon.h"
#include "cwdb.h"
#include "davac4.h"
#include "davac5.h"
#include "dsp.h"
#include "dwdb.h"
#include "dxc.h"
#include "edi.h"
#include "excdb.h"
#include "fft.h"
#include "fifo.h"
#include "httpd.h"
#include "kbdbind.h"
#include "kbd.h"
#include "language2.h"
#include "main.h"
#include "map.h"
#include "masterdb.h"
#include "menu.h"
#include "misc.h"
#include "namedb.h"
#include "net.h"
#include "ntpq.h"
#include "pa.h"
#include "ppdev.h"
#include "qrvdb.h"
#include "qsodb.h"
#include "rain.h"
#include "rc.h"
#include "rotar.h"
#include "sdev.h"
#include "sdr.h"
#include "stats.h"
#include "svnversion.h"
#include "tsdl.h"
#include "session.h"
#include "ssbd.h"
#include "terminal.h"
#include "trig.h"
//#include "update.h"
//#include "vhfcontestnet.h"
#include "slovhfnet.h"
#include "wizz.h"

#ifdef ANDROID
#include <android/log.h>
#endif

int retval = RET_OK;
char * starting_sbrk;
struct zselect *zsel;
struct zfiledlg *zfiledlg;

extern char pwwlo[MAX_STR_LEN];
    
#ifdef Z_UNIX_ANDROID
void sigchld(void *arg, siginfo_t *siginfo, void *ctx);

void sig_terminate(void *arg, siginfo_t *siginfo, void *ctx)
{
    unhandle_basic_signals();
    zselect_terminate(zsel);
    retval = RET_SIGNAL;
}

void sig_intr(void *arg, siginfo_t *siginfo, void *ctx)
{              
    if (!term) {
        unhandle_basic_signals();
        zselect_terminate(zsel);
    } else {
        unhandle_basic_signals();
        exit_prog(NULL);
    }
}

void sig_ctrl_c(void *arg, siginfo_t *siginfo, void *ctx)
{
    dbg("sig_ctrl_c\n");
    if (!is_blocked()) kbd_ctrl_c();
}

void sig_ign(void *arg, siginfo_t *siginfo, void *ctx)
{
}

void sig_tstp(void *arg, siginfo_t *siginfo, void *ctx)
{              
#ifdef SIGSTOP
    int pid = getpid();
    block_itrm(0);
#if defined (SIGCONT) && defined(SIGTTOU)
    if (!fork()) {
        sleep(1);
        kill(pid, SIGCONT);
        exit(0);
    }
#endif
    raise(SIGSTOP);
#endif
}
#endif

#ifndef Z_MSC_MINGW
void sig_cont(void *arg, siginfo_t *siginfo, void *ctx)
{
    if (!unblock_itrm(0)) resize_terminal(NULL);
}
#endif

void tucnak_crash(GString *gs){

	if (zdebug_debug_type()) 
		z_msgbox_info("Tucnak-" Z_PLATFORM " " VERSION, "Tucnak crashed. Now is time to attach the debugger.");

	cwdaemon_safe_abort(cwda);

#ifdef Z_HAVE_SDL    
    if (sdl) {
        SDL_Quit();
    }
    else
#endif    
    {
#ifndef Z_MSC_MINGW
        itrm_safe_abort();
#endif
    }


#ifdef Z_MSC_MINGW
	g_string_append(gs, "Tucnak unhandled exception.\nPlease report to ok1zia@nagano.cz");
#else
	g_string_append(gs, "Tucnak segmentation fault.\nPlease report to ok1zia@nagano.cz");
#endif
}

static void sig_pipe(int a){
   /* dbg("sig_pipe(%d)\n", a);
    dbg("pid=%d\n", getpid());
    sleep(10);*/
}
    
void handle_basic_signals(struct terminal *term)
{
#ifndef Z_HAVE_SDL    
#define sdl 0
#endif
#ifdef Z_UNIX
    zselect_signal_set(SIGHUP, sig_intr, NULL, 0);
    if (!sdl) zselect_signal_set(SIGINT, sig_ctrl_c, NULL, 0);
    zselect_signal_set(SIGTERM, sig_terminate, NULL, 0);
#endif
    if (!sdl){
#ifdef SIGTSTP
        zselect_signal_set(SIGTSTP, sig_tstp, NULL, 0);
#endif
#ifdef SIGTTIN
        zselect_signal_set(SIGTTIN, sig_tstp, NULL, 0);
#endif
#ifdef SIGCONT
        zselect_signal_set(SIGCONT, sig_cont, NULL, 0);
#endif
    }
#ifdef SIGTTOU
    zselect_signal_set(SIGTTOU, sig_ign, NULL, 0);
#endif
#ifdef Z_UNIX
	signal(SIGPIPE, sig_pipe);
#endif
}

/*static void handle_sdl_signals(void){
#ifndef Z_MSC_MINGW
    zselect_signal_set(SIGSEGV, sig_segv, NULL, 1);
#endif
}*/


void unhandle_terminal_signals()
{
#ifdef Z_UNIX
    zselect_signal_set(SIGHUP, NULL, NULL, 0);
    if (!sdl) zselect_signal_set(SIGINT, NULL, NULL, 0);
#endif
#ifdef SIGTSTP
    zselect_signal_set(SIGTSTP, NULL, NULL, 0);
#endif
#ifdef SIGTTIN
    zselect_signal_set(SIGTTIN, NULL, NULL, 0);
#endif
#ifdef SIGCONT
    zselect_signal_set(SIGCONT, NULL, NULL, 0);
#endif
#ifdef SIGTTOU
    zselect_signal_set(SIGTTOU, NULL, NULL, 0);
#endif
#ifdef Z_UNIX
	signal(SIGPIPE, SIG_IGN);
#endif
}

void unhandle_basic_signals()
{
#ifdef Z_UNIX
    zselect_signal_set(SIGHUP, NULL, NULL, 0);
    if (!sdl) zselect_signal_set(SIGINT, NULL, NULL, 0);
    zselect_signal_set(SIGTERM, NULL, NULL, 0);
#endif
#ifdef SIGTSTP
    zselect_signal_set(SIGTSTP, NULL, NULL, 0);
#endif
#ifdef SIGTTIN
    zselect_signal_set(SIGTTIN, NULL, NULL, 0);
#endif
#ifdef SIGCONT
    zselect_signal_set(SIGCONT, NULL, NULL, 0);
#endif
#ifdef SIGTTOU
    zselect_signal_set(SIGTTOU, NULL, NULL, 0);
#endif
#ifdef Z_UNIX
	signal(SIGPIPE, SIG_IGN);
#endif
}

#ifndef Z_HAVE_SDL
#undef sdl
#endif

#ifndef Z_MSC_MINGW
int term_attach_terminal(int in, int out, int ctl)
{
    int fd[2];

    /*struct terminal *term;*/
    dbg("attach_terminal\n");
    if (z_pipe(fd)) {
        error("ERROR: can't create pipe for internal communication");
        return -1;
    }
	z_sock_nonblock(fd[0], 1);
	z_sock_nonblock(fd[1], 1);
    handle_trm(in, out, out, fd[1], ctl);
    if ((term = init_term(fd[0], out, win_func))) {
        handle_basic_signals(term); /* OK, this is race condition, but it must be so; GPM installs it's own buggy TSTP handler */
        return fd[1];
    }

    closesocket(fd[0]);
    closesocket(fd[1]);
    return -1;
}
#endif


static int ac;
static char **av;
int first_contest_def = 0;
char *tucnak_dir = NULL;
char *logs_dir = NULL;
char *home_dir = NULL;

static void init_home(void)
{
    struct stat st;
	char *c = NULL;

	c = getenv("TUCNAK");
	if (c) {
		tucnak_dir = g_strdup(c);
		home_dir = g_strdup(c);
		z_dirname(home_dir);
	}
	if (!tucnak_dir){
		home_dir = g_strdup(getenv("HOME"));
		//home_dir = NULL;//FIXME
		if (home_dir) tucnak_dir = g_strdup_printf("%s/tucnak", home_dir);
	}
#ifdef Z_MSC_MINGW 
	if (!tucnak_dir){
		home_dir = z_strdup_home();
    	tucnak_dir = g_strdup_printf("%s/Tucnak", home_dir);
	}
#endif
	if (!tucnak_dir){
		char *d = g_strdup(av[0]);
		z_dirname(d);
		tucnak_dir = g_strdup(d);
		home_dir = g_strdup(d);
		g_free(d);
	}

	z_wokna(tucnak_dir);
	z_wokna(home_dir);
	
    if (stat(tucnak_dir, &st)) {
        if (z_mkdir(tucnak_dir, 0777)) zinternal(VTEXT(T_CANT_CREATE_HOME), tucnak_dir);
    }else{
    // exists  
        if (!S_ISDIR(st.st_mode)) zinternal(VTEXT(T_MUST_BE_DIR_MODE), tucnak_dir, st.st_mode);
	}
}


static void main_msg_handler(struct zselect *zsel, int n, char **items){
    
   // dbg("threadpipe_rel_read('%s')\n", items[0]);
    if (!n || !items[0]) zinternal("main_msg_handler bad format");

	if (strcmp(items[0], "AC")==0){
		ac_redraw();
		return;
	}
	if (strcmp(items[0], "LOG")==0){
		log_adds(items[1]);
		dbg("%s", items[1]);
		return;
	}

#ifdef USE_SDR
	if (strcmp(items[0], "SDR")==0){
		sdr_read_handler(gsdr, items[1], items[2]);
		return;
	}
#endif
    
    if (strcmp(items[0], "ST")==0){
		if (!ctest) return;
        check_autosave();
        redraw_stats(aband);
        recalc_allb_stats();
        recalc_statsfifo(aband);
        redraw_later();
        return;
    }
    if (strcmp(items[0], "CWQS")==0){
        redraw_later();
        return;
    }
    if (strcmp(items[0], "COR")==0){
#ifdef Z_HAVE_SDL
        zcor_read_handler(n, items);
#endif
        return;
    }
    if (strcmp(items[0], "TERM")==0){
        redraw_later();
        return;
    }
    
	if (!items[1]) {
		zinternal("main_msg_handler bad format 2 (%s)", items[0]);
	}
#if defined(HAVE_SNDFILE) || defined(Z_ANDROID)
    if (strcmp(items[0], "SSBP")==0){
        ssbd_play_read_handler(gssbd, items[1]);
        return;
    }
#endif
#ifdef HAVE_SNDFILE    
    if (strcmp(items[0], "SSBR")==0){
        ssbd_rec_read_handler(gssbd, items[1]);
        return;
    }
#endif
    if (strcmp(items[0], "CW")==0){
        cwdaemon_read_handler(cwda, items[1], items[2]);
        return;
    }
#ifdef HAVE_HAMLIB    
    if (strcmp(items[0], "TRIG")==0){
        trigs_read_handler(gtrigs, n, items);
        return;
    }
#endif
    if (strcmp(items[0], "SC")==0){
        log_adds(items[2]); //items[1] is always "!"
        return;
    }
    if (strcmp(items[0], "ROT")==0){
        rotar_read_handler(n, items);
        return;
    }
#ifdef OCT8TOR
	if (strcmp(items[0], "OCT") == 0){
		oct8tor_read_handler(n, items);
		return;
	}
#endif
	if (strcmp(items[0], "ZFILEDLG")==0){
		zfiledlg_read_handler(n, items);
		return;
	}
	if (strcmp(items[0], "ZASYNCDNS")==0){
		zasyncdns_read_handler(n, items);
		return;
	}

}

#ifdef Z_ANDROID
void android_trace_keys(char *s){
    if (!cfg->trace_keys) return;
    error("%s", s);
    log_adds(s);
}
#endif


void trace_handler(char *s){
	if (!gses) return;
	log_adds(s);
}

void info(GString *gs){
    g_string_append_printf(gs, "\n  %s-%s %s\n\n", PACKAGE, Z_PLATFORM, VERSION);
    g_string_append_printf(gs, "tucnak svn version: %s\nlibzia svn version: %s\n\n", T_SVNVER, z_svnver());
#ifdef __GLIBC__
    g_string_append_printf(gs,"\n  glibc info\n");
    g_string_append_printf(gs,"compiled version: %d.%d\n", __GLIBC__, __GLIBC_MINOR__);
    g_string_append_printf(gs,"runtime version: %s\n", gnu_get_libc_version());
    g_string_append_printf(gs,"runtime release: %s\n\n", gnu_get_libc_release());
#endif
#ifdef Z_HAVE_SDL        
    sdl_info(gs);
#endif
#if defined(HAVE_LINUX_PPDEV_H) || defined (Z_MSC_MINGW_CYGWIN)
    parport_info(gs);
#endif        
#ifdef HAVE_SNDFILE
#ifdef HAVE_ALSA        
    alsa_info(gs);
#endif
#ifdef HAVE_PORTAUDIO
	pa_info(gs);
#endif
#endif        
#ifdef Z_HAVE_LIBFTDI
	if (gses != NULL) {
		free_cwdaemon(cwda);
		free_rotars();
	}
    usb_info(gs);
#endif
#ifdef HAVE_HIDAPI
    hid_info(gs);
#endif
#ifdef Z_HAVE_LIBFTDI
	if (gses != NULL) {
		cwda = init_cwdaemon();
		init_rotars();
		progress(NULL);
		redraw_later();
	}
#endif
    iface_info(gs);
    bat_info(gs);
    serial_info(gs);
#ifdef Z_ANDROID
    double h, w;
    int state;
    //SDL_ANDROID_GetHW(&h, &w, &state);
    zandroid_init(zsel);
    zandroid_get_location(&h, &w, &state);
    g_string_append_printf(gs, "\n  geolocation info\n");
    g_string_append_printf(gs, "state: ");
    switch (state){
        case 0: g_string_append(gs, "searching\n"); break;
        case 1: g_string_append(gs, "fixed\n"); break;
        case 2: g_string_append(gs, "error\n"); break;
        default: g_string_append_printf(gs, "unknown\n", state); break;
    }
    if (h > -1000 && h < 1000){
	    g_string_append_printf(gs, "lon: %f\n", h);
	    g_string_append_printf(gs, "lat: %f\n", w);
        hw2loc(pwwlo, h, w, 10);
		g_string_append_printf(gs, "wwl: %s\n", pwwlo);
	}else{
		g_string_append_printf(gs, "location unknown\n");
	}
#endif
    g_string_append(gs, "");
}

void settings(GString *gs){
	z_get_settings(gs);
	g_string_append(gs, "\n\n");
	g_string_append(gs, txt_settings);
    g_string_append_printf(gs, "    macros: ");
#ifdef UNIX
    g_string_append_printf(gs, "UNIX ");  
#endif
#ifdef _UNIX
    g_string_append_printf(gs, "_UNIX ");  
#endif
#ifdef __UNIX
    g_string_append_printf(gs, "__UNIX ");  
#endif
#ifdef LINUX
    g_string_append_printf(gs, "LINUX ");
#endif
#ifdef _LINUX
    g_string_append_printf(gs, "_LINUX ");
#endif
#ifdef __LINUX
    g_string_append_printf(gs, "__LINUX ");
#endif
#ifdef WIN32
    g_string_append_printf(gs, "WIN32 ");
#endif
#ifdef _WIN32
    g_string_append_printf(gs, "_WIN32 ");
#endif
#ifdef _WIN64
    g_string_append_printf(gs, "_WIN64 ");
#endif
#ifdef __CYGWIN__
    g_string_append_printf(gs, "__CYGWIN__ ");
#endif
#ifdef __GNUC__
    g_string_append_printf(gs, "__GNUC__=%d.%d.%d ", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#endif
#ifdef _MSC_VER
    g_string_append_printf(gs, "_MSC_VER=%d.%02d ", _MSC_VER / 100, _MSC_VER % 100);
#endif
#ifdef __MINGW32__
    g_string_append_printf(gs, "__MINGW32__ ");
#endif
#ifdef __x86_64__
    g_string_append_printf(gs, "__x86_64__ ");
#endif
#ifdef __i386__
    g_string_append_printf(gs, "__i386__ ");
#endif
#ifdef __hppa__
    g_string_append_printf(gs, "__hppa__ ");
#endif
#ifdef __ppc__
    g_string_append_printf(gs, "__ppc__ ");
#endif
#ifdef __powerpc__
    g_string_append_printf(gs, "__powerpc__ ");
#endif
#ifdef __arm__
    g_string_append_printf(gs, "__arm__ ");
#endif
#ifdef Z_UNIX
    g_string_append_printf(gs, "Z_UNIX ");
#endif
#ifdef Z_MSC
    g_string_append_printf(gs, "Z_MSC ");
#endif
#ifdef Z_MINGW
    g_string_append_printf(gs, "Z_MINGW ");
#endif
#ifdef Z_MINGW
    g_string_append_printf(gs, "Z_CYGWIN ");
#endif
    g_string_append_printf(gs, "\n");
}

static void init(void)
{
	char userbuf[64];

    parse_options(ac, av);
	zdebug_init(ac, av, free_all_itrms, trace_handler, "Tucnak-" Z_PLATFORM " " VERSION);
	//zjson_test();

	dbg("Tucnak-%s %s svn %s (%s) pid=%d user=%s\n", Z_PLATFORM, PACKAGE_VERSION, T_SVNVER, z_svnver(), getpid(), z_username(userbuf, sizeof(userbuf)));
    /*{
        void *codebase;
        char *libzia = z_libzia_file_name(&codebase);
        dbg("libzia: '%s' codebase 0x%p\n", libzia, codebase);
    }*/

                
    if (opt_i){
		GString *gs = g_string_sized_new(1024);
		info(gs);
#ifdef Z_MSC_MINGW
		MessageBox(NULL, gs->str, "Tucnak info", MB_OK | MB_ICONINFORMATION);
#elif defined(Z_ANDROID)
        __android_log_print(ANDROID_LOG_DEBUG, "Tucnak", "%s", gs->str);
#else
		printf("%s", gs->str);
#endif
		g_string_free(gs, TRUE);
        exit(0);
    }
    if (opt_s){
		GString *gs = g_string_sized_new(1024);
		settings(gs);
#ifdef Z_MSC_MINGW
		MessageBox(NULL, gs->str, "Tucnak settings", MB_OK | MB_ICONINFORMATION);
#elif defined(Z_ANDROID)
        __android_log_print(ANDROID_LOG_DEBUG, "Tucnak", "%s", gs->str);
#else
		printf("%s", gs->str);
#endif
		g_string_free(gs, TRUE);
        exit(0);
    }
        
    srand(time(NULL) + getpid());
    init_debug();
	glog = init_fifo(1000);
    //init_zptrarray();
    //init_ghash();

    init_trans();
    init_rc();
    term_spec_init();

#ifndef Z_MSC_MINGW
    zselect_signal_set(SIGCHLD, sigchld, NULL, 1);
#endif

    init_home();
	
    init_keymaps();
	read_rc_files();
#ifdef Z_ANDROID
    SDL_Android_key_trace(android_trace_keys);
#endif
	
	set_logs_dir();

	z_wokna(logs_dir);
    	
#ifdef Z_HAVE_SDL    
    sdl=init_sdl();
	
    //log_addf(VTEXT(T_TUCNAK_DIRECTORY_IS), tucnak_dir);

    if (sdl){
        setenv("TERM", "sdl", 1);
        if (sdl_attach_terminal(get_input_handle(), get_output_handle(), get_ctl_handle())==-1){
            retval = RET_FATAL;
            zselect_terminate(zsel);
        }
    }
    else
#endif
    {
#ifndef Z_MSC_MINGW
        if (term_attach_terminal(get_input_handle(), get_output_handle(), get_ctl_handle())==-1){
            retval = RET_FATAL;
            zselect_terminate(zsel);
        }
#endif
    }
    if (!term) zinternal("init() !term");
    
    gtalk = init_fifo(1000);        
    cw = init_cw();
    dw = init_dw();
    excdb = init_exc();
    qrv = init_qrv();
    wizz = init_wizz();
	namedb = init_namedb();
	masterdb = init_masterdb();
    init_sconns();
    init_rotars();
#ifdef Z_HAVE_SDL    
	progress(VTEXT(T_INIT_MAP));		
    gcor = zcor_init(zsel, map_cor_callback, cor_tucnakcor, COR_ITEMS);
#endif

    if (cfg->dssaver){
		progress(VTEXT(T_DISABLE_SAVER));		
		z_disable_screensaver();
	}
    
    read_cw_files(cw);
    read_dw_files(dw);
    read_wizz_files(wizz);
	read_namedb_files(namedb);
	/*read_ebw_files(cw,namedb);*/
	read_masterdb_files(masterdb);
    
    init_sound();
    gnet = init_net();
	httpd = init_httpd();
    cwda = init_cwdaemon();
    gssbd = init_ssbd();

    spotdb = init_spotdb();
#ifdef Z_UNIX
    ntpq = init_ntpq();        
#endif
#ifdef HAVE_HAMLIB

    init_trigs();        
#endif
#ifdef USE_FFT
	gfft = init_fft();
#endif
	gacs = init_acs();
#ifdef USE_SDR
	gsdr = init_sdr();
#endif
#ifdef Z_HAVE_SDL
	gbat = zbat_init();
	//grain = init_rain();
#endif

    
    zfiledlg = zfiledlg_init();
    zfiledlg->last_dir = g_strdup(tucnak_dir);

	if (cfg->maximized && cfg->fullscreen == 0){
#ifdef Z_HAVE_SDL
        if (sdl && sdl->screen){
            sdl->ignore_progress = 1; 
            SDL_FillRect(sdl->screen, NULL, 0);
            SDL_UpdateRect(sdl->screen, 0, 0, 0, 0);
            zsdl_maximize(zsdl, 1);
        }
#endif
	}else{
		progress(VTEXT(T_STARTING_TUCNAK));
	}



#if 0
	{int i;
	GString *gs;
	gs = g_string_new("");
	ST_START();
	for (i = 0; i < 10000; i++) {
		g_string_append_printf(gs, "%-14s %-6s %08d %s\n", "OK1ZIA", "JN69QR", 20120308, "C");
	}
	g_string_truncate(gs, 0);
	ST_STOP("g_string_append_printf");

	for (i = 0; i < 10000; i++) {
		zg_string_append_printf(gs, "%-14s %-6s %08d %s\n", "OK1ZIA", "JN69QR", 20120308, "C");
	}
	g_string_truncate(gs, 0);
	ST_STOP("zg_string_append_printf");

	for (i = 0; i < 10000; i++) {
		char s[256], *c;
		sprintf(s, "%-14s %-6s %08d %s\n", "OK1ZIA", "JN69QR", 20120308, "C");
		g_string_append(gs, s);
	}
	g_string_truncate(gs, 0);
	ST_STOP("sprintf");

	for (i = 0; i < 10000; i++) {
		char s[256];
		sprintf_s(s, 256, "%-14s %-6s %08d %s\n", "OK1ZIA", "JN69QR", 20120308, "C");
		g_string_append(gs, s);
	}
	g_string_truncate(gs, 0);
	ST_STOP("sprintf_s");

	g_string_free(gs, 1);}
#endif
}



static void terminate_all_subsystems(void)
{
#ifdef Z_HAVE_SDL
    /*free_gfx();*/
#endif
    dbg("terminate_all_subsystems\n");
    zselect_bh_check(zsel);
    zselect_bh_check(zsel);
	zselect_bh_check(zsel);

	free_ctest();


#ifdef Z_HAVE_SDL    
    sdl_stop_event_thread();
	free_rain(grain);
	zbat_free(gbat);
	free_acs(gacs); gacs = NULL;
	progress(VTEXT(T_FREE_MAP));		
    zcors_free();
#endif
#ifdef USE_SDR
	free_sdr(gsdr);
	gsdr = NULL;
#endif
#ifdef HAVE_HAMLIB
    free_trigs(gtrigs); 
	gtrigs = NULL;
#endif
#ifdef Z_UNIX
    free_ntpq(ntpq); 
#endif
    free_qrv(qrv);
    free_exc(excdb);
    free_dw(dw);
    free_cw(cw);
    free_wizz(wizz);
	free_namedb(namedb);
	free_masterdb(masterdb);
    free_rotars();
    free_spotdb(spotdb);
	free_httpd(httpd);
    free_net(gnet);

    free_cwdaemon(cwda); cwda = NULL;
    free_ssbd(gssbd, 1); gssbd = NULL;
    free_sound();
#ifdef USE_FFT
	free_fft(gfft);  // after ssb
#endif
//    FREE(free_threadpipe(tpipe));
	zfiledlg_free(zfiledlg);
    
    free_fifo(glog);
    free_fifo(gtalk);
	

    free_namelist();
    free_all_itrms();

    free_history_lists();
    free_keymaps();
    free_conv_table();
    //zselect_bh_check(zsel);
    //zselect_bh_check(zsel);
    destroy_terminal(NULL);
	free_term_specs();
    free_namelist();
#ifdef Z_HAVE_SDL    
    free_sdl();
#endif
    shutdown_trans();
    free_rc();
    check_memory_leaks();
    //free_ghash();
    //free_zptrarray();
    free_debug();
	g_free(tucnak_dir);
	g_free(logs_dir);
	g_free(home_dir);
}

void redraw(void){
    
 //   dbg("redraw()\n");
#ifdef Z_HAVE_SDL        
    if (sdl) {
        sdl_redraw_screen();
    }
    else
#endif
    {
        term_redraw_screen();
    }
}

int main(int argc, char *argv[])
{
    //FILE* f = fopen("allchars.bin", "wb");
    //int i;
    //for (i = 0; i < 256; i++) {
    //    fputc(i, f);  
    //    if ((i % 16) == 15) fprintf(f, "\r\n");
    //}
    //fclose(f);

	//zavgfilter_test();
#if 0
    GString *gs = g_string_sized_new(8000);
    z_backtrace_symbols(gs, NULL, 0);
    printf("----\n%s\n----\n", gs->str);
    exit(0);
#endif

#if 0
	char data[256];
	int len;
	struct zserial *zser;

	zdebug_init(argc, argv, NULL, NULL);

	zser = zserial_init_win32("COM3");
	zserial_set_line(zser, 9600, 8, 'E', 1);
	if (zserial_open(zser)){
		error("%s", zserial_errorstr(zser));
		zserial_free(zser);
		return -1;
	}

	len = 0;
	
	ST_START();
	if (zserial_prot(zser, 240, 17, data, &len, 400)){
		error("%s", zserial_errorstr(zser));
		ST_STOP("prot error");
		zserial_free(zser);
		return -1;
	}
	ST_STOP("prot");
	zserial_free(zser);
	exit(0);

#endif
#if 0
	GString *gs = g_string_new("");
	zg_string_eprintfa("", gs, "%b", "A", 1);
	g_string_assign(gs, "");
	zg_string_eprintfa("", gs, "%b", "AA", 2);
	g_string_assign(gs, "");
	zg_string_eprintfa("", gs, "%b", "AAA", 3);
	g_string_assign(gs, "");
	zg_string_eprintfa("", gs, "%b", "AAAA", 4);

#endif
#if 0
    char s[20], t[20];
    strcpy(s, "  abc  "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, "  abc"); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, "abc  "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, " abc "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, " abc"); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, "abc "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, "abc"); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, "  "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, " "); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    strcpy(s, ""); strcpy(t, s); printf("'%s'   '%s'\n", s, trim(t)); 
    printf("'%s'   '%s'\n", NULL, trim(NULL)); 
    exit(0);
#endif

#if 0
	char str[256];
    double lat = 0/*50.1*/, lon=0/*15.0*/;
    int d;
    for (d = 0; d < 365; d++){
        struct tm tm;
        memset(&tm, 0, sizeof(tm));
        tm.tm_mday = 1 + d;
        tm.tm_mon = 0;
        tm.tm_year = 2014 - 1900;
        time_t tt = mktime(&tm);
        gmtime_r(&tt, &tm);
	    printf("%02d.%02d.%04d  %s\n", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, zsun_strdup_riseset(tt, 50.1, -100));
    }

	return 0;
#endif
#if 0
    g_thread_init(NULL);
    debug_type=2;
    init_debug();
    int thr=2;
    ST_START;
    similar_calls("OK2M", "OK1ZIA",0,thr,1);
    similar_calls("OK2M", "OK1KRQ",0,thr,1);

    similar_calls("OK2M", "OK2",0,thr,1);
    similar_calls("OK2M", "OKM",0,thr,1);
    similar_calls("OK2M", "O2M",0,thr,1);
    similar_calls("OK2M", "K2M",0,thr,1);
    
    similar_calls("OK2M", "KO2M",0,thr,1);
    
    similar_calls("OK2M", "OK2X",0,thr,1);
    similar_calls("OK2M", "OKXM",0,thr,1);
    similar_calls("OK2M", "OX2M",0,thr,1);
    similar_calls("OK2M", "XK2M",0,thr,1);
    
    similar_calls("OK2M", "OK2MX",0,thr,1);
    similar_calls("OK2M", "OK2XM",0,thr,1);
    similar_calls("OK2M", "OKX2M",0,thr,1);
    similar_calls("OK2M", "OXK2M",0,thr,1);
    similar_calls("OK2M", "XOK2M",0,thr,1);
    
    similar_calls("OK2M", "DK2M",0,thr,1);
    similar_calls("OK2M", "DK3M",0,thr,1);
    similar_calls("OK2M", "DK3O",0,thr,1);
    similar_calls("OK2M", "DL2M",0,thr,1);
    similar_calls("OK2M", "DL3M",0,thr,1);
    similar_calls("OK2M", "DL3O",0,thr,1);
    
    similar_calls("OK1KRQ", "OK1ZIA",0,thr,1);

    similar_calls("OK1KRQ", "OK1KRQ",0,thr,1);
    similar_calls("OK1KRQ", "OK1KR",0,thr,1);
    similar_calls("OK1KRQ", "OK1KQ",0,thr,1);
    similar_calls("OK1KRQ", "OK1RQ",0,thr,1);
    similar_calls("OK1KRQ", "OKKRQ",0,thr,1);
    similar_calls("OK1KRQ", "K1KRQ",0,thr,1);
    
    similar_calls("OK1KRQ", "OK1KQR",0,thr,1);
    similar_calls("OK1KRQ", "OK1RKQ",0,thr,1);
    similar_calls("OK1KRQ", "OKK1RQ",0,thr,1);
    similar_calls("OK1KRQ", "O1KKRQ",0,thr,1);
    similar_calls("OK1KRQ", "KO1KRQ",0,thr,1);

    similar_calls("OK1KRQ", "OK1KRX",0,thr,1);
    similar_calls("OK1KRQ", "OK1KXQ",0,thr,1);
    similar_calls("OK1KRQ", "OK1XRQ",0,thr,1);
    similar_calls("OK1KRQ", "OKXKRQ",0,thr,1);
    similar_calls("OK1KRQ", "OX1KRQ",0,thr,1);
    similar_calls("OK1KRQ", "XK1KRQ",0,thr,1);
    similar_calls("OK1KRQ", "OK1Q",0,thr,1);
    
    similar_calls("OM3I", "OM3RLA",0,thr,1);
    similar_calls("OM3I", "OM3RL",0,thr,1);
    similar_calls("OM3I", "OM3R",0,thr,1);
    similar_calls("OM3RKA", "OM5MZ",0,thr,1);
    similar_calls("HG10P", "HG3A",0,thr,1);
    similar_calls("HG3A", "HG10P",0,thr,1);
    ST_STOP;
    return;
#endif    
#if 0    
    GThread *thr;
    GError *gerr;
    gpointer ex;

    g_thread_init(NULL);
    thr=g_thread_try_new("test", func, NULL, 1,&gerr); 
    ex=g_thread_join(thr);
    printf("thread exited %p\n", ex);
    return(0);
#endif
    
#if 0    
    {
        void *p;
        init_debug();
        p=mem_alloc(10);
        p=mem_realloc(p, 20);
        g_free(p);
                
        free_debug();
        check_memory_leaks();
        exit(0);
    }
#endif    
    
#if 0
    char *s;
    g_thread_init(NULL);
    init_debug();

    s=g_strdup("");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup(".");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("/");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("./");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("/.");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a./");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a/./");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("./b");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a./b");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a//b");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a/../b");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("../b/c");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a/..");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    s=g_strdup("a/../");
    printf("'%s'  ", s);
    s=optimize_path(s);
    printf(" '%s'\n", s);
    g_free(s);
    
    exit(1);
#endif  
#ifdef Z_MINGW
//    MessageBox(NULL, "Hello World, I'm Tucnak compiled under mingw", "Info", MB_OK);

#endif
#if defined(Z_UNIX_ANDROID) && !defined(Z_MACOS)
    starting_sbrk = (char *)sbrk(0);
#endif
#ifdef ANDROID
    __android_log_print(ANDROID_LOG_DEBUG, "Tucnak", "Tucnak is alive!!! (%s)", __FUNCTION__);
    android_main(argc, argv);
#endif
											 
	z_dump_init("tucnak.dmp", tucnak_crash, "Tucnak-" Z_PLATFORM " " VERSION, DDIR);
	zg_thread_set_name("Tucnak main thread");

    ac = argc;
    av = (char **)argv;

/*    dbg("\n\n");*/
#if !GLIB_CHECK_VERSION(2, 32, 0)
    g_thread_init(NULL);
#endif
	
#ifdef __MINGW32__
    init_mingw();
#endif

    zsel = zselect_init(redraw);
	zselect_msg_set(zsel, main_msg_handler);
	//zselect_start_profile(zsel, 0.099);
	init();
	zselect_loop(zsel);
    terminate_all_subsystems();

    sound(0);
    
    return retval;
}

  

#ifndef Z_MSC_MINGW

void sigchld(void *arg, siginfo_t *siginfo, void *ctx)
{
    int pid;
    
    pid = waitpid(-1, NULL, WNOHANG);
    dbg("sigchld  pid=%d\n", pid);
    if (pid == sound_pid) abort_sound(); 
}

#endif

void set_logs_dir(){
	g_free(logs_dir);

	if (cfg->logdirpath && *cfg->logdirpath)
		logs_dir = g_strdup_printf("%s/%s", tucnak_dir, cfg->logdirpath);
	else
		logs_dir = g_strdup(tucnak_dir);
}

