/*
* FOXMP3 
* Copyright (c) 2006 acmesystems.it - john@acmesystems.it
*
* 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, MA02111-1307USA
*
* Feedback, Bugs...  info@acmesystems.it 
*
*/ 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/poll.h>
#include <signal.h>
#include "lib/mp3.h"

STATE states[MAX_STATE_COUNT];
static EVENT events[MAX_EVENT_COUNT];
static TRANSITION transitions[MAX_TRANSITION_COUNT];
static int transition_count = 0;
int state_current = MP3_STATE_STARTUP;
static int state_previous = MP3_STATE_NONE;
static int shutdown = 0;

void state_setup(void){
	EVENT_ADD(MP3_EVENT_TIMEOUT, "Timeout");
	EVENT_ADD(MP3_EVENT_FILE, "File");
	EVENT_ADD(MP3_EVENT_STREAM, "Stream"); 
	EVENT_ADD(MP3_EVENT_STOP, "Stop");
	EVENT_ADD(MP3_EVENT_ERROR, "Error");
	EVENT_ADD(MP3_EVENT_SHUTDOWN, "Shutdown");
	EVENT_ADD(MP3_EVENT_END, "End");

	STATE_ADD(MP3_STATE_STARTUP, "Startup", state_startup_enter, NULL);
	STATE_ADD(MP3_STATE_IDLE, "Idle", state_idle_enter, NULL);
	STATE_ADD(MP3_STATE_FILE_START, "File startup", state_file_startup_enter, state_file_startup_leave);
	STATE_ADD(MP3_STATE_FILE_HANDLE, "File handle", state_file_handle_enter, state_file_handle_leave);
	STATE_ADD(MP3_STATE_STREAM_START, "Stream start", state_stream_startup_enter, state_stream_startup_leave);
	STATE_ADD(MP3_STATE_STREAM_HANDLE, "Stream handle", state_stream_handle_enter, state_stream_handle_leave);
	STATE_ADD(MP3_STATE_ERROR, "Error", state_error_enter, NULL);
	STATE_ADD(MP3_STATE_SHUTDOWN, "Shutdown", state_shutdown_enter, NULL);

	TRANSITION_ADD(MP3_STATE_STARTUP, MP3_EVENT_TIMEOUT, MP3_STATE_IDLE);
	
	TRANSITION_ADD(MP3_STATE_IDLE, MP3_EVENT_TIMEOUT, MP3_STATE_IDLE);
	TRANSITION_ADD(MP3_STATE_IDLE, MP3_EVENT_FILE, MP3_STATE_FILE_START);
	TRANSITION_ADD(MP3_STATE_IDLE, MP3_EVENT_STREAM, MP3_STATE_STREAM_START);
	
	TRANSITION_ADD(MP3_STATE_FILE_START, MP3_EVENT_TIMEOUT, MP3_STATE_FILE_HANDLE);
	TRANSITION_ADD(MP3_STATE_FILE_START, MP3_EVENT_ERROR, MP3_STATE_ERROR);

	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_TIMEOUT, MP3_STATE_FILE_HANDLE);
	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_END, MP3_STATE_IDLE);
	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_ERROR, MP3_STATE_ERROR);
	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_FILE, MP3_STATE_FILE_START);
	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_STOP, MP3_STATE_IDLE);
	TRANSITION_ADD(MP3_STATE_FILE_HANDLE, MP3_EVENT_STREAM, MP3_STATE_STREAM_START);

	TRANSITION_ADD(MP3_STATE_STREAM_START, MP3_EVENT_TIMEOUT, MP3_STATE_STREAM_HANDLE);
	TRANSITION_ADD(MP3_STATE_STREAM_START, MP3_EVENT_ERROR, MP3_STATE_ERROR);

	TRANSITION_ADD(MP3_STATE_STREAM_HANDLE, MP3_EVENT_TIMEOUT, MP3_STATE_STREAM_HANDLE);
	TRANSITION_ADD(MP3_STATE_STREAM_HANDLE, MP3_EVENT_STOP, MP3_STATE_IDLE);
	TRANSITION_ADD(MP3_STATE_STREAM_HANDLE, MP3_EVENT_ERROR, MP3_STATE_ERROR);
	TRANSITION_ADD(MP3_STATE_STREAM_HANDLE, MP3_EVENT_FILE, MP3_STATE_FILE_START);
	TRANSITION_ADD(MP3_STATE_STREAM_HANDLE, MP3_EVENT_STREAM, MP3_STATE_STREAM_START);
	
	TRANSITION_ADD(MP3_STATE_ERROR, MP3_EVENT_TIMEOUT, MP3_STATE_IDLE);

	TRANSITION_ADD(MP3_STATE_DEFAULT, MP3_EVENT_ERROR, MP3_STATE_ERROR);
	TRANSITION_ADD(MP3_STATE_DEFAULT, MP3_EVENT_SHUTDOWN, MP3_STATE_SHUTDOWN);
	state_startup_enter(0, 0, NULL);
};

void state_transition(int transition, int event, EVENT_PARAM *param){
	state_previous = state_current;
	state_current = transitions[transition].new_state;
	if(state_previous != state_current){
		printf("\n -- Transition %s -> %s\n", 
			states[state_previous].name, 
			states[state_current].name);
	};
	if(states[state_previous].leave != NULL)
			states[state_previous].leave(state_current, event);
	if(states[state_current].enter != NULL)
		states[state_current].enter(state_previous, event, param);
	if(param){
		if(param->text){
			free(param->text);
		};
		free(param);
	};
};

void state_event(int event, EVENT_PARAM *param){
	int i = 0;
	if(event != MP3_EVENT_TIMEOUT){
		printf("\n -- Got event %s (%d) in state %s (%d)\n", 
			events[event].name, 
			event, 
			states[state_current].name, 
			state_current);
	};
	for(i = 0; i < transition_count; i++){
		if((transitions[i].old_state == state_current) 
				&& (transitions[i].event == event)){
			state_transition(i, event, param);			
			return;
		};
	};
	for(i = 0; i < transition_count; i++){
		if((transitions[i].old_state == MP3_STATE_DEFAULT) 
				&& (transitions[i].event == event)){
			state_transition(i, event, param);			
			return;
		};
	};
	if(param){
		if(param->text){
			free(param->text);
		};
		free(param);
	};
	printf("Event %s not handled in state %s\n", events[event].name, states[state_current].name);
};

void state_mainloop(void){
	while(1){
		if(shutdown){
			state_event(MP3_EVENT_SHUTDOWN, NULL);
		};
		mp3_nix_socket_handle();
		mp3_tcp_socket_handle();
		if(state_current == MP3_STATE_IDLE){
			poll(0, 0, 200);
		} else {
			poll(0, 0, 1);
		};
		state_event(MP3_EVENT_TIMEOUT, NULL);
	};
};

EVENT_PARAM* state_new_event(unsigned char *text, int numeric){
	EVENT_PARAM *p = (EVENT_PARAM*)malloc(sizeof(EVENT_PARAM));
	if(text){
		p->text = strdup(text);
	} else {
		p->text = NULL;
	};
	p->numeric = numeric;
	return p;
};

void sig_handler(int sig){
	shutdown = 1;
};

int main(int argc, char **argv) {	
	unsigned char daemonize = 1;

	printf("\n\n");
	printf("ALPMP3 - MP3 Statemachine v1.0\n");

	if(argc > 1){
		if(!strcmp(argv[1], "--no-daemon")){
			daemonize = 0;
		} else {
			printf("I am now going to daemonize. If you want me to stay in ");
			printf("the foreground, add the parameter --no-daemon.\n");
		};
	};
	
	if(daemonize){
		daemon(0, 0);
		printf("----------------------------------------\n\n");
		printf("Made by openwrt.org (blogic@openwrt.org)\n");
		printf("\n");
	};

	signal(SIGINT, sig_handler); 
	
	state_setup();
	state_mainloop();

	return 0;
}