/******************************************************************************
 * $Id: main.c 580 2012-03-29 09:56:21Z yamada.rj $
 ******************************************************************************
 *
 * Copyright (C) 2012 Asahi Kasei Microdevices Corporation, Japan
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "AKFS_Common.h"
#include "AKFS_Compass.h"
#include "AKFS_Disp.h"
#include "AKFS_FileIO.h"
#include "AKFS_Measure.h"
#include "AKFS_APIs.h"

#ifndef WIN32
#include <sched.h>
#include <pthread.h>
#include <linux/input.h>
#endif

#define ERROR_INITDEVICE		(-1)
#define ERROR_OPTPARSE			(-2)
#define ERROR_SELF_TEST			(-3)
#define ERROR_READ_FUSE			(-4)
#define ERROR_INIT				(-5)
#define ERROR_GETOPEN_STAT		(-6)
#define ERROR_STARTCLONE		(-7)
#define ERROR_GETCLOSE_STAT		(-8)

/* Global variable. See AKFS_Common.h file. */
int g_stopRequest = 0;
int g_opmode = 0;
int g_dbgzone = 0;
int g_mainQuit = AKD_FALSE;

/* Static variable. */
static pthread_t s_thread;  /*!< Thread handle */

/*!
 A thread function which is raised when measurement is started.
 @param[in] args This parameter is not used currently.
 */
static void* thread_main(void* args)
{
	AKFS_MeasureLoop();
	return ((void*)0);
}

/*!
  Signal handler.  This should be used only in DEBUG mode.
  @param[in] sig Event
 */
static void signal_handler(int sig)
{
	if (sig == SIGINT) {
		ALOGE("SIGINT signal");
		g_stopRequest = 1;
		g_mainQuit = AKD_TRUE;
	}
}

/*!
 Starts new thread.
 @return If this function succeeds, the return value is 1. Otherwise,
 the return value is 0.
 */
static int startClone(void)
{
	pthread_attr_t attr;

	pthread_attr_init(&attr);
	g_stopRequest = 0;
	if (pthread_create(&s_thread, &attr, thread_main, NULL) == 0) {
		return 1;
	} else {
		return 0;
	}
}

/*!
 This function parse the option.
 @retval 1 Parse succeeds.
 @retval 0 Parse failed.
 @param[in] argc Argument count
 @param[in] argv Argument vector
 @param[out] layout_patno
 */
int OptParse(
	int		argc,
	char*	argv[],
	AKFS_PATNO*	layout_patno)
{
#ifdef WIN32
	/* Static */
#if defined(AKFS_WIN32_PAT1)
	*layout_patno = PAT1;
#elif defined(AKFS_WIN32_PAT2)
	*layout_patno = PAT2;
#elif defined(AKFS_WIN32_PAT3)
	*layout_patno = PAT3;
#elif defined(AKFS_WIN32_PAT4)
	*layout_patno = PAT4;
#elif defined(AKFS_WIN32_PAT5)
	*layout_patno = PAT5;
#else
	*layout_patno = PAT1;
#endif
	g_opmode = OPMODE_CONSOLE;
	/*g_opmode = 0;*/
	g_dbgzone = AKMDATA_LOOP | AKMDATA_TEST;
#else
	int		opt;
	char	optVal;

	*layout_patno = PAT_INVALID;

	while ((opt = getopt(argc, argv, "sm:z:")) != -1) {
		switch(opt){
			case 'm':
				optVal = (char)(optarg[0] - '0');
				if ((PAT1 <= optVal) && (optVal <= PAT8)) {
					*layout_patno = (AKFS_PATNO)optVal;
					AKMDEBUG(DBG_LEVEL2, "%s: Layout=%d\n", __FUNCTION__, optVal);
				}
				break;
			case 's':
				g_opmode |= OPMODE_CONSOLE;
				break;
            case 'z':
                /* If error detected, hopefully 0 is returned. */
                errno = 0;
                g_dbgzone = (int)strtol(optarg, (char**)NULL, 0); 
                AKMDEBUG(DBG_LEVEL2, "%s: Dbg Zone=%d\n", __FUNCTION__, g_dbgzone);
                break;
			default:
				ALOGE("%s: Invalid argument", argv[0]);
				return 0;
		}
	}

	/* If layout is not specified with argument, get parameter from driver */
	if (*layout_patno == PAT_INVALID) {
		int16_t n;
		if (AKD_GetLayout(&n) == AKM_SUCCESS) {
			if ((PAT1 <= n) && (n <= PAT8)) {
				*layout_patno = (AKFS_PATNO)n;
			}
		}
	}
	/* Error */
	if (*layout_patno == PAT_INVALID) {
		ALOGE("No layout is specified.");
		return 0;
	}
#endif

	return 1;
}

void ConsoleMode(void)
{
	/*** Console Mode *********************************************/
	while (AKD_TRUE) {
		/* Select operation */
		switch (Menu_Main()) {
		case MODE_SelfTest:
			AKFS_SelfTest();
			break;
		case MODE_Measure:
			/* Reset flag */
			g_stopRequest = 0;
			/* Measurement routine */
			AKFS_MeasureLoop();
			break;

		case MODE_Quit:
			return;

		default:
			AKMDEBUG(DBG_LEVEL0, "Unknown operation mode.\n");
			break;
		}
	}
}

int main(int argc, char **argv)
{
	int			retValue = 0;
	AKFS_PATNO	pat;
	uint8		regs[3];

	/* Show the version info of this software. */
	Disp_StartMessage();

#if ENABLE_AKMDEBUG
	/* Register signal handler */
	signal(SIGINT, signal_handler);
#endif

	/* Open device driver */
	if(AKD_InitDevice() != AKD_SUCCESS) {
		retValue = ERROR_INITDEVICE;
		goto MAIN_QUIT;
	}

	/* Parse command-line options */
	/* This function calls device driver function to get layout */
	if (OptParse(argc, argv, &pat) == 0) {
		retValue = ERROR_OPTPARSE;
		goto MAIN_QUIT;
	}

	/* Self Test */
	if (g_opmode & OPMODE_FST){
		if (AKFS_SelfTest() != AKD_SUCCESS) {
			retValue = ERROR_SELF_TEST;
			goto MAIN_QUIT;
		}
	}

	/* OK, then start */
	if (AKFS_ReadAK8975FUSEROM(regs) != AKM_SUCCESS) {
		retValue = ERROR_READ_FUSE;
		goto MAIN_QUIT;
	}

	/* Initialize library. */
	if (AKFS_Init(pat, regs) != AKM_SUCCESS) {
		retValue = ERROR_INIT;
		goto MAIN_QUIT;
	}

	/* Start console mode */
	if (g_opmode & OPMODE_CONSOLE) {
		ConsoleMode();
		goto MAIN_QUIT;
	}

	/*** Start Daemon ********************************************/
	while (g_mainQuit == AKD_FALSE) {
		int st = 0;
		/* Wait until device driver is opened. */
		if (AKD_GetOpenStatus(&st) != AKD_SUCCESS) {
			retValue = ERROR_GETOPEN_STAT;
			goto MAIN_QUIT;
		}
		if (st == 0) {
			ALOGI("Suspended.");
		} else {
			ALOGI("Compass Opened.");
			/* Reset flag */
			g_stopRequest = 0;
			/* Start measurement thread. */
			if (startClone() == 0) {
				retValue = ERROR_STARTCLONE;
				goto MAIN_QUIT;
			}

			/* Wait until device driver is closed. */
			if (AKD_GetCloseStatus(&st) != AKD_SUCCESS) {
				retValue = ERROR_GETCLOSE_STAT;
				g_mainQuit = AKD_TRUE;
			}
			/* Wait thread completion. */
			g_stopRequest = 1;
			pthread_join(s_thread, NULL);
			ALOGI("Compass Closed.");
		}
	}

MAIN_QUIT:

	/* Release library */
	AKFS_Release();
	/* Close device driver. */
	AKD_DeinitDevice();
	/* Show the last message. */
	Disp_EndMessage(retValue);

	return retValue;
}