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



#include <unistd.h>
#include <termios.h>

#define _GNU_SOURCE
#include <dlfcn.h>

#include <signal.h>

#include <stdarg.h>
#include <execinfo.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


#include "osp.h"



#define IOSIZE (32*1024*1024)
void *g_SemDeadFunc;

int32_t osp_signal_init(void);

uint32_t *gOspPrintLevel = NULL;


/*keep DbgOut with order !*/
void *g_DbgOutSem = 0;

char DbgBuf[MAX_DBGINFO_LEN] = {0};
static OSP_FUNCPTR gpfunc[DeadFuncNum] = {0};



int32_t osp_task_dbg_out_enable(uint32_t taskid)
{
	gOspTask[taskid].dbg_out_is_enable = 1;

    return 0;
       
}

int32_t osp_task_dbg_out_disable(uint32_t taskid)
{
    gOspTask[taskid].dbg_out_is_enable = 0;
	return 0;
}

int32_t osp_task_dbg_out_enable_all(void)
{
    int32_t i;

	for (i = 0; i < OSP_MAX_TASK; i++)
	{
		if (gOspTask[i].Active)
		{
            osp_task_dbg_out_enable(i);
		}
	}
    return OSP_OK;
}

int32_t osp_task_dbg_out_disable_all(void)
{
    int32_t i;

	for (i = 0; i < OSP_MAX_TASK; i++)
	{
		if (gOspTask[i].Active)
		{
            osp_task_dbg_out_disable(i);
		}
	}
    return OSP_OK;
}



int32_t osp_task_dbg_is_log(uint32_t taskid)
{
    return  gOspTask[taskid].dbg_out2log_is_enable;
}
int32_t osp_task_dbg_out2log_disable_all(void)
{
    int32_t i;

	for (i = 0; i < OSP_MAX_TASK; i++)
	{
		if (gOspTask[i].Active)
		{
            osp_task_dbg_out2log_disable(i);
		}
	}
    return OSP_OK;

}
int32_t osp_task_dbg_out2log_disable(uint32_t taskid)
{
    gOspTask[taskid].dbg_out2log_is_enable = 0;

    return 0;
       
}
int32_t osp_task_dbg_out2log_enable_all(void)
{
    int32_t i;
    for (i = 0; i < OSP_MAX_TASK; i++)
    {
        if (!osp_task_is_shell(i))
            osp_task_dbg_out2log_enable(i);
    } 
    return OSP_OK;
    
}

int32_t osp_task_dbg_out2log_enable(uint32_t taskid)
{
    gOspTask[taskid].dbg_out2log_is_enable = 1;
    
    return 0;
}

int32_t osp_task_dbg_out_is_enable(uint32_t taskid)
{
   return gOspTask[taskid].dbg_out_is_enable;
}


void osp_local_shell_out_main(Osp_Msg_Head *pMsg)
{


    if (osp_local_shell_task_is_reged())
    {
        osp_print(MSG_HEAD_TO_COMM(pMsg), MSG_DADA_LEN(pMsg));
    }
    
    osp_net_shell_out(pMsg); 
	
    osp_free_msg(pMsg);   
}


void osp_debug_out_set_level(int32_t level)
{
    *gOspPrintLevel = level;
}

    
OSP_STATUS osp_debug_out(int32_t level, char *fmt,...)
{
    va_list vl;
    Osp_Msg_Head *pMsg;
    int32_t buflen = 0;

    if (!osp_is_init_done())
    {
        /*to makesure osp_debug_out can be useful anytime*/
        char *pbuf = malloc(MAX_DBGINFO_LEN);
        if (NULL != pbuf)
        {
            
            va_start(vl, fmt);
            buflen = vsnprintf(pbuf + buflen, MAX_DBGINFO_LEN - buflen, fmt, vl);
            va_end(vl);
            printf("%s\n", pbuf);
            free(pbuf);
        }
        return OSP_OK;
    
    }    

    
    if ((NULL == gOspPrintLevel) && (*gOspPrintLevel < level))
        return (OSP_ERROR);
    if (!osp_task_dbg_out_is_enable(CURRENT_TASKID))
        return (OSP_ERROR);
        
    if (NULL == g_DbgOutSem)
    {
        return OSP_ERROR;
    }
       
    osp_sem_take(g_DbgOutSem, -1);
    if ((osp_task_dbg_is_log(CURRENT_TASKID))  && (CMD_DEBUG_LEVEL != level))
    {
        char *pbuf = DbgBuf;
        va_start(vl, fmt);
        buflen = vsnprintf(pbuf, MAX_DBGINFO_LEN, fmt, vl);
        va_end(vl);
        pbuf[buflen] = '\0';
        osp_dbg_log(pbuf, buflen);
        osp_sem_give(g_DbgOutSem);
        return OSP_OK;
    }

    pMsg = osp_alloc_msg_print_log();
    if (NULL == pMsg)
    {
        /*to makesure osp_debug_out can be useful anytime*/
        char *pbuf = DbgBuf;

        va_start(vl, fmt);
        buflen = vsnprintf(pbuf, MAX_DBGINFO_LEN, fmt, vl);
        va_end(vl);
        printf("%s", pbuf);

        osp_sem_give(g_DbgOutSem);
        return OSP_ERROR;
    }
    
    va_start(vl, fmt);
    buflen = vsnprintf(MSG_HEAD_TO_COMM(pMsg), MAX_DBGINFO_LEN, fmt, vl);
    va_end(vl);
    

    

    pMsg->DstId = localshellout;
    pMsg->SrcId = CURRENT_TASKID;
    
    pMsg->MsgSize = buflen + MSG_HEAD_SIZE;
    osp_send_msg(pMsg);
    osp_sem_give(g_DbgOutSem);
    
    return (OSP_OK);
}
OSP_STATUS osp_debug_out_with_time(int32_t level, char *fmt,...)
{
    va_list vl;
    Osp_Msg_Head *pMsg;
    int32_t buflen = 0;
    OSP_RTCTIME osptime;


    
    if (!osp_is_init_done())
    {
        /*to makesure osp_debug_out can be useful anytime*/
        char *pbuf = malloc(MAX_DBGINFO_LEN);
        if (NULL != pbuf)
        {
            osp_get_rtc_time(&osptime);
            
            buflen = snprintf((char *)pbuf, (size_t)(MAX_DBGINFO_LEN), \
                            "[%04d/%02d/%02d-%02d:%02d:%02d:%02d]", \
                            osptime.year, osptime.month, osptime.day, \
                            osptime.hour, osptime.minute, osptime.second, osptime.usecond);
            
            va_start(vl, fmt);
            buflen = vsnprintf(pbuf+buflen, MAX_DBGINFO_LEN-buflen, fmt, vl);
            va_end(vl);
            printf("%s", pbuf);
            free(pbuf);
        }
         return OSP_OK;
    
    }
 


    if ((gOspPrintLevel) && (*gOspPrintLevel < level))
    {
        return (OSP_ERROR);
    }
    if (!osp_task_dbg_out_is_enable(CURRENT_TASKID))
    {
        return (OSP_ERROR);
    }
    if (NULL == g_DbgOutSem)
    {
        return OSP_ERROR;
    }
    
    osp_sem_take(g_DbgOutSem, -1);
    if (osp_task_dbg_is_log(CURRENT_TASKID))
    {
        char *pbuf = DbgBuf;
    
        /*to makesure osp_debug_out_with_time can be useful anytime*/
            

        osp_get_rtc_time(&osptime);
            
        buflen = snprintf((char *)pbuf, (size_t)(MAX_DBGINFO_LEN), \
                            "[%04d/%02d/%02d-%02d:%02d:%02d:%02d]", \
                            osptime.year, osptime.month, osptime.day, \
                            osptime.hour, osptime.minute, osptime.second, osptime.usecond);
        va_start(vl, fmt);
        buflen += vsnprintf(pbuf+buflen, MAX_DBGINFO_LEN, fmt, vl);
        va_end(vl);
        pbuf[buflen] = '\0';
        osp_dbg_log(pbuf, buflen);

 
        osp_sem_give(g_DbgOutSem);        
        return OSP_OK;
       
    }    

    pMsg = osp_alloc_msg_print_log();
    if (NULL == pMsg)
    {

        /*to makesure osp_debug_out_with_time can be useful anytime*/
        char *pbuf = DbgBuf;

        osp_get_rtc_time(&osptime);
            
        buflen = snprintf((char *)pbuf, (size_t)(MAX_DBGINFO_LEN), \
                            "[%04d/%02d/%02d-%02d:%02d:%02d:%02d]", \
                            osptime.year, osptime.month, osptime.day, \
                            osptime.hour, osptime.minute, osptime.second, osptime.usecond);

        va_start(vl, fmt);
        buflen = vsnprintf(pbuf + buflen, MAX_DBGINFO_LEN, fmt, vl);
        va_end(vl);
        printf("%s",pbuf);

 
        osp_sem_give(g_DbgOutSem);
        return OSP_OK;
    }


    osp_get_rtc_time(&osptime);
    buflen = snprintf((char *)MSG_HEAD_TO_COMM(pMsg), (size_t)(MAX_DBGINFO_LEN), \
                    "[%04d/%02d/%02d-%02d:%02d:%02d:%02d]", \
                    osptime.year, osptime.month, osptime.day, \
                    osptime.hour, osptime.minute, osptime.second, osptime.usecond);


    va_start(vl, fmt);
    buflen += vsnprintf(MSG_HEAD_TO_COMM(pMsg) + buflen, MAX_DBGINFO_LEN - buflen, fmt, vl);
    va_end(vl);
    

    pMsg->DstId = localshellout;
    pMsg->SrcId = CURRENT_TASKID;
    
    pMsg->MsgSize = buflen + MSG_HEAD_SIZE;
    osp_send_msg(pMsg);

    osp_sem_give(g_DbgOutSem);
    return (OSP_OK);
}

OSP_STATUS osp_print(char *pbuf, uint32_t len)
{
    if ((NULL == pbuf) || (0 == len))
    {
        return OSP_ERROR;
    }
    printf("%s", pbuf);
    fflush(stdout);

    return OSP_OK;
}

int32_t osp_shell_dis_mem(int32_t num, uint64_t addr)
{
    uint32_t *p = (uint32_t *)addr;
    int32_t i;
    char *pbuf;
    int32_t len = 0;

    if (NULL == p)
    {
        return -1;
    }
    if ((num * 4) > MAX_DBGINFO_LEN)
    {
        osp_debug_out(CMD_DEBUG_LEVEL, "mux display memsize : %d\r\n", (MAX_DBGINFO_LEN / 4));
        return -1;
    }
    
    pbuf = malloc(MAX_DBGINFO_LEN);
    if (NULL == pbuf)
    {
        return -1;
    }
    
    for (i = 0; i < num; i++)
    {
        if ((i & 0x3) == 0)
            len += sprintf(pbuf + len, "%p : \t", p+i);
        len += sprintf(pbuf + len, "0x%08x\t", p[i]);
        
        if ((i & 0x3) == 0x3)
            len += sprintf(pbuf + len, "%c", '\n');
    }
    pbuf[len] = '\0';
    osp_debug_out(CMD_DEBUG_LEVEL, "%s", pbuf);
    free(pbuf);

    return OSP_OK;
}


int32_t osp_shell_set_mem(int32_t val, uint64_t addr)
{
    uint32_t *p = (uint32_t *)addr;
    if (NULL == p)
        return -1;
    *p = val;

    return OSP_OK;
}




int32_t osp_local_shell_in_main()
{
    return OSP_OK;
}





void osp_debug_init(void)
{

    g_DbgOutSem = osp_get_init_mem(sizeof(lx_sem_t));
    gOspPrintLevel = (uint32_t *)osp_get_init_mem(sizeof(uint32_t));
  

    if (0 == g_ProcessId)
    {
        *gOspPrintLevel = DBG_DEBUG_LEVEL;
        osp_semsm_create(g_DbgOutSem);
    }
    osp_signal_init();

}

void osp_signal_action_trace(int32_t nr, siginfo_t *info, void *void_context)
{
    void *trace[100] = {0};
    int32_t trace_size = 0;
    char **messages = NULL;
    int32_t  i;
    
    trace_size = backtrace(trace, 100);
    messages = (char **)backtrace_symbols(trace, trace_size);
 
    for (i = 0; i < trace_size; ++i)
    {
       osp_debug_out(CMD_DEBUG_LEVEL, "[bt] %s\n", messages[i]);
    }
    return;
}
void osp_signal_action_suspend(int32_t nr, siginfo_t *info, void *void_context)
{
    sigset_t SigSet;
    int32_t Sig;
    
    (void)sigemptyset(&SigSet);
    (void)sigaddset(&SigSet,OSP_TASK_SIGNAL_RESUME);
//    osp_debug_out(CMD_DEBUG_LEVEL, "task:%d suspend\n", CURRENT_TASKID);
    TASK_STATE(CURRENT_TASKID)= TASK_SUSPEND;
    (void)sigwait(&SigSet, &Sig);
    TASK_STATE(CURRENT_TASKID)= TASK_RUNNING;
//    osp_debug_out(CMD_DEBUG_LEVEL, "task:%d resume\n", CURRENT_TASKID);

}
void osp_signal_action_resume(int32_t nr, siginfo_t *info, void *void_context)
{
}
void osp_signal_action_except(int32_t nr, siginfo_t *info, void *void_context)
{
    osp_assert(0);
}


int32_t osp_signal_init(void)
{

  struct sigaction act;
  
  act.sa_sigaction = &osp_signal_action_trace;
  act.sa_flags = SA_SIGINFO;
  
  if (sigaction(OSP_TASK_SIGNAL_TRACE, &act, NULL) < 0) {
      osp_assert(0);
  }

  act.sa_sigaction = &osp_signal_action_suspend;
  act.sa_flags = SA_SIGINFO;
  
  if (sigaction(OSP_TASK_SIGNAL_SUSPEND, &act, NULL) < 0) {
      osp_assert(0);
  }
  act.sa_sigaction = &osp_signal_action_resume;
  act.sa_flags = SA_SIGINFO;
  
  if (sigaction(OSP_TASK_SIGNAL_RESUME, &act, NULL) < 0) {
      osp_assert(0);
  } 

  act.sa_sigaction = &osp_signal_action_except ;
  act.sa_flags = SA_SIGINFO;
  
  if (sigaction(OSP_TASK_SIGNAL_SEGV, &act, NULL) < 0) {
      osp_assert(0);
  } 

  act.sa_sigaction = &osp_signal_action_except ;
  act.sa_flags = SA_SIGINFO;
  
  if (sigaction(OSP_TASK_SIGNAL_BUSSERR, &act, NULL) < 0) {
      osp_assert(0);
  }  

  signal(SIGTTOU, SIG_IGN);
  g_SemDeadFunc = osp_semm_create();

  return OSP_OK;
   
}
int32_t osp_trace_task(uint32_t Id)
{
    if (gOspTask[Id].PhreadId)
    {    
        (void)pthread_kill(TASK_THREADID(Id),OSP_TASK_SIGNAL_TRACE);       
    }
    return OSP_OK;
}
int32_t osp_suspend_task(uint32_t Id)
{
    if (gOspTask[Id].PhreadId)
    {
        (void)pthread_kill(TASK_THREADID(Id),OSP_TASK_SIGNAL_SUSPEND);
    }
    return OSP_ERROR;
}

int32_t osp_resume_task(uint32_t Id)
{
    if (gOspTask[Id].PhreadId)
    {     
        (void)pthread_kill(TASK_THREADID(Id),OSP_TASK_SIGNAL_RESUME); 
    }
    return OSP_ERROR;        
}

void osp_set_dbg_cmdlev(void)
{
    osp_debug_out_set_level(CMD_DEBUG_LEVEL);
}

void osp_set_dbg_errlev(void)
{
    osp_debug_out_set_level(ERR_DEBUG_LEVEL);
}

void osp_set_dbg_crunlev(void)
{
    osp_debug_out_set_level(RUN_DEBUG_LEVEL);
}

void osp_set_dbg_cwarlev(void)
{
    osp_debug_out_set_level(WARNING_DEBUG_LEVEL);
}

int32_t osp_reg_dead_func(OSP_FUNCPTR pfunc)
{
    
    osp_sem_take(g_SemDeadFunc, -1); 
    {
        static int32_t num = 0;
        
        if ((NULL == pfunc)||(num == (DeadFuncNum-1))) 
        {
            osp_sem_give(g_SemDeadFunc);
            return OSP_ERROR;
        }

        gpfunc[num++] = pfunc;
        osp_sem_give(g_SemDeadFunc);
        return 0 ;
    }
    
}
void osp_run_dead_func(void)
{
    int32_t i=0;
    for (i = 0; i < DeadFuncNum; i++)
    {
        if (gpfunc[i])
            gpfunc[i]();
        else
            break;
    }
    osp_debug_out_with_time(CMD_DEBUG_LEVEL,"%d DeadFunc called!\r\n", i); 
}
void osp_assert(int32_t val)
{
    
    if (val == 0)
    {
        osp_run_dead_func();
        /*to make sure deadmsg has been recorded! anyway this shuld to change in reliable  way,todoa*/
        usleep(100);


        osp_trace_task(CURRENT_TASKID);
        osp_fflush_all_logfile();
        osp_suspend_task(CURRENT_TASKID);    
        while (1);
    }

}


