#include <CrashTick.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dostags.h>

#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include <stdio.h>
#include <stdarg.h>

#ifdef HAS_VARARGS_H
#include <varargs.h>
#endif

#include <shared/typedefs.h>
#include <ctshared/ctconfig.h>

#include <logwrite.h>

extern struct Config config;

#define LOGWRITEERROR_OK         0
#define LOGWRITEERROR_EXITING    1
#define LOGWRITEERROR_OPENFAILED 2

struct logwritemessage
{
   struct Message message;
   char *string;           /* NULL if the logwrite task should be removed */
   char prefix;
   LONG *args;
   short error;            /* Return code from logwrite task              */
};

struct MsgPort *logwritereplyport;

long logwrite_oksignal;
long logwrite_errorsignal;
struct Task *logwrite_task;
struct MsgPort *logwrite_port;

void __saveds logtask(void)
{
   struct DateTime dt;
   UBYTE date[40],time[40];
   struct logwritemessage *lwm;
   short writes;
   BPTR fh;
   struct MsgPort *TimerPort;
   struct timerequest *IOTimer;
   BOOL activetimer;
   ULONG sigs;

   TimerPort = (struct MsgPort *)CreateMsgPort();
   IOTimer   = (struct timerequest *)AllocMem(sizeof(struct timerequest),MEMF_ANY);

   if(!TimerPort || !IOTimer)
   {
      if(TimerPort) DeleteMsgPort(TimerPort);
      if(IOTimer) FreeMem(IOTimer, sizeof(struct timerequest));

      Signal(logwrite_task,1L<<logwrite_errorsignal);
      return;
   }

   IOTimer->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
   IOTimer->tr_node.io_Message.mn_Length       = sizeof(struct timerequest);
   IOTimer->tr_node.io_Message.mn_ReplyPort    = TimerPort;

	if(OpenDevice("timer.device",UNIT_VBLANK,(struct IORequest *)IOTimer,0))
   {
      DeleteMsgPort(TimerPort);
      FreeMem(IOTimer, sizeof(struct timerequest));

      Signal(logwrite_task,1L<<logwrite_errorsignal);
      return;
   }

   if(!(logwrite_port=CreatePort(NULL,0)))
   {
      DeleteMsgPort(TimerPort);
      CloseDevice((struct IORequest *)IOTimer);
      FreeMem(IOTimer, sizeof(struct timerequest));

      Signal(logwrite_task,1L<<logwrite_errorsignal);
      return;
   }

   Signal(logwrite_task,1L<<logwrite_oksignal);

   fh=NULL;
   writes=0;
   activetimer=FALSE;

   for(;;)
   {
      sigs=Wait((1L<<logwrite_port->mp_SigBit) | (1L<<TimerPort->mp_SigBit));

      if(sigs & (1L<<TimerPort->mp_SigBit))
      {
         activetimer=FALSE;
         if(fh) Close(fh);
         fh=NULL;
      }

      while(lwm=(struct logwritemessage *)GetMsg(logwrite_port))
      {
         if(!lwm->string) /* Time to terminate the task */
         {
            if(fh) Close(fh);

            if(activetimer)
            {
               AbortIO((struct IORequest *)IOTimer);
               WaitIO((struct IORequest *)IOTimer);
            }

            Forbid(); /* Multitasking enabled again when this task quits... */

            DeleteMsgPort(TimerPort);
            CloseDevice((struct IORequest *)IOTimer);
            FreeMem(IOTimer, sizeof(struct timerequest));
            ReplyMsg((struct Message *)lwm);
            DeletePort(logwrite_port);

            return;
         }
         else
         {
            if(!fh)
            {
               if(fh=Open(config.cfg_Logfile,MODE_READWRITE))
               {
                  writes=0;

                  if(!activetimer && config.cfg_LogBufferSecs!=0)
                  {
                     activetimer=TRUE;

                     IOTimer->tr_time.tv_secs=config.cfg_LogBufferSecs;
                     IOTimer->tr_time.tv_micro=0;
                     IOTimer->tr_node.io_Command=TR_ADDREQUEST;
                     SendIO((struct IORequest *)IOTimer);
                  }

                  Seek(fh,0,OFFSET_END);
               }
            }

            if(fh)
            {
               DateStamp(&dt.dat_Stamp);
               dt.dat_Format=FORMAT_DOS;
               dt.dat_Flags=0;
               dt.dat_StrDay=NULL;
               dt.dat_StrDate=date;
               dt.dat_StrTime=time;
               DateToStr(&dt);

               FPrintf(fh,"%lc %s %s ",lwm->prefix,date,time);
               VFPrintf(fh,lwm->string,lwm->args);
               FPutC(fh,'\n');

               lwm->error=LOGWRITEERROR_OK;

               writes++;

               if(writes >= config.cfg_LogBufferLines)
               {
                  if(activetimer)
                  {
                     AbortIO((struct IORequest *)IOTimer);
                     WaitIO((struct IORequest *)IOTimer);
                     activetimer=FALSE;
                  }

                  Close(fh);
                  fh=NULL;
               }
            }
            else
            {
               lwm->error=LOGWRITEERROR_OPENFAILED;
            }

            ReplyMsg((struct Message *)lwm);
         }
      }
   }
}

uchar taskname[200];

short InitLogWrite()
{
   struct Process *newtask;
   unsigned long sigs;
   short kg;

   if(!(logwritereplyport=CreatePort(NULL,0)))
   {
      return(FALSE);
   }
      
   logwrite_task=FindTask(NULL);

   logwrite_oksignal=AllocSignal(-1);
   logwrite_errorsignal=AllocSignal(-1);

   if(logwrite_oksignal == -1 || logwrite_errorsignal == -1)
   {
      if(logwrite_oksignal != -1) FreeSignal(logwrite_oksignal);
      if(logwrite_errorsignal != -1) FreeSignal(logwrite_errorsignal);

      DeletePort(logwritereplyport);
      return(FALSE);
   }

   sprintf(taskname,"%s_Logwritetask",FilePart(config.cfg_Logfile));
   newtask=CreateNewProcTags(NP_Entry,logtask,NP_Name,taskname,TAG_END);

   if(!newtask)
   {
      FreeSignal(logwrite_oksignal);
      FreeSignal(logwrite_errorsignal);
      DeletePort(logwritereplyport);
      return(FALSE);
   }

   kg=TRUE;

   while(kg)
   {
      sigs=Wait((1L<<logwrite_oksignal) | (1L<<logwrite_errorsignal));

      if(sigs & (1L<<logwrite_errorsignal))
      {
         FreeSignal(logwrite_oksignal);
         FreeSignal(logwrite_errorsignal);
         DeletePort(logwritereplyport);
         return(FALSE);
      }
      if(sigs & (1L<<logwrite_oksignal))
      {
         kg=FALSE;
      }
   }

   FreeSignal(logwrite_oksignal);
   FreeSignal(logwrite_errorsignal);

   return(TRUE);
}

void CloseLogWrite(void)
{
   struct logwritemessage *lwm;

   if(!(lwm=AllocMem(sizeof(struct logwritemessage),MEMF_PUBLIC|MEMF_CLEAR)))
   {
      printf("Couldn't write to logfile. Out of memory!\n");
      return;
   }

   lwm->message.mn_ReplyPort=logwritereplyport;
   lwm->message.mn_Length=sizeof(struct logwritemessage);
   lwm->string=NULL;

   Forbid();

   if(logwrite_port)
   {
      PutMsg(logwrite_port,(struct Message *)lwm);
      WaitPort(logwritereplyport);
      GetMsg(logwritereplyport);
   }

   FreeMem(lwm,sizeof(struct logwritemessage));
   Permit();

   DeletePort(logwritereplyport);
   logwrite_port=NULL;
}

uchar *categoryletters="-%=!/D+^?";

void LogWrite(ulong level,ulong category,uchar *fmt,...)
{
   struct logwritemessage *lwm;
   va_list args;

   if(level > config.cfg_Loglevel)
      return;

   if(level == 0)
      LogWrite(6,DEBUG,"*** Warning: Loglevel is 0!!! ***");

   if(fmt[0]==0)
   {
      printf("\n");
      return;
   }

   if(!(lwm=AllocMem(sizeof(struct logwritemessage),MEMF_PUBLIC|MEMF_CLEAR)))
   {
      printf("Couldn't write to logfile. Out of memory!\n");
      return;
   }

   va_start(args, fmt);

   VFPrintf(Output(),fmt,args);
   Printf("\n");

   lwm->message.mn_ReplyPort=logwritereplyport;
   lwm->message.mn_Length=sizeof(struct logwritemessage);
   lwm->string=fmt;
   lwm->prefix=categoryletters[category];
   lwm->args=(LONG *)args;

   Forbid();

   if(logwrite_port)
   {
      PutMsg(logwrite_port,(struct Message *)lwm);
      WaitPort(logwritereplyport);
      GetMsg(logwritereplyport);
   }

   Permit();

   va_end(args);

   if(lwm->error == LOGWRITEERROR_OPENFAILED)
      printf("%s\n",getString(ERR_LOGWRITETASK_FAILED));
   
   FreeMem(lwm,sizeof(struct logwritemessage));
}

