/*
* binclock.c
*
* Released under GPL
*
* Copyright (C) 2004 Nico Golde <binclock@ngolde.de>
* Homepage: http://www.ngolde.de
* Latest change: Sam Jul 24 13:33:04 CEST 2004
*
* 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 2
* 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* Description:
* binary clock for text terminals
* with color support
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <limits.h>

#define MAX 4096

/* version number */
static char *versionstring="BinClock 1.5 [2004-07-24]";

/* help */
static int bclock_usage(void){
    puts (versionstring);
    puts("usage: binclock [options]\n"
         "\n"
         "short options:\n"
         "-v      print the version number + release date + exit.\n"
         "-h      print version info and startup options  + exit.\n"
         "-l      loop the time every second.\n"
         "-n      shows the time with an additional normal format.\n"
         "-t      show traditional binary output (without color)\n\n"
         "long options:\n"
         "--color=[on|off] default: on\n");
    exit(0);
}

/* show version */
static int bclock_version(void){
    puts (versionstring);
    exit(0);
}

/* options */
static struct option long_options[] = {
    {"help", 0, NULL, 'h'},
    {"version", 0, NULL, 'v'},
    {"color", 1, NULL, 'c'},
    {"loop", 0, NULL, 'l'},
    {"normal" ,0, NULL, 'n'},
    {"traditional", 0, NULL, 't'},
    {NULL,0,NULL,0}
};
typedef struct{
    char *char_one;
    char *char_zero;
    int color_one;
    int color_zero;
} OPTIONS;
OPTIONS o;

/* binary printing */
static void printbin( int arg ){
    if( arg > 0 ) printbin( arg >> 1 );
    printf( "%d", arg & 0x1 );
}

static void traditional_outpt(int looop){
    time_t the_time;
    struct tm *ptr_time;
    time(&the_time);
    ptr_time=localtime(&the_time);
    printbin(ptr_time->tm_hour); printf(" : ");
    printbin(ptr_time->tm_min); printf(" : ");
    printbin(ptr_time->tm_sec); printf("\n");
    if(looop==1){
        sleep(1);
        system("clear");
        traditional_outpt(1);
    }    
}

int mkcolorint_check(char *val, int line, char *key) {
    if(!strcmp(val,"black"))          return 0;
    else if (!strcmp(val, "red"))     return 1;
    else if (!strcmp(val, "green"))   return 2;
    else if (!strcmp(val, "yellow"))  return 3;
    else if (!strcmp(val, "blue"))    return 4;
    else if (!strcmp(val, "magenta")) return 5;
    else if (!strcmp(val, "cyan"))    return 6;
    else if (!strcmp(val, "white"))   return 7;
    else{
        printf("parse error in:\nline: %d\nstring: %s\n\n",line,key);
        exit(1);
    }
}

static int parse_config(void){
    FILE *conf;
    char buffer[MAX], *home, fn[MAX];  
    unsigned int line=0;
    char *filename = "/.binclockrc";

    if ((home=getenv("HOME")))
	    snprintf(fn, 100, "%s%s", home, filename);
    else *fn='\0';

    /* initialize variables */
    o.char_one="1";
    o.char_zero="0";
    o.color_one=1;
    o.color_zero=2;

    /* try to open global config file */
    if(!(conf=fopen(fn,"r"))){
        if((conf=fopen("/etc/binclockrc","r"))==NULL){
            if((conf=fopen("/usr/local/etc/binclockrc","r"))==NULL){
                /* if there is no config file */
    	    	perror("binclock: using defaults... binclockrc not found");
    	    	return -1;
            }
        }
    }

    /* parse config file */
    while(fgets(buffer, MAX, conf)!=NULL){
        line++;
    	{	
            char *key, *val;
            key=strtok(buffer, "=");	/* strtok ist mächtig ... */
            val=strtok(NULL, "\r\n");
            if (!val) continue;	/* etwas ohne "=" in der zeile -> ignorieren */
            key=strtok(key, " \t");	/*dewhite*/
            val=strtok(val, " \t#");
            if(!strncasecmp(key,"color_one",9))
                o.color_one=mkcolorint_check(val, line, key);
            else if(!strncasecmp(key,"color_zero",10))
                o.color_zero=mkcolorint_check(val, line, key);
            else if(!strncasecmp(key,"char_one",8))  o.char_one=strdup(val);
            else if(!strncasecmp(key,"char_zero",9)) o.char_zero=strdup(val);
            else{
                printf("parse error in:\nline: %d\nstring: %s\n\n",line,key);
                exit(1);
            }
        }
    }
    fclose(conf);
return 0;
}


/* main function for showing time and filling the matrix */
static void bin_time(int color,int normal){

    /* colors */
    const char *const colors="\033[40;3%1.1d;1m";
    int i,j;
    int clock[10][10];
    time_t t;
    char tmpstr[50];
    struct tm *tmworld;
    
    t=time(0);
    
    /* fetch localtime */
    tmworld=localtime(&t);
    
    /* fill the tmpstr with hours, minutes and seconds */
    snprintf(tmpstr, 50,"%02i%02i%02i",
    	     tmworld->tm_hour, tmworld->tm_min, tmworld->tm_sec);
    /* fill the matrix */
    for(i=0; i<6;i++){
    	clock[i][0] = 0;
    	clock[i][1] = 0;
    	clock[i][2] = 0;
    	clock[i][3] = 0;
	    switch(tmpstr[i]){
    	    case'1':
                clock[i][3]=1;
                break;
    	    case'2':
                clock[i][2]=1;
                break;
    	    case'3':
                clock[i][3]=1;
                clock[i][2]=1;
                break;
    	    case'4':
                clock[i][1]=1;
                break;
    	    case'5':
                clock[i][3]=1;
                clock[i][1]=1;
                break;
	        case'6':
                clock[i][2]=1;
                clock[i][1]=1;
                break;
            case'7':
                clock[i][3]=1;
                clock[i][2]=1;
                clock[i][1]=1;
                break;
    	    case'8':
                clock[i][0]=1;
                break;
    	    case'9':
                clock[i][3]=1;
                clock[i][0]=1;
                break;
    	    }
    }

    /* print the content of the matrix */
    for(i=0;i<=3;i++){
        for(j=0;j<=5;j++){
            /* if binclock is started with --color=on option */
            if(color){
                if(clock[j][i]==1){
                    sprintf(tmpstr, colors, o.color_one);
                    /* print colorstring */
                    printf("%s%s ", tmpstr, o.char_one);
                }
                else{
                    sprintf(tmpstr, colors, o.color_zero);
                    /* print colorstring */
                    printf("%s%s ", tmpstr, o.char_zero);
                }
            }

            /* print time without color */
            else{
                if(clock[j][i])
                    printf("%s ",o.char_one);
                else
                    printf("%s ",o.char_zero);
           }
            if(i==0 && j==5 && normal==1)
                printf("\033[0m%02i:%02i:%02i\033[0m",tmworld->tm_hour, tmworld->tm_min, tmworld->tm_sec);
        }
        printf("\n");
    }

    if(color)
        /* set the terminal color to default */
        printf("\033[0m");

    /* flush the standard output */
    fflush(stdout);
}

/* main function */
int main(int argc, char *argv[]){
    int l=0,c=1,t=0, n=0,option,akt_optind,option_index=0;

    /* parse program arguments */
    while(1){
        akt_optind=optind;
        option=getopt_long(argc,argv,"lnc:hvt",long_options,&option_index);

        /* if there is o argument leave while */
            if(option==EOF)
                break;
            switch(option){
                case 'h': 	
            		bclock_usage();
	            	break;
                case 'v':
                    bclock_version();
        	    	break;
                case 'l':
                    l=1;
    	        	break;
                case 'n':
                    n=1;
                    break;
	            /* if --color option is set */
                case 'c':
                    if(!strcmp(optarg,"on")) c=1;
                    else if (!strcmp(optarg,"off")) c=0;
                    /* if --color option is not on or off */
                    else{
                        puts(versionstring);
                        printf("Unknown option: \"--%s\"\n"
                               "More info with: \"%s -h\"\n",optarg,argv[0]);
                        return -1;
                        break;
                    }
                    break;
                 case 't':
                     t=1;
                     break;
                default:
                    bclock_usage();                
                    break;
    	    }
    }

    /* no traditional output */
    if(t==0){
        parse_config();
        do{
            /* clear the screen */
            if (l) system("clear");		
                bin_time(c,n);

            /* sleep one second */
            sleep(l);
        } while(l); 
    }
    else if(t==1 && l==1){
        traditional_outpt(1);
    }
    else{    
        traditional_outpt(0);
    }    
return 0;
}

