#include <teec_callback_handler.h>

#include <stdio.h>

#include <fs_po.h>
#include <protocol.h>
#include <tees_log.h>
#include <teec_param_utils.h>
#include <teec_common.h>

static TEEC_Result TeecResultFromMbFs(MultibuildFsResult res) {
  switch (res) {
    case MB_FS_OK: {
      return TEE_SUCCESS;
    }
    case MB_FS_ERROR_NOT_FOUND: {
      return TEEC_ERROR_ITEM_NOT_FOUND;
    }
    case MB_FS_ERROR_SHORT_BUFFER: {
      return TEEC_ERROR_SHORT_BUFFER;
    }
    case MB_FS_ERROR_BAD_PARAMETERS: {
      return TEEC_ERROR_BAD_PARAMETERS;
    }
    default:
      return TEE_ERROR_GENERIC;
  }
}

static TEEC_Result DeletePersistentObjectFromFS(TEEC_UUID *uuid,
                                                const uint8_t *id,
                                                uint32_t id_len) {
  return TeecResultFromMbFs(FsPoRemove((const uint8_t *)uuid,
                                       sizeof(TEEC_UUID), id, id_len));
}

static TEEC_Result ReadPersistentObjectFromFS(TEEC_UUID *uuid,
                                              const uint8_t *id,
                                              uint32_t id_len,
                                              uint8_t *data,
                                              uint32_t *data_len) {
  return TeecResultFromMbFs(FsPoRead((const uint8_t *)uuid,
                                     sizeof(TEEC_UUID),
                                     id, id_len, data, data_len,
                                     MB_FS_STORE_DATA));
}

static TEEC_Result WritePersistentObjectToFS(TEEC_UUID *uuid,
                                             const uint8_t *id,
                                             uint32_t id_len,
                                             const uint8_t *data,
                                             uint32_t data_len) {
  return TeecResultFromMbFs(FsPoStore((const uint8_t *)uuid,
                                      sizeof(TEEC_UUID),
                                      id, id_len, data, data_len,
                                      MB_FS_STORE_DATA));
}

static TEEC_Result ReadPoMetadataFromFS(TEEC_UUID *uuid,
                                        const uint8_t *id,
                                        uint32_t id_len,
                                        uint8_t *data,
                                        uint32_t *data_len) {
  return TeecResultFromMbFs(FsPoRead((const uint8_t *)uuid,
                                     sizeof(TEEC_UUID),
                                     id, id_len, data,
                                     data_len, MB_FS_STORE_METADATA));
}

static TEEC_Result WritePoMetadataToFS(TEEC_UUID *uuid,
                                       const uint8_t *id,
                                       uint32_t id_len,
                                       const uint8_t *data,
                                       uint32_t data_len) {
  return TeecResultFromMbFs(FsPoStore((const uint8_t *)uuid,
                                      sizeof(TEEC_UUID),
                                      id, id_len, data,
                                      data_len, MB_FS_STORE_METADATA));
}

static TEEC_Result ReadPoAttributesFromFS(TEEC_UUID *uuid,
                                          const uint8_t *id,
                                          uint32_t id_len,
                                          uint8_t *data,
                                          uint32_t *data_len) {
  return TeecResultFromMbFs(FsPoRead((const uint8_t *)uuid,
                                     sizeof(TEEC_UUID),
                                     id, id_len, data,
                                     data_len, MB_FS_STORE_ATTRIB));
}

static TEEC_Result GetReeTime(const uint8_t *data,
                              uint32_t data_len) {
  if (!data || sizeof(MB_Time) > data_len) {
    return TEEC_ERROR_BAD_PARAMETERS;
  }
  return TeecResultFromMbFs(ReeTimeGet((MB_Time *)data));
}

static TEEC_Result WritePoAttributesToFS(TEEC_UUID *uuid,
                                         const uint8_t *id,
                                         uint32_t id_len,
                                         const uint8_t *data,
                                         uint32_t data_len) {
  return TeecResultFromMbFs(FsPoStore((const uint8_t *)uuid,
                                      sizeof(TEEC_UUID),
                                      id, id_len, data,
                                      data_len, MB_FS_STORE_ATTRIB));
}


static TEEC_Result InitPoUpdate(TEEC_UUID *uuid,
                                const uint8_t *id,
                                uint32_t id_len) {
  return TeecResultFromMbFs(FsPoInitUpdate((const uint8_t *)uuid,
                                           sizeof(TEEC_UUID), id, id_len));
}

static TEEC_Result AbortPoUpdate(TEEC_UUID *uuid,
                                 const uint8_t *id,
                                 uint32_t id_len) {
  return TeecResultFromMbFs(FsPoAbortUpdate((const uint8_t *)uuid,
                                            sizeof(TEEC_UUID), id, id_len));
}

static TEEC_Result CommitPoUpdate(TEEC_UUID *uuid,
                                  const uint8_t *id,
                                  uint32_t id_len) {
  return TeecResultFromMbFs(FsPoCommitUpdate((const uint8_t *)uuid,
                                             sizeof(TEEC_UUID), id, id_len));
}

static TEEC_Result GetPoList(TEEC_UUID *uuid,
                             uint8_t *data, uint32_t *data_len) {
  return TeecResultFromMbFs(FsPoList((const uint8_t *)uuid,
                                     sizeof(TEEC_UUID),
                                     data, data_len));
}

static TEEC_Result GetCancellationFlag(U64 *teec_oper,
                                       uint8_t *out,
                                       uint32_t *out_len) {
  TEEC_Result res = TEEC_SUCCESS;

  if (!teec_oper || !out_len || !*out_len || !out) {
    return TEEC_ERROR_BAD_PARAMETERS;
  }
  TEEC_Operation *oper = NULL;
  U64ToPtr(teec_oper,(void **)&oper);
  *out = IsOperationInCRList(oper);
  *out_len = 1;
  return res;
}

TEEC_Result MbHandleSWdCommand(uint32_t command_id,
                              TEEC_UUID *owner_uuid,
                              const uint8_t *po_id,
                              uint32_t po_id_len,
                              void *data,
                              uint32_t *data_len,
                              U64 *teec_oper) {
  TEEC_Result result;

  switch (command_id) {
    case PROTOCOL_COMMAND_READF: {
      result = ReadPersistentObjectFromFS(owner_uuid, po_id,
                                          po_id_len, data, data_len);
      break;
    }
    case PROTOCOL_COMMAND_READMD: {
      result = ReadPoMetadataFromFS(owner_uuid, po_id,
                                    po_id_len, data, data_len);
      break;
    }
    case PROTOCOL_COMMAND_READATTR: {
      result = ReadPoAttributesFromFS(owner_uuid, po_id,
                                      po_id_len, data, data_len);
      break;
    }
    case PROTOCOL_COMMAND_WRITEF: {
      result = WritePersistentObjectToFS(owner_uuid, po_id,
                                         po_id_len, data, *data_len);
      break;
    }
    case PROTOCOL_COMMAND_WRITEMD: {
      result = WritePoMetadataToFS(owner_uuid, po_id,
                                   po_id_len, data, *data_len);
      break;
    }
    case PROTOCOL_COMMAND_WRITEATTR: {
      result = WritePoAttributesToFS(owner_uuid, po_id,
                                     po_id_len, data, *data_len);
      break;
    }
    case PROTOCOL_COMMAND_DELETE: {
      result = DeletePersistentObjectFromFS(owner_uuid, po_id, po_id_len);
      break;
    }
    case PROTOCOL_COMMAND_INIT_UPDATE: {
      result = InitPoUpdate(owner_uuid, po_id, po_id_len);
      break;
    }
    case PROTOCOL_COMMAND_ABORT_UPDATE: {
      result = AbortPoUpdate(owner_uuid, po_id, po_id_len);
      break;
    }
    case PROTOCOL_COMMAND_COMMIT_UPDATE: {
      result = CommitPoUpdate(owner_uuid, po_id, po_id_len);
      break;
    }
    case PROTOCOL_COMMAND_LIST: {
      result = GetPoList(owner_uuid, data, data_len);
      break;
    }
    case PROTOCOL_COMMAND_GETREETIME: {
      result = GetReeTime(data, *data_len);
      break;
    }
    case PROTOCOL_COMMAND_GETCANCELFLAG: {
      result = GetCancellationFlag(teec_oper,data,data_len);
      break;
    }
    default: {
      MB_LOGW("Invalid PO command %x!\n", command_id);
      result = TEEC_ERROR_NOT_SUPPORTED;
      break;
    }
  }

  return result;
}
