/*
 * Copyright (C) 2009, 2010 by Nicole Faerber <nicole.faerber@dpin.de>
 *
 * 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.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>

#include <sqlite3.h>

#include "rds.h"
#include "bitstream.h"
#include "tmc.h"
#include "tmc_consts.h"

// #define DEBUG 1

static struct TMC_info tmc_info;
static struct TMC_msg cur_tmc_msg;
static char tmc_msg_str[4096];
static char *tmc_msg_ptr = tmc_msg_str;

static struct _TMC_Private {
	void (*tmc_sid_cb)(char *sid_text, void *user_data);
	void *tmc_sid_cb_data;
	void (*tmc_msg_cb)(char *msg, void *user_data);
	void *tmc_msg_cb_data;
} _tmc_private;

void tmc_init(void)
{
	memset(&_tmc_private, 0, sizeof(struct _TMC_Private));
}

void tmc_set_sid_cb(void (*tmc_sid_cb)(char *sid_text, void *user_data), void *user_data)
{
	_tmc_private.tmc_sid_cb = tmc_sid_cb;
	_tmc_private.tmc_sid_cb_data = user_data;
}

static void reset_msg_buf(void)
{
	tmc_msg_ptr = tmc_msg_str;
	memset(tmc_msg_str, 0, 4096);
}

void tmc_set_msg_cb(void (*tmc_msg_cb)(char *msg, void *user_data), void *user_data)
{
	reset_msg_buf();
	_tmc_private.tmc_msg_cb = tmc_msg_cb;
	_tmc_private.tmc_msg_cb_data = user_data;
}

void interpret_tmc(int event, int location, int extent, int CI, int direction)
{
char sql[256]="";
sqlite3_stmt *ppstmt;
int neg_off=0, pos_off=0, lin_ref=0;
char road_nr[256]="", fst_name[256]="";
char rdir1[256]="", rdir2[256]="";
char last_name[256]="";
char evt_str[256]="";
char type_str1[128]="";
char type_str2[128]="";
char typechr;
int typenr, subtype;

	//fprintf(stderr, "mCI=%d CI=%d\n", cur_tmc_msg.CI, CI);
	if (CI != cur_tmc_msg.CI || CI == -1) {
#ifdef DEBUG
		printf("GF evt=%d loc=%d ext=%d CI=%d dir=%d", event, location, extent, CI, direction);
		printf("ev=%d ", event);
#endif
		snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,LINEAR_REFERENCE,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", location);
		sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
		if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
			printf("lcl.db failed to get location %d event %d\n", location, event);
#endif
			// return; // we cannot do much without location
		} else {
			neg_off = sqlite3_column_int(ppstmt, 2);
			pos_off = sqlite3_column_int(ppstmt, 3);
			lin_ref = sqlite3_column_int(ppstmt, 4);
			strncpy(road_nr, (char *)sqlite3_column_text(ppstmt, 0), 255);
			strncpy(fst_name, (char *)sqlite3_column_text(ppstmt, 1), 255);
			strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 5), 128);
			subtype = sqlite3_column_int(ppstmt, 6);
			typechr = type_str1[0];
			typenr = atoi(&type_str1[1]);
#ifdef DEBUG
			printf(" type=%c %d stype=%d\n", typechr, typenr, subtype);
#endif
			snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype);
			sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
			if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
				printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype);
#endif
			} else {
				strncpy(type_str1, (char *)sqlite3_column_text(ppstmt, 0), 128);			
			}
			if (lin_ref != 0) {
				snprintf(sql, 256, "select FIRST_NAME,SECOND_NAME from lcl where LOCATIONCODE=%d", lin_ref);
				sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
				if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
					printf("lcl.db failed to get linear ref %d\n", lin_ref);
#endif
				} else {
					if (!direction) {
						strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 1), 255);
						strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 0), 255);
					} else {
						strncpy(rdir1, (char *)sqlite3_column_text(ppstmt, 0), 255);
						strncpy(rdir2, (char *)sqlite3_column_text(ppstmt, 1), 255);
					}
				}
			}
			if (extent > 0) {
				// printf("%s ab %s ", road_nr, fst_name);
				if (direction) {
					/* neg */
					while (extent--) {
						snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", neg_off);
						sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
						if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
							printf("lcl.db failed to get location %d\n", location);
#endif
							break;
						} else
							neg_off = sqlite3_column_int(ppstmt, 2);
					}
					//printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1));
				} else {
					/* pos */
					while (extent--) {
						snprintf(sql, 256, "select ROADNUMBER,FIRST_NAME,NEGATIVE_OFFSET,POSITIVE_OFFSET,TYPE,SUBTYPE from lcl where LOCATIONCODE=%d", pos_off);
						sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
						if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
							printf("lcl.db failed to get location %d\n", location);
#endif
							break;
						} else
							pos_off = sqlite3_column_int(ppstmt, 3);
					}
					//printf("bis %s\n", (char *)sqlite3_column_text(ppstmt, 1));
				}
				strncpy(last_name, (char *)sqlite3_column_text(ppstmt, 1), 255);
				extent=1;
				strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 4), 128);
				subtype = sqlite3_column_int(ppstmt, 5);
				typechr = type_str2[0];
				typenr = atoi(&type_str2[1]);
				// printf(" type=%c %d stype=%d\n", typechr, typenr, subtype);
				snprintf(sql, 256, "select SNATDESC from lcl_supc where CLASS='%c' AND TCD=%d AND STCD=%d", typechr, typenr, subtype);
				sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
				if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
					printf("lcl.db failed to get suptype %c %d stype=%d\n", typechr, typenr, subtype);
#endif
				} else {
					strncpy(type_str2, (char *)sqlite3_column_text(ppstmt, 0), 128);
				}
			}
		}
		snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", event);
		sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
		if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
			printf("lcl.db failed to get event %d\n", event);
#endif
		} else {
			strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
		}
		if (CI != -1)
			cur_tmc_msg.CI = CI;

		// here goes the message ;)
		if (lin_ref != 0)
			tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s %s - %s,\n", road_nr, rdir1, rdir2);
		if (location == 64777) {
			tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
		} else {
			if (extent) {
#ifdef DEBUG
				printf("dir=%d\n", direction);
#endif
				if (direction)
					tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str1, fst_name, type_str2, last_name, evt_str);
				else
					tmc_msg_ptr += sprintf(tmc_msg_ptr, "zwischen %s %s und %s %s\n%s\n", type_str2, last_name, type_str1, fst_name, evt_str);
			} else
				tmc_msg_ptr += sprintf(tmc_msg_ptr, "in Höhe %s %s\n%s\n", type_str1, fst_name, evt_str);
		}
	}
}

void parse_tmc_single(unsigned short *rdsgroup)
{
unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF;
unsigned short location = cur_tmc_msg.msgs[0][2];
unsigned char extent = (cur_tmc_msg.msgs[0][1] & ((0x07) << 11)) >> 11;
unsigned char dir = (cur_tmc_msg.msgs[0][1] & ((0x01) << 14)) >> 14;
static unsigned short oevent=0;
static unsigned short olocation=0;

#ifdef DEBUG
	printf(" single group\n");
#endif
	if (olocation != location && oevent != event) {
		interpret_tmc(event, location, extent, -1, dir);
		olocation = location;
		oevent = event;
		if (_tmc_private.tmc_msg_cb != NULL) {
			_tmc_private.tmc_msg_cb(tmc_msg_str, _tmc_private.tmc_msg_cb_data);
		}
		reset_msg_buf();
	}
}

char *replace_str(char *str, char *orig, char *rep)
{
static char buffer[4096];
char *p;

	if(!(p = strstr(str, orig)))  // Is 'orig' even in 'str'?
		return str;

	strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
	buffer[p-str] = '\0';

	sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));

return buffer;
}

void interpret_tmc_multi(void)
{
int i;
unsigned short event = cur_tmc_msg.msgs[0][1] & 0x7FF;
unsigned short location = cur_tmc_msg.msgs[0][2];
unsigned char extent = (cur_tmc_msg.msgs[0][1] & (0x07 << 11)) >> 11;
unsigned char dir = (cur_tmc_msg.msgs[0][1] & (0x01 << 14)) >> 14;
unsigned char label;
unsigned short aevent;
char sql[256]="";
sqlite3_stmt *ppstmt;
char evt_str[256]="";
char lofrstr[16]="";
unsigned char ext_buf[256];

#ifdef DEBUG
	printf("interpret_tmc_multi: sz=%d\n", cur_tmc_msg.msgsz);
	for (i=0; i<cur_tmc_msg.msgsz; i++) {
		printf(" #%d 0x%04x 0x%04x 0x%04x\n", i, cur_tmc_msg.msgs[i][0], cur_tmc_msg.msgs[i][1], cur_tmc_msg.msgs[i][2]);
	}

	printf("TMC event=%d loc=%d ext=%d dir=%d\n", event, location, extent, dir);
#endif

	interpret_tmc(event, location, extent, -1, dir);


	for (i=0; i < cur_tmc_msg.msgsz; i++) {
		ext_buf[i*4] = (cur_tmc_msg.msgs[i+1][1] & 0xff00) >> 8;
		ext_buf[(i*4)+1] = cur_tmc_msg.msgs[i+1][1] & 0xff;
		ext_buf[(i*4)+2] = (cur_tmc_msg.msgs[i+1][2] & 0xff00) >> 8;
		ext_buf[(i*4)+3] = cur_tmc_msg.msgs[i+1][2] & 0xff;
	};
	bitstream_start(ext_buf, cur_tmc_msg.msgsz*4);

	bitstream_get_next_bits(4);

	while (bitstream_bits_remaining() > 0) {
		label = bitstream_get_next_bits(4);
#ifdef DEBUG
		if (OutputFlags & RDS_OUTPUT_TMC) {
			printf("o  '%s' %x\n", EVNT_LABEL[label], label);
		}
#endif
		if (label == 0) {
			unsigned char dur=0;

			dur = bitstream_get_next_bits(3);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " dur=%d\n", dur);
		}
		if (label == 1) {
			unsigned char ctrlcd=0;

			ctrlcd = bitstream_get_next_bits(3);
			if (ctrlcd == 2)
				tmc_msg_ptr += sprintf(tmc_msg_ptr, "in beiden Richtungen\n");
			else
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " ctrlcd=%d\n", ctrlcd);
		}
		if (label == 2) {
			unsigned int lofr=0;

			lofr = bitstream_get_next_bits(5);
			if (lofr == 0) {
				printf("shit - more than 100km\n");
				strcpy(lofrstr, ">100 km");
			}
			if (lofr>0 && lofr<11) {
				printf("lofr = %dkm\n", lofr);
				sprintf(lofrstr, "%d km", lofr);
			}
			if (lofr>10 && lofr<16) {
				printf("lofr = %dkm\n", 12 + ((lofr-11)*2));
				sprintf(lofrstr, "%d km", 12 + ((lofr-11)*2));
			}
			if (lofr>15 && lofr<32)
				printf("lofr = %dkm\n", 25 + ((lofr-16)*5));
				sprintf(lofrstr, "%d km", 25 + ((lofr-16)*5));
		}
		if (label == 3) {
			unsigned int slim=0;

			slim = bitstream_get_next_bits(5);
			if (slim > 0)
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " slim = %d km/h\n", slim*5);
		}
		if (label == 4) {
			unsigned int quant5=0;

			quant5 = bitstream_get_next_bits(5);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant5 = %d km\n", quant5);
		}
		if (label == 5) {
			unsigned int quant8=0;

			quant8 = bitstream_get_next_bits(8);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " quant8 = %d km\n", quant8);
		}
		if (label == 6) {
			unsigned char supinfo=0;

			supinfo = bitstream_get_next_bits(8);
#ifdef DEBUG
			printf(" supinfo = %d\n", supinfo);
#endif
			snprintf(sql, 256, "select TEXT_DE from ev_sup where CODE=%d", supinfo);
			sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
			if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
				printf("lcl.db failed to get ev_sup %d\n", supinfo);
#endif
			} else {
				strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
				tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
			}
		}
		if (label == 7) {
			unsigned int stime=0;

			stime = bitstream_get_next_bits(8);
			// printf(" stime = %d\n", stime);
			if (stime < 96) {
				stime *= 15; // 15 min internval
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %02d:%02d\n", (stime / 60), (stime % 60));
			} else if (stime < 201) {
				stime -= 96;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %d Tage, %d:00 Uhr\n", (stime / 24), (stime % 24));
			} else if (stime < 232) {
				stime -= 201;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab dem %d.\n", stime+1);
			} else {
				stime -= 232;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " ab %s. %s\n", (stime % 2) ? "31":"15", monthname[stime / 2]);
			}
		}
		if (label == 8) {
			unsigned int etime=0;

			etime = bitstream_get_next_bits(8);
			// printf(" etime = %d\n", etime);
			if (etime < 96) {
				etime *= 15; // 15 min internval
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis vor. %02d:%02d\n", (etime / 60), (etime % 60));
			} else if (etime < 201) {
				etime -= 96;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis %d Tage, %d:00 Uhr\n", (etime / 24), (etime % 24));
			} else if (etime < 232) {
				etime -= 201;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis zum %d.\n", etime+1);
			} else {
				etime -= 232;
				tmc_msg_ptr += sprintf(tmc_msg_ptr, " bis etwa %s. %s\n", (etime % 2) ? "31":"15", monthname[etime / 2]);
			}
		}
		if (label == 9) {
			aevent = bitstream_get_next_bits(11);
#ifdef DEBUG
			printf(" aevent = %d ", aevent);
#endif
			snprintf(sql, 256, "select TEXT_DE_NOQUANT from evl where CODE=%d", aevent);
			sqlite3_prepare(lcl_db, sql, 256, &ppstmt, NULL);
			if (sqlite3_step(ppstmt) != SQLITE_ROW) {
#ifdef DEBUG
				printf("-lcl.db failed to get event %d\n", aevent);
#endif
			} else {
				strncpy(evt_str, (char *)sqlite3_column_text(ppstmt, 0), 255);
				// printf("(L)='%s'\n", evt_str);
				if (strlen(lofrstr) != 0 && strstr(evt_str,"(L)")!=NULL) {
					replace_str(evt_str, "(L)", lofrstr);
				}
				tmc_msg_ptr += sprintf(tmc_msg_ptr, "%s\n", evt_str);
			}
		}
		if (label == 10) {
			unsigned char div;

			div = bitstream_get_next_bits(16);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " div = %d\n", div);
		}
		if (label == 11) {
			unsigned char dest;

			dest = bitstream_get_next_bits(16);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " dest = %d\n", dest);
		}
		if (label == 12) {
			unsigned char res;

			res = bitstream_get_next_bits(16);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " res = %d\n", res);
		}
		if (label == 13) {
			unsigned char clink;

			clink = bitstream_get_next_bits(16);
			tmc_msg_ptr += sprintf(tmc_msg_ptr, " clink = %d\n", clink);
		}
		if (label == 14) {
			// tmc_msg_ptr += sprintf(tmc_msg_ptr, " seperator\n");
		}
		if (label == 15) {
#ifdef DEBUG
			printf(" res\n");
#endif
		}
	}
#ifdef DEBUGx
	if (cur_tmc_msg.msgsz > 2) {
		for (i=2; i<cur_tmc_msg.msgsz; i++) {
			printf(" #%d 0x%04x 0x%04x 0x%04x\n", i, cur_tmc_msg.msgs[i][0], cur_tmc_msg.msgs[i][1], cur_tmc_msg.msgs[i][2]);
		}
	}
#endif
	if (_tmc_private.tmc_msg_cb != NULL) {
		_tmc_private.tmc_msg_cb(tmc_msg_str, _tmc_private.tmc_msg_cb_data);
	}
	reset_msg_buf();
}

void parse_tmc_multi(unsigned short *rdsgroup)
{
unsigned char GSI = (rdsgroup[2] & 0x3000) >> 12;
static unsigned char oGSI = 0;
#ifdef DEBUG
unsigned char CI = rdsgroup[1] & 0x07;
#endif

#ifdef DEBUG
	printf(" multi CI=%d ", CI);
#endif
	if (rdsgroup[2] & 0x8000) {
		if (cur_tmc_msg.msgsz > 0) {
			interpret_tmc_multi();
			cur_tmc_msg.msgsz = 0;
		}
#ifdef DEBUG
		printf(" #1 GSI=%d\n", GSI);
#endif
		oGSI = 0;
		memset(&cur_tmc_msg.msgs, 0, 6*3*sizeof(short));
		cur_tmc_msg.msgs[0][0] = rdsgroup[1];
		cur_tmc_msg.msgs[0][1] = rdsgroup[2];
		cur_tmc_msg.msgs[0][2] = rdsgroup[3];
		//msgsz = 1;
	} else {
		if (rdsgroup[2] & 0x4000) {
			// printf(" #2 GSI=%d\n", GSI);
			cur_tmc_msg.msgs[1][0] = rdsgroup[1];
			cur_tmc_msg.msgs[1][1] = rdsgroup[2];
			cur_tmc_msg.msgs[1][2] = rdsgroup[3];
			cur_tmc_msg.msgsz = 2;
		} else {
			if (oGSI == 0) {
				oGSI = GSI;
			}
			// printf(" #%d GSI=%d (oGSI=%d)\n", 2+(oGSI-GSI), GSI, oGSI);
			cur_tmc_msg.msgs[2+(oGSI-GSI)][0] = rdsgroup[1];
			cur_tmc_msg.msgs[2+(oGSI-GSI)][1] = rdsgroup[2];
			cur_tmc_msg.msgs[2+(oGSI-GSI)][2] = rdsgroup[3];
			cur_tmc_msg.msgsz = 2+(oGSI-GSI);
		}
	}
	// printf("  0x%04x 0x%04x\n", rdsgroup[2], rdsgroup[3]);
}

// enum TMCtype { TMC_GROUP=0,  TMC_SINGLE,  TMC_SYSTEM,  TMC_TUNING };

void decode_tmc(unsigned short *rdsgroup)
{
unsigned char X4 = (rdsgroup[1] & 0x10) >> 4;
unsigned char X3 = (rdsgroup[1] & 0x08) >> 3;
unsigned char X0X2 = rdsgroup[1] & 0x07;
//static unsigned char tmc_provider[9] = "";

	if (X4 == 0) { // User message
		if (X3 == 1) { // single group msg
			parse_tmc_single(rdsgroup);
		} else { // multi group msg
			if (X0X2 > 0) {
				parse_tmc_multi(rdsgroup);
			}
#ifdef DEBUG
			else
				printf("err... X0X2(CI) = %d\n", X0X2);
#endif
		}
	} else {
		if (X3 == 1) {
			//printf(" tuning?\n");
		} else {
			// printf(" system ");
			if (X0X2 == 4) {
				tmc_info.provider_str[0] = (rdsgroup[2] & 0xff00) >> 8;
				tmc_info.provider_str[1] = rdsgroup[2] & 0xff;
				tmc_info.provider_str[2] = (rdsgroup[3] & 0xff00) >> 8;
				tmc_info.provider_str[3] = rdsgroup[3] & 0xff;
				if (_tmc_private.tmc_sid_cb != NULL)
					_tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data);
				if (OutputFlags & RDS_OUTPUT_TMC)
					printf("provider = '%s'\n", tmc_info.provider_str);
			} else if (X0X2 == 5) {
				tmc_info.provider_str[4] = (rdsgroup[2] & 0xff00) >> 8;
				tmc_info.provider_str[5] = rdsgroup[2] & 0xff;
				tmc_info.provider_str[6] = (rdsgroup[3] & 0xff00) >> 8;
				tmc_info.provider_str[7] = rdsgroup[3] & 0xff;
				if (_tmc_private.tmc_sid_cb != NULL)
					_tmc_private.tmc_sid_cb(tmc_info.provider_str, _tmc_private.tmc_sid_cb_data);
				if (OutputFlags & RDS_OUTPUT_TMC)
					printf("provider = '%s'\n", tmc_info.provider_str);
			}
#ifdef DEBUG
			 else
				printf("X0X2(CI)=%d \n", X0X2);
#endif
		}
	}
}

