/*      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 "ops-env.h"
#include "util.h"
#include "segment.h"
#include "parse.h"
#include "curly.h"
#include "key.h"
#include "sink.h"
#include "digest.h"

#include "util/ting.h"
#include "util/hash.h"


static   mcxHash*    envTable_g        =  NULL;    /* environment keys  */


mcxstatus yamEnvNew
(  const char* tag
,  const char* openstr
,  const char* closestr
)
   {  mcxTing*  opentag    =  mcxTingNew(tag)
   ;  mcxTing*  closetag   =  mcxTingNew(tag)
   ;  mcxKV*   kv
   ;  mcxbool ok           =  FALSE

   ;  mcxTingAppend(closetag, "!_")

  /*
   *  envStack contains mcxTing key member, which is also stored
   *  in the envTable_g. First thought I could not redefine it,
   *  right now I think it is possible, because overwriting key
   *  does not change the key pointer.
  */
   ;  while(1)
      {  yamStack* envStack = currentEnvStack()
      ;  if (envStack->sp->prev)
         {  yamErr
            ("\\env#3"
            ,  "(re)define not allowed within environment\n"
               "    now within environment <%s>"
            ,  ((mcxTing*) envStack->sp->val)->str
            )
         ;  break
      ;  }

         kv =  mcxHashSearch(opentag, envTable_g, MCX_DATUM_INSERT)
      ;  if (kv->key != opentag)
         {  yamErr("\\env#3", "overwriting key <%s>",opentag->str)
         ;  mcxTingFree(&opentag)
      ;  }
         if (kv->val)
         mcxTingWrite((mcxTing*) (kv->val), openstr)
      ;  else
         kv->val = mcxTingNew(openstr)

      ;  kv =  mcxHashSearch(closetag, envTable_g, MCX_DATUM_INSERT)
      ;  if (kv->key != closetag)
         mcxTingFree(&closetag)

      ;  if (kv->val)
         mcxTingWrite((mcxTing*) (kv->val), closestr)
      ;  else
         kv->val = mcxTingNew(closestr)

      ;  ok = TRUE
      ;  break
   ;  }

      if (!ok)
      {  mcxTingFree(&opentag)
      ;  mcxTingFree(&closetag)
      ;  return STATUS_FAIL
   ;  }

      return STATUS_OK
;  }


const char* yamEnvOpen
(  const char* label_
,  const char* data_
,  yamSeg*  seg
)
   {  mcxTing* label = mcxTingNew(label_)
   ;  mcxTing* data = data_ ? mcxTingNew(data_) : NULL
   ;  const char* val = NULL
   ;  mcxKV*  kv = mcxHashSearch(label, envTable_g, MCX_DATUM_FIND)
   ;  mcxTing* dollar = mcxTingEmpty(NULL, 30)

   ;  while (1)
      {  yamStack* envStack = currentEnvStack()
      ;  if (data && data->len)
         {  int x, n_args = 0
         ;  yamSeg*  argseg
         ;  if (yamDigest(data, data, seg))
            {  yamErr
               ("\\begin#1", "arguments in env <%s> did not parse", label->str)
            ;  break
         ;  }
            argseg = yamStackPushTmp(data)

         ;  if (data->str[0] == ':')
            {  data->str[0] = ' '
            ;  while ((x = yamParseScopes(argseg, 1, 0)) == 1)
               {  n_args++
               ;  mcxTingPrint(dollar, "$%d", n_args)
               ;  yamKeyDef(dollar->str, arg1_g->str)
            ;  }
            }
            else
            while ((x = yamParseScopes(argseg, 2, 0)) == 2)
            {  n_args++
            ;  mcxTingPrint(dollar, "$%s", arg1_g->str)
            ;  yamKeyDef(dollar->str, arg2_g->str)
         ;  }

            yamStackFreeTmp(&argseg)
         ;  mcxTingPrint(dollar, "%d", n_args)
         ;  yamKeyDef("$0", dollar->str)
      ;  }
         else
         yamKeyDef("$0", "0")

      ;  if (kv)
         {  mcxTing* ting = (mcxTing*) kv->key
         ;  ting_stack_push(envStack, ting->str, ting->len)
         ;  val = ((mcxTing*) kv->val)->str
         ;  break
      ;  }
         else
         yamErr("\\begin#1", "env <%s> does not exist", label->str)

      ;  break
   ;  }

      mcxTingFree(&label)
   ;  mcxTingFree(&data)
   ;  mcxTingFree(&dollar)
   ;  return val
;  }


mcxstatus yamEnvClose
(  const char* label_
)
   {  mcxTing* label = mcxTingNew(label_)
   ;  const char* check = NULL
   ;  yamStack* envStack = currentEnvStack()

   ;  if (!(check = ting_stack_pop(envStack)))
      {  yamErr
         (  "\\end#1"
         ,  "close request for <%s> scope while at bottom"
         ,  label->str
         )
      ;  mcxTingFree(&label)
      ;  return STATUS_FAIL
   ;  }

      if (strcmp(label->str, check))
      {  yamErr
         (  "\\end#1"
         ,  "close request for <%s> scope while inner scope <%s> still open"
         ,  label->str
         ,  check
         )
      ;  mcxTingFree(&label)
      ;  return STATUS_FAIL
   ;  }

      mcxTingFree(&label)
   ;  return STATUS_OK
;  }


const char* yamEnvEnd
(  const char* label_
,  yamSeg*  seg
)
   {  mcxTing* label =  mcxTingPrint(NULL, "%s!_", label_)
   ;  mcxKV* kv      =  mcxHashSearch(label, envTable_g, MCX_DATUM_FIND)
   ;  mcxTingFree(&label)  
   ;  return kv ? ((mcxTing*) kv->val)->str : NULL
;  }


void mod_env_init
(  int n
)
   {  envTable_g = yamHashNew(n)
;  }


void mod_env_exit
(  void
)
   {  mcxHashFree(&envTable_g, mcxTingFree_v, mcxTingFree_v)
;  }


