/*
 * Copyright (C) 2016 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 "apptohostevent.h"

#include "contexthub.h"
#include "log.h"

namespace android {

/* AppToHostEvent *************************************************************/

std::unique_ptr<AppToHostEvent> AppToHostEvent::FromBytes(
        const std::vector<uint8_t>& buffer) {
    auto event = std::unique_ptr<AppToHostEvent>(new AppToHostEvent());
    event->Populate(buffer);
    if (!event->IsValid()) {
        return nullptr;
    }

    return event;
}

uint64_t AppToHostEvent::GetAppId() const {
    return GetTypedData()->appId;
}

uint8_t AppToHostEvent::GetDataLen() const {
    return GetTypedData()->dataLen;
}

const uint8_t *AppToHostEvent::GetDataPtr() const {
    return (reinterpret_cast<const uint8_t*>(GetTypedData())
              + sizeof(struct HostHubRawPacket));
}

bool AppToHostEvent::CheckAppId(SensorType sensor_type) const {
    // Make sure the app ID matches what we expect for the sensor type, bail out
    // early if it doesn't
    switch (sensor_type) {
      case SensorType::Accel:
      case SensorType::Gyro:
        if (GetAppId() != kAppIdBoschBmi160Bmm150) {
            return false;
        }
        break;

      case SensorType::Proximity:
        if (GetAppId() != kAppIdAmsTmd2772 && GetAppId() != kAppIdRohmRpr0521 &&
            GetAppId() != kAppIdAmsTmd4903) {
            return false;
        }
        break;

      case SensorType::Barometer:
        if (GetAppId() != kAppIdBoschBmp280) {
            return false;
        }
        break;

      case SensorType::AmbientLightSensor:
        if (GetAppId() != kAppIdAmsTmd4903) {
            return false;
        }
        break;

      default:
        return false;
    }

    return true;
}

bool AppToHostEvent::CheckEventHeader(SensorType sensor_type) const {
    if (GetDataLen() < sizeof(struct SensorAppEventHeader)) {
        return false;
    }

    if (!CheckAppId(sensor_type)) {
        return false;
    }

    return true;
}

bool AppToHostEvent::IsCalibrationEventForSensor(SensorType sensor_type) const {
    if (!CheckEventHeader(sensor_type)) {
        return false;
    }

    // If we made it this far, we only need to confirm the message ID
    auto header = reinterpret_cast<const struct SensorAppEventHeader *>(
        GetDataPtr());
    return (header->msgId == SENSOR_APP_MSG_CALIBRATION_RESULT);
}

bool AppToHostEvent::IsTestEventForSensor(SensorType sensor_type) const {
    if (!CheckEventHeader(sensor_type)) {
        return false;
    }

    // If we made it this far, we only need to confirm the message ID
    auto header = reinterpret_cast<const struct SensorAppEventHeader *>(
        GetDataPtr());
    return (header->msgId == SENSOR_APP_MSG_TEST_RESULT);
}

bool AppToHostEvent::IsValid() const {
    const HostHubRawPacket *packet = GetTypedData();
    if (!packet) {
        return false;
    }

    // dataLen should specify the amount of data that follows the event type
    // and HostHubRawPacket headers
    if (event_data.size() < (sizeof(uint32_t) + sizeof(struct HostHubRawPacket)
                               + packet->dataLen)) {
        LOGW("Invalid/short AppToHost event of size %zu", event_data.size());
        return false;
    }

    return true;
}

const HostHubRawPacket *AppToHostEvent::GetTypedData() const {
    // After the event type header (uint32_t), we should have struct
    // HostHubRawPacket
    if (event_data.size() < sizeof(uint32_t) + sizeof(struct HostHubRawPacket)) {
        LOGW("Invalid/short AppToHost event of size %zu", event_data.size());
        return nullptr;
    }
    return reinterpret_cast<const HostHubRawPacket *>(
        event_data.data() + sizeof(uint32_t));
}

}  // namespace android