/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2023 Martin Preuss, all rights reserved.
 *
 * The license for this file can be found in the file COPYING which you
 * should have received along with this file.
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif


#include "./u_objects_p.h"
#include "./u_base.h"
#include "./aqhomehttp.h"
#include "aqhome/http/httpservice_http.h"

#include <gwenhywfar/gwenhywfar.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/i18n.h>


GWEN_INHERIT(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS);



/* ------------------------------------------------------------------------------------------------
 * forward declarations
 * ------------------------------------------------------------------------------------------------
 */

static void GWENHYWFAR_CB _freeData(void *bp, void *p);
static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
static GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
static GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);

static void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);

static void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf);
static void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf);
static GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq);
static GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id);
static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
static int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf);
static GWEN_MSG *_addOrEditAndCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb);
static int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id);
static GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id);
static void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf);



/* ------------------------------------------------------------------------------------------------
 * implementations
 * ------------------------------------------------------------------------------------------------
 */

AQH_HTTP_URLHANDLER *AQH_ObjectsHttpUrlHandler_new(AQH_SERVICE *sv,
						   uint32_t neededPermsList,
						   uint32_t neededPermsAdd,
						   uint32_t neededPermsDel,
						   uint32_t neededPermsEdit,
						   const char *urlForObjectList)
{
  AQH_HTTP_URLHANDLER *uh;
  AQH_URLHANDLER_OBJECTS *xuh;

  uh=AQH_HttpUrlHandler_new(sv);
  GWEN_NEW_OBJECT(AQH_URLHANDLER_OBJECTS, xuh);
  GWEN_INHERIT_SETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh, xuh, _freeData);

  AQH_HttpUrlHandler_SetHandleFn(uh, _handleUrl);

  xuh->neededPermsList=neededPermsList;
  xuh->neededPermsAdd=neededPermsAdd;
  xuh->neededPermsDel=neededPermsDel;
  xuh->neededPermsEdit=neededPermsEdit;
  xuh->urlForObjectList=urlForObjectList?strdup(urlForObjectList):NULL;

  return uh;
}



void _freeData(void *bp, void *p)
{
  AQH_URLHANDLER_OBJECTS *xuh;

  xuh=(AQH_URLHANDLER_OBJECTS*)p;
  free(xuh->urlForObjectList);
  GWEN_FREE_OBJECT(xuh);
}



void AQH_ObjectsHttpUrlHandler_SetAddOrEditObjectFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_ADDOREDITOBJECT_FN fn)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh) {
    xuh->addOrEditObjectFn=fn;
  }
}



void AQH_ObjectsHttpUrlHandler_SetFindObjectByIdAndReturnAsDbFn(AQH_HTTP_URLHANDLER *uh,
								AQH_OBJECTSHTTPURLHANDLER_FINDOBJECTBYIDANDRETURNASDB_FN fn)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh) {
    xuh->findObjectByIdAndReturnAsDbFn=fn;
  }
}



void AQH_ObjectsHttpUrlHandler_SetWriteAddPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEADDPAGE_FN fn)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh) {
    xuh->writeAddPageFn=fn;
  }
}



void AQH_ObjectsHttpUrlHandler_SetWriteEditPageFn(AQH_HTTP_URLHANDLER *uh, AQH_OBJECTSHTTPURLHANDLER_WRITEEDITPAGE_FN fn)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh) {
    xuh->writeEditPageFn=fn;
  }
}



void AQH_ObjectsHttpUrlHandler_SetListObjectsIntoBufferFn(AQH_HTTP_URLHANDLER *uh,
							  AQH_OBJECTSHTTPURLHANDLER_LISTOBJECTSINTOBUFFER_FN fn)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh) {
    xuh->listObjectsIntoBufferFn=fn;
  }
}




int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
  if (GWEN_INHERIT_ISOFTYPE(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh)) {
    const char *protocol;
    const char *cmd;
    GWEN_MSG *msgOut;

    AQH_HttpService_SetupModuleAndPerms(AQH_HttpUrlHandler_GetHttpService(uh), rq, "aqhome");
    protocol=AQH_HttpRequest_GetProtocol(rq);
  
    AQH_HttpRequest_SetupUrlPathMembers(rq);
    if (AQH_HttpRequest_GetUrlPathMembers(rq)==NULL) {
      msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
      AQH_HttpRequest_SetResponseMsg(rq, msgOut);
      return 0;
    }
  
    cmd=AQH_HttpRequest_GetCommand(rq);
    if (cmd && *cmd) {
      if (strcasecmp(cmd, "GET")==0)
	msgOut=_handleGet(uh, rq);
      else if (strcasecmp(cmd, "POST")==0)
	msgOut=_handlePost(uh, rq);
      else {
	msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL);
      }
      AQH_HttpRequest_SetResponseMsg(rq, msgOut);
      return 0;
    }
    else {
      DBG_ERROR(AQH_LOGDOMAIN, "No command in request");
      return GWEN_ERROR_INVALID;
    }
  }
  else {
    DBG_ERROR(NULL, "Not an AQH_URLHANDLER_OBJECTS object");
    return GWEN_ERROR_INTERNAL;
  }
}



GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
  GWEN_BUFFER *pageBuf;
  int rv;
  GWEN_MSG *msgOut=NULL;
  const char *protocol;
  const GWEN_STRINGLIST *sl;
  const char *s;

  protocol=AQH_HttpRequest_GetProtocol(rq);
  pageBuf=GWEN_Buffer_new(0, 2048, 0, 1);
  /* header */
  rv=AQH_HttpUrlHandler_AddContentHeaders(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers");
    GWEN_Buffer_free(pageBuf);
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
  }

  /* middle part (header - middle - footer) */
  sl=AQH_HttpRequest_GetUrlPathMembers(rq);

  s=GWEN_StringList_StringAt(sl, 1);
  if (!(s && *s))
    s="list";

  if (strcasecmp(s, "list")==0)
    _handleGetList(uh, rq, pageBuf);
  else if (strcasecmp(s, "add")==0)
    _handleGetAdd(uh, rq, pageBuf);
  else if (strcasecmp(s, "edit")==0)
    _handleGetEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0), pageBuf);
  else {
    DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
    GWEN_Buffer_free(pageBuf);
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
  }

  /* footer */
  rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf);
  if (rv<0) {
    DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers");
    GWEN_Buffer_free(pageBuf);
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL);
  }

  msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf));
  GWEN_Buffer_free(pageBuf);
  return msgOut;
}



GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
  GWEN_DB_NODE *db;
  const GWEN_STRINGLIST *sl;
  const char *protocol;
  const char *s;

  DBG_ERROR(NULL, "POST:");
  db=AQH_HttpRequest_GetDbPostBody(rq);
  GWEN_DB_Dump(db, 2);

  protocol=AQH_HttpRequest_GetProtocol(rq);
  sl=AQH_HttpRequest_GetUrlPathMembers(rq);

  s=GWEN_StringList_StringAt(sl, 1);
  if (s && *s) {
    if (strcasecmp(s, "add")==0)
      return _handlePostAdd(uh, rq);
    else if (strcasecmp(s, "edit")==0)
      return _handlePostEdit(uh, rq, GWEN_StringList_StringAsIntAt(sl, 2, 0));
    else {
      DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s);
      return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
    }
  }
  else {
    DBG_ERROR(NULL, "Invalid url (2nd member missing)");
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL);
  }
}



void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  uint32_t perms;

  DBG_ERROR(NULL, "LIST");
  perms=AQH_HttpRequest_GetModulePerms(rq);
  if (perms & xuh->neededPermsList) {
    _listObjectsIntoBuffer(uh, pageBuf);
  }
  else {
    GWEN_Buffer_AppendArgs(pageBuf, "<p>%s</p>", I18N("No permissions to see list of objects."));
  }
}



void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  uint32_t perms;

  DBG_ERROR(NULL, "ADD");
  perms=AQH_HttpRequest_GetModulePerms(rq);
  if (perms & xuh->neededPermsAdd) {
    _writeAddPage(uh, rq, NULL, pageBuf);
  }
  else {
    DBG_INFO(NULL, "No permissions to add.");
    GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("No permissions to add an object."));
  }
}



void _handleGetEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  uint32_t perms;

  DBG_ERROR(NULL, "EDIT");
  perms=AQH_HttpRequest_GetModulePerms(rq);
  if (perms & xuh->neededPermsEdit) {
    if (id>0) {
      GWEN_DB_NODE *db;

      db=_findObjectByIdAndReturnAsDb(uh, id);
      if (db) {
	_writeEditPage(uh, rq, db, pageBuf);
	GWEN_DB_Group_free(db);
      }
      else {
	DBG_ERROR(NULL, "Object %d not found", id);
	GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("Object not found."));
      }
    }
    else {
      DBG_ERROR(NULL, "Missing object id");
      GWEN_Buffer_AppendArgs(pageBuf, "<p><font color=\"red\">%s</font></p>", I18N("Missing or invalid object id."));
    }
  }
  else {
    DBG_INFO(NULL, "No permissions to edit.");
    GWEN_Buffer_AppendArgs(pageBuf, "<p>%s</p>", I18N("No permissions to edit."));
  }
}



GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  uint32_t perms;

  perms=AQH_HttpRequest_GetModulePerms(rq);
  if (perms & xuh->neededPermsAdd)
    return _addOrEditAndCreateResponse(uh, rq, 0, _writeAddPage);
  else {
    DBG_INFO(NULL, "No perms to add object");
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 403, "Forbidden",
                                             AQH_HttpRequest_GetProtocol(rq), NULL);
  }
}



GWEN_MSG *_handlePostEdit(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  uint32_t perms;

  perms=AQH_HttpRequest_GetModulePerms(rq);
  if (perms & xuh->neededPermsEdit) {
    return _addOrEditAndCreateResponse(uh, rq, id, _writeEditPage);
  }
  else {
    DBG_INFO(NULL, "No perms to edit");
    return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 403, "Forbidden",
                                             AQH_HttpRequest_GetProtocol(rq), NULL);
  }
}



GWEN_MSG *_addOrEditAndCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, int id, AQH_HTTP_URLHANDLER_WRITEPAGE_CB cb)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);
  AQH_SERVICE *sv;
  GWEN_DB_NODE *db;
  int rv;

  sv=AQH_HttpUrlHandler_GetHttpService(uh);
  db=AQH_HttpRequest_GetDbPostBody(rq);
  rv=_addOrEditObject(uh, db, id);
  if (rv<0)
    return AQH_BaseHttpUrlHandler_CreateResponseForErrorCode(uh, rq, rv, cb, db);
  return AQH_HttpService_CreateRedirectingResponseMsg(sv, AQH_HttpRequest_GetProtocol(rq), xuh->urlForObjectList);
}



int _addOrEditObject(AQH_HTTP_URLHANDLER *uh, GWEN_DB_NODE *db, int id)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh->addOrEditObjectFn)
    return xuh->addOrEditObjectFn(uh, db, id);
  return GWEN_ERROR_NOT_IMPLEMENTED;
}



GWEN_DB_NODE *_findObjectByIdAndReturnAsDb(AQH_HTTP_URLHANDLER *uh, int id)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh->findObjectByIdAndReturnAsDbFn)
    return xuh->findObjectByIdAndReturnAsDbFn(uh, id);
  return NULL;
}



int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh->writeAddPageFn)
    return xuh->writeAddPageFn(uh, rq, dbValues, pageBuf);
  return GWEN_ERROR_NOT_IMPLEMENTED;
}



int _writeEditPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh->writeEditPageFn)
    return xuh->writeEditPageFn(uh, rq, dbValues, pageBuf);
  return GWEN_ERROR_NOT_IMPLEMENTED;
}



void _listObjectsIntoBuffer(AQH_HTTP_URLHANDLER *uh, GWEN_BUFFER *pageBuf)
{
  AQH_URLHANDLER_OBJECTS *xuh=GWEN_INHERIT_GETDATA(AQH_HTTP_URLHANDLER, AQH_URLHANDLER_OBJECTS, uh);

  if (xuh->listObjectsIntoBufferFn)
    xuh->listObjectsIntoBufferFn(uh, pageBuf);
}





