/*
 * Copyright 2012 The Weather Channel
 * Copyright 2013 Reza Naghibi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */


#include "dtree_client.h"


static long dtree_print_node(const dtree_dt_index*,const char*(*f)(void*),const dtree_dt_node*,char*,int);
extern int dtree_node_depth(const dtree_dt_index*,const dtree_dt_node*);
char *dtree_node_path(const dtree_dt_index*,const dtree_dt_node*,char*);
void dtree_timersubn(struct timespec*,struct timespec*,struct timespec*);
int dtree_gettime(struct timespec*);


//finds a certain flag among dup nodes
const dtree_dt_node *dtree_get_flag(const dtree_dt_index *h,const dtree_dt_node *n,dtree_flag_f flag,dtree_pos_f pos)
{
    packed_ptr pp=n->curr;
    
    while(pp)
    {
        if(n->flags & flag && (!n->pos || !pos || n->pos==pos))
            return n;
        
        pp=n->dup;
        n=DTREE_DT_GETPP(h,pp);
    }
    
    return NULL;
}

//finds a certain flag among dup nodes
dtree_flag_f dtree_get_flags(const dtree_dt_index *h,const dtree_dt_node *n,dtree_pos_f pos)
{
    dtree_flag_f f;
    packed_ptr pp;
    
    if(n)
    {
        f=0;
        pp=n->dup;

        if(!n->pos || !pos || n->pos==pos)
            f=n->flags;
        
        if(pp)
            return f | dtree_get_flags(h,DTREE_DT_GETPP(h,pp),pos);
        else
            return f;
    }
    else
        return 0;
}

//gets the depth of the current node
int dtree_node_depth(const dtree_dt_index *h,const dtree_dt_node *n)
{
    int d=0;
    packed_ptr pp;
    
    pp=n->curr;
    
    while(pp && n)
    {
        d++;
        pp=n->prev;
        n=DTREE_DT_GETPP(h,pp);
    }
    
    return d-1;
}

//gets the pattern path of a node
char *dtree_node_path(const dtree_dt_index *h,const dtree_dt_node *n,char *buf)
{
    int d;
    packed_ptr pp;
    
    d=dtree_node_depth(h,n);
    
    buf[d]='\0';
    pp=n->curr;
    
    while(pp && n && d>0)
    {
        d--;
        buf[d]=n->data;
        pp=n->prev;
        n=DTREE_DT_GETPP(h,pp);
    }
    
    return buf;
}

//walks the dtree
long dtree_print(const dtree_dt_index *h,const char* (*f)(void*))
{
    unsigned int i;
    char buf[DTREE_DATA_BUFLEN];
    long tot=0;
    
    memset(buf,0,sizeof(buf));
    
    for(i=0;h->head && i<DTREE_HASH_NCOUNT;i++)
        tot+=dtree_print_node(h,f,DTREE_DT_GETPP(h,h->head->nodes[i]),buf,0);
    
    dtree_printd(DTREE_PRINT_INITDTREE,"WALK: Walked %ld tokens\n",tot);
    
    return tot;
}

//walks the dtree_node
static long dtree_print_node(const dtree_dt_index *h,const char* (*f)(void*),const dtree_dt_node *n,char *path,int depth)
{
    unsigned int i;
    char buf[DTREE_DATA_BUFLEN];
    const char *s;
    long tot=0;
    packed_ptr pp;
    const dtree_dt_node *dupn;
    
    if(!n || !n->curr || depth>(DTREE_DATA_BUFLEN-1))
        return 0;
    
    path[depth]=n->data;
    path[depth+1]='\0';
    
    if(n->flags & DTREE_DT_FLAG_TOKEN)
    {
        s="";
        
        if(f)
            s=f(n->payload);
        
        dtree_printd(DTREE_PRINT_INITDTREE,"PRINT: found token: '%s' flags: %d pdata: '%s' p(%p) cparam(%p)\n",
                 path,n->flags,s,n->payload,n->cparam);
        
        tot++;
        
        dtree_node_path(h,n,buf);
        
        if(strcmp(path,buf))
            dtree_printd(DTREE_PRINT_INITDTREE,"PRINT: ERROR path mismatch: '%s' != '%s'\n",path,buf);
    }
    
    pp=n->dup;
    dupn=DTREE_DT_GETPP(h,pp);
    
    while(pp)
    {
        s="";
        
        if(f)
            s=f(dupn->payload);
        
        dtree_printd(DTREE_PRINT_INITDTREE,"PRINT: found token: '%s' flags: %d pdata: '%s' p(%p) cparam(%p)\n",
                 path,dupn->flags,s,dupn->payload,dupn->cparam);

        tot++;
        
        pp=dupn->dup;
        dupn=DTREE_DT_GETPP(h,pp);
    }
    
    for(i=0;i<DTREE_HASH_NCOUNT;i++)
    {
        if(n->nodes[i])
            tot+=dtree_print_node(h,f,DTREE_DT_GETPP(h,n->nodes[i]),path,depth+1);
    }
    
    path[depth]='\0';
    
    return tot;
}

//debug printf
void dtree_printd(int level,const char* fmt,...)
{
#if DTREE_DEBUG_LOGGING
    va_list ap;
    
    if(level & DTREE_PRINT_ENABLED)
    {
        va_start(ap,fmt);
    
        vprintf(fmt,ap);
    
        va_end(ap);
    }
#endif
}

//subtracts timers
void dtree_timersubn(struct timespec *end,struct timespec *start,struct timespec *r)
{
    r->tv_sec=end->tv_sec-start->tv_sec;
    r->tv_nsec=end->tv_nsec-start->tv_nsec;
    
    if(r->tv_nsec<0)
    {
      r->tv_sec--;
      r->tv_nsec+=(1000*1000*1000);
    }
}

//get timestamp
int dtree_gettime(struct timespec *ts)
{
#if defined(_DTREE_NO_TIMESPEC) || defined(__MACH__)
    ts->tv_sec=0;
    ts->tv_nsec=0;
    return 0;
#else
    return clock_gettime(CLOCK_REALTIME,ts);
#endif
}

