/*      Copyright (C) 2001, 2002, 2003, 2004 Stijn van Dongen
 *
 * This file is part of Zoem. You can redistribute and/or modify Zoem under the
 * terms of the GNU General Public License;  either version 2 of the License or
 * (at your option) any later  version.  You should have received a copy of the
 * GPL along with Zoem, in the file COPYING.
*/

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "filter.h"
#include "util.h"
#include "entry.h"
#include "key.h"
#include "source.h"
#include "read.h"
#include "digest.h"

#include "util/hash.h"
#include "util/ting.h"
#include "util/types.h"
#include "util/io.h"
#include "util/minmax.h"


typedef struct
{  mcxTing*          fname
;  const mcxTing*    txt
;  int               linect
;
}  inputHook         ;


#define MAX_FILES_NEST 10

inputHook inputHookDir[MAX_FILES_NEST]
=   
{  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
,  {  NULL, NULL, 1 }
}  ;

#define hd inputHookDir

static int        hdidx_g        =  -1;
static mcxTing*   fntxt_g        =  NULL;


void mod_source_init
(  int   n
)
   {  fntxt_g = mcxTingNew("__fnin__")
   ;  yamKeyInsert(fntxt_g, "_nil_")
;  }


void mod_source_exit
(  void
)
   {  int i
   ;  for (i=0;i<MAX_FILES_NEST;i++)
      {  mcxTing* t = hd[i].fname
      ;  if (t)
         mcxTingFree(&t)
   ;  }
;  }


mcxstatus sourcePop
(  void
)
   {  hdidx_g--

   ;  if (hdidx_g >= 0)
      yamKeyInsert(fntxt_g, hd[hdidx_g].fname->str)

   ;  return STATUS_OK
;  }


mcxstatus sourcePush
(  const char*       str
,  const mcxTing*    txt
)
   {  hdidx_g++

   ;  if (hdidx_g >= MAX_FILES_NEST && hdidx_g--)
      return STATUS_FAIL

   ;  hd[hdidx_g].linect   =  1
   ;  hd[hdidx_g].txt      =  txt

   ;  if (hd[hdidx_g].fname)
      mcxTingWrite(hd[hdidx_g].fname, str)
   ;  else
      hd[hdidx_g].fname    =   mcxTingNew(str)

   ;  yamKeyInsert(fntxt_g, str)

   ;  return STATUS_OK
;  }


mcxbool sourceCanPush
(  void
)  {  return hdidx_g+1 < MAX_FILES_NEST ? TRUE : FALSE
;  }


int sourceGetLc
(  void
)
   {  return hd[hdidx_g].linect
;  }


const char* sourceGetName
(  void
)
   {  return hdidx_g >= 0 ? hd[hdidx_g].fname->str : ""
;  }


mcxTing* sourceGetPath
(  void
)
   {  char* s
   ;  char* a = hdidx_g < 0 ? NULL : hd[hdidx_g].fname->str
   ;  if (!a)
      return NULL
   ;  if (!(s = strrchr(a, '/')))
      return NULL
   ;  return mcxTingNNew(a, s-a+1)
;  }


void sourceIncrLc
(  const mcxTing* txt
,  int d
)
   {  if (!txt || hd[hdidx_g].txt == txt)
      hd[hdidx_g].linect += d
   /* case !txt is used for inline file ___jump_lc___ hack */
;  }


void enter_interactive
(  void
)
   {  mcxTing* txt = mcxTingNew("\\zinsert{stdia}")
   ;  if (0)
      yamOutput(txt, NULL, ZOEM_FILTER_DEFAULT)
   ;  else
      yamDigest(txt, txt, NULL)
   ;  mcxTingFree(&txt)
;  }


mcxstatus sourceAscend
(  mcxTing    *fnsearch
,  sink*       sd
,  int         fltidx
,  size_t      chunk_size
,  int         debug
,  int         use_searchpath
,  int         allow_inline
)
   {  size_t   sz       =  0
   ;  mcxTing* filetxt  =   mcxTingEmpty(NULL, 1000)

   ;  const mcxTing* inline_txt = NULL
   ;  const mcxTing** inline_txt_ptr = allow_inline ? &inline_txt : NULL
   ;  mcxIO *xf         =  yamTryOpen(fnsearch, inline_txt_ptr, use_searchpath)
   ;  mcxstatus stat_open =  STATUS_OK
   ;  mcxstatus stat_read =  STATUS_OK
   ;  mcxstatus stat_zoem =  STATUS_OK

   ;  if (inline_txt)
         mcxTingWrite(filetxt, inline_txt->str)
      ,  stat_read = STATUS_DONE
   ;  else if (xf)
      {  struct stat mystat
      ;  if (!xf->stdio)
         {  if (stat(xf->fn->str, &mystat))
            mcxErr("sourceAscend", "can not stat file <%s>", xf->fn->str)
         ;  else if (!chunk_size)
            sz = mystat.st_size
         ;  else if (chunk_size)
            sz = MIN(mystat.st_size, 1.1 * chunk_size)

         ;  if (sz)
            mcxTingEmpty(filetxt, sz)
      ;  }
      }
      else
      stat_open = STATUS_FAIL

   ;  if (stat_open == STATUS_OK)
      {  sourcePush(xf->fn->str, filetxt)

      ;  while(1)
         {  if (!inline_txt)
            {  stat_read = yamReadChunk(xf, filetxt, 0, NULL, chunk_size)
            ;  if (stat_read != STATUS_OK && stat_read != STATUS_DONE)
               break
         ;  }

            if ((stat_zoem = yamOutput(filetxt, sd, fltidx)))
            {  mcxErr("zoem", "unwound on error/exception")
            ;  if (debug)         /* fixme; could also enter debug in caller? */
               enter_interactive()
            ;  else
               break
         ;  }
            
            if (inline_txt || stat_read == STATUS_DONE)
            break

         ;  if (xf && stat_read == STATUS_OK)
            mcxTell("zoem", "read chunk of size <%ld>", (long) filetxt->len)
      ;  }
         sourcePop()
   ;  }

      mcxTingFree(&filetxt)
   ;  mcxIOfree(&xf)

   ;  if (stat_open)
      return STATUS_FAIL_OPEN
   ;  if (stat_read != STATUS_DONE)
      return STATUS_FAIL_READ
   ;  if (stat_zoem)
      return STATUS_FAIL_ZOEM

   ;  return STATUS_OK
;  }


