/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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 <fcntl.h>
#include <errno.h>
#include <math.h>
#include <poll.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/select.h>
#include <cutils/log.h>
#include <linux/kxtf9.h>

#include "KionixSensor.h"

#define KIONIX_IOCTL_ENABLE_OUTPUT	KXTF9_IOCTL_ENABLE_OUTPUT
#define KIONIX_IOCTL_DISABLE_OUTPUT	KXTF9_IOCTL_DISABLE_OUTPUT
#define KIONIX_IOCTL_GET_ENABLE		KXTF9_IOCTL_GET_ENABLE
#define KIONIX_IOCTL_UPDATE_ODR		KXTF9_IOCTL_UPDATE_ODR

#define KIONIX_UNIT_CONVERSION(value) ((value) * GRAVITY_EARTH / (1024.0f))

/*****************************************************************************/

KionixSensor::KionixSensor()
    : SensorBase(DIR_DEV, INPUT_NAME_ACC),
      mEnabled(0),
      mDelay(-1),
      mInputReader(32),
      mHasPendingEvent(false)
{
    mPendingEvent.version = sizeof(sensors_event_t);
    mPendingEvent.sensor = ID_A;
    mPendingEvent.type = SENSOR_TYPE_ACCELEROMETER;
    memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));

	open_device();
}

KionixSensor::~KionixSensor() {
    if (mEnabled) {
        setEnable(0, 0);
    }

	close_device();
}

int KionixSensor::setInitialState() {
    struct input_absinfo absinfo;

	if (mEnabled) {
    	if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_X), &absinfo)) {
			mPendingEvent.acceleration.x = KIONIX_UNIT_CONVERSION(absinfo.value);
		}
    	if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_Y), &absinfo)) {
			mPendingEvent.acceleration.y = KIONIX_UNIT_CONVERSION(absinfo.value);
		}
		if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ACCEL_Z), &absinfo)) {
			mPendingEvent.acceleration.z = KIONIX_UNIT_CONVERSION(absinfo.value);
		}
	}
    return 0;
}

bool KionixSensor::hasPendingEvents() const {
    return mHasPendingEvent;
}

int KionixSensor::setEnable(int32_t handle, int enabled) {
    int err = 0;
	int opDone = 0;

	/* handle check */
	if (handle != ID_A) {
		ALOGE("KionixSensor: Invalid handle (%d)", handle);
		return -EINVAL;
	}

	if (mEnabled <= 0) {
		if (enabled) {
			err = ioctl(dev_fd, KIONIX_IOCTL_ENABLE_OUTPUT);
			opDone = 1;
		}
	} else if (mEnabled == 1) {
		if (!enabled) {
			err = ioctl(dev_fd, KIONIX_IOCTL_DISABLE_OUTPUT);
			opDone = 1;
		}
	}
	if (err != 0) {
		ALOGE("KionixSensor: IOCTL failed (%s)", strerror(errno));
		return err;
	}
	if (opDone) {
		ALOGD("KionixSensor: Control set %d", enabled);
		setInitialState();
	}

	if (enabled) {
		mEnabled++;
		if (mEnabled > 32767) mEnabled = 32767;
	} else {
		mEnabled--;
		if (mEnabled < 0) mEnabled = 0;
	}
	ALOGD("KionixSensor: mEnabled = %d", mEnabled);

    return err;
}

int KionixSensor::setDelay(int32_t handle, int64_t delay_ns)
{
	int err = 0;
	int ms; 

	/* handle check */
	if (handle != ID_A) {
		ALOGE("KionixSensor: Invalid handle (%d)", handle);
		return -EINVAL;
	}

	if (mDelay != delay_ns) {
		ms = delay_ns / 1000000;
        if (ioctl(dev_fd, KIONIX_IOCTL_UPDATE_ODR, &ms)) {
			return -errno;
		}
		mDelay = delay_ns;
	}

	return err;
}

int64_t KionixSensor::getDelay(int32_t handle)
{
	return (handle == ID_A) ? mDelay : 0;
}

int KionixSensor::getEnable(int32_t handle)
{
	return (handle == ID_A) ? mEnabled : 0;
}

int KionixSensor::readEvents(sensors_event_t* data, int count)
{
    if (count < 1)
        return -EINVAL;

    if (mHasPendingEvent) {
        mHasPendingEvent = false;
        mPendingEvent.timestamp = getTimestamp();
        *data = mPendingEvent;
        return mEnabled ? 1 : 0;
    }

    ssize_t n = mInputReader.fill(data_fd);
    if (n < 0)
        return n;

    int numEventReceived = 0;
    input_event const* event;

    while (count && mInputReader.readEvent(&event)) {
        int type = event->type;
        if (type == EV_ABS) {
            float value = event->value;
            if (event->code == EVENT_TYPE_ACCEL_X) {
                mPendingEvent.acceleration.x = KIONIX_UNIT_CONVERSION(value);
            } else if (event->code == EVENT_TYPE_ACCEL_Y) {
                mPendingEvent.acceleration.y = KIONIX_UNIT_CONVERSION(value);
            } else if (event->code == EVENT_TYPE_ACCEL_Z) {
                mPendingEvent.acceleration.z = KIONIX_UNIT_CONVERSION(value);
            }
        } else if (type == EV_SYN) {
            mPendingEvent.timestamp = timevalToNano(event->time);
            if (mEnabled) {
                *data++ = mPendingEvent;
                count--;
                numEventReceived++;
            }
        } else {
            ALOGE("KionixSensor: unknown event (type=%d, code=%d)",
                    type, event->code);
        }
        mInputReader.next();
    }

    return numEventReceived;
}