#include "stdio.h"
#include "rtthread.h"
#include "board.h"
#include "mystdlib.h"
#include "list.h"
#include "mystring.h"
#include "signal.h"
#include "transmit.h"
#include "buff.h"
#include "bytearray.h"
#include "debug.h"
#include "crc.h"
#include "dev_flash.h"
#include "mymisc.h"
#include "dev_backup.h"









typedef struct _tran_def{
  ucport_def *u;
  int is_busy;
}tran_def;







static int tran_init(void)
{
  sig_thread t=thread_creat(10);
  tran_def *tran= tran_creat(t);
  app_variable("tran",tran,0);
  return 0;
}
app_init_export(tran_init);


void tran_end_slot(tran_def *obj,void *src,void *data,int ack,char *err_str);

tran_def *tran_creat(sig_thread t)
{
  tran_def *tr=calloc(1,sizeof(tran_def));
  param_check(tr);
  return tr;
}


static ucport_def *ucport_creat(tran_def *t,const char *codec_name,uint8_t cmd,array_def *data);

void tran_recv_slot(tran_def *t,const char *codec_name,uint8_t cmd,array_def *data,char *err_str)
{
  if((strcmp("ok",err_str)!=0)||(cmd==0))
  {
    return;
  }
  //DBG_LOG("tran recv:%s",str_temp(arr_string(data)));
  tran_def *tr=t;
  if((tr->u)&&(cmd==tr->u->cmd))
  {
    if(tr->u->dolater==0||(tr->u->dolater(tr->u,cmd,data,err_str)<0))
      goto init;
  }else
  {
    init:
    DBG_LOG("tran init");
    if(tran_get_busy(tr)){
      DBG_WARN("ucport is busy.");
      array_def *d=arr_creat();
      arr_append(d,TRAN_ERR_BUSY);
      emit tran_send_signal(tr,cmd,arr_temp(d));
      return;
    }
    CHECK_DO(tr->u,tr->u->del);
    tr->u=ucport_creat(t,codec_name,cmd,data);
    if(tr->u==0){
      DBG_LOG("recv data but handle is none.cmd=%02x",cmd);
    }else{
      tr->u->p=tr;
      if(tr->u->del==0)
        tr->u->del=(void (*)(ucport_def *))free;
    }
  }
}




void tran_end_slot(tran_def *obj,void *src,void *data,int ack,char *err_str)
{
  //DBG_LOG("tran end slot");
  param_check(obj);
  if(strcmp(err_str,"timeout")==0)
  {
  }else{
  }
  if((obj->u)&&obj->u->doend)
    obj->u->doend(obj->u,src,data,ack,err_str);
}

void tran_set_busy(tran_def *t,int busy)
{
  t->is_busy=busy;
}
int tran_get_busy(tran_def *t)
{
  return t->is_busy;
}









static ucport_item *cmd_find(const char *codec_name,uint8_t cmd)
{
#if defined (__CC_ARM)  
  extern const int transtruct$$Base;
  extern const int transtruct$$Limit;
  ucport_item *start=(ucport_item *)&transtruct$$Base;
  ucport_item *end=(ucport_item *)&transtruct$$Limit;
#elif defined (__GNUC__)
  extern const int __start_transtruct;
  extern const int __stop_transtruct;
  ucport_item *start=(ucport_item *)&__start_transtruct;
  ucport_item *end=(ucport_item *)&__stop_transtruct;
#endif
  for(ucport_item *t=start;t<end;t++)
  {
    if((t->cmd==cmd)&&(strcmp(t->codec_name,codec_name)==0))
    {
      return t;
    }
  }
  return 0;
}






static ucport_def *ucport_creat(tran_def *t,const char *codec_name,uint8_t cmd,array_def *data)
{
  ucport_item *item=cmd_find(codec_name,cmd);
  if(item){
    return item->creat(t,cmd,data);
  }
  return 0;
}






typedef struct{
  ucport_def u;
  uint8_t *addr;
  int recved_bytes;
  int all_bytes;
  int packet_now;
  int packet_all;
  void (*doexert)(ucport_def *u);
}updata_def;

// [0,1]是当前包数，[2,3]是总包数，剩下的是文件数据
// 当前包数从1开始
static int updata_write_dolater(ucport_def *u,uint8_t cmd,array_def *data,char *err_str)
{
  if(arr_get(data,0)!=2)
  {
    DBG_WARN("cmd=%02x,not a data packet.",cmd);
    u->cmd=0;
    return -1;
  }
  arr_remove(data,0,1);
  updata_def *uu=(updata_def *)u;
  uu->packet_now++;
  int len;
  int packet_now=arr_get(data,0)|(arr_get(data,1)<<8);
  if(packet_now==uu->packet_now)
  {
    //DBG_LOG("write flash addr=%08x",uu->addr);
    len=arr_length(data)-4;
    flash_write(uu->addr,&arr_data(data)[4],len);
    uu->recved_bytes+=len;
    uu->addr+=len;
    array_def *a=arr_creat();
    arr_append(a,0);
    emit tran_reply_signal(u->p,arr_temp(a));
  }
  else
  {
    DBG_WARN("packet err.");
    uu->u.cmd=0;
  }
  if(uu->all_bytes==uu->recved_bytes)
  {
    flash_operate_end();
    DBG_LOG("updata success.");
    if(uu->doexert)
    {
      // 做额外的处理
      uu->doexert(u);
    }
    uu->u.cmd=0;
  }
  return 0;
}


static int updata_read_dolater(ucport_def *u,uint8_t cmd,array_def *data,char *err_str)
{
  if(arr_get(data,0)!=2)
  {
    DBG_WARN("cmd=%02x,not a data packet.",cmd);
    u->cmd=0;
    return -1;
  }
  updata_def *uu=(updata_def *)u;
  int len=200;
  if(uu->all_bytes<uu->recved_bytes+len)
    len=uu->all_bytes-uu->recved_bytes;
  uu->packet_now++;
  array_def *a=arr_creat();
  arr_append(a,uu->packet_now&0xff);
  arr_append(a,(uu->packet_now>>8)&0xff);
  arr_append(a,uu->packet_all&0xff);
  arr_append(a,(uu->packet_all>>8)&0xff);
  arr_appends(a,uu->addr,len);
  emit tran_reply_signal(u->p,arr_temp(a));
  uu->addr+=len;
  uu->recved_bytes+=len;
  if(uu->all_bytes==uu->recved_bytes)
  {
    DBG_LOG("upload success.");
    uu->u.cmd=0;
    return 0;
  }
  return 0;
}

// 初始化写函数
static void updata_prepar_write(updata_def *u,uint8_t cmd,array_def *data)
{
  ucport_def *uu=(ucport_def *)u;
  u->all_bytes=arr_get(data,0)|(arr_get(data,1)<<8)|(arr_get(data,2)<<16)|(arr_get(data,3)<<24);
  uu->cmd=cmd;
  uu->dolater=updata_write_dolater;
  uu->del=(void (*)(ucport_def *))free;
  uint8_t *td=calloc(FLASH_FILE_HEAD_SIZE,sizeof(uint8_t));
  // 文件大小和文件名
  memcpy(td,arr_data(data),arr_length(data));
  // used字段
  ((flash_file *)td)->used=0xffffffff;
  flash_write(u->addr,td,FLASH_FILE_HEAD_SIZE);
  free(td);
  u->addr+=FLASH_FILE_HEAD_SIZE;
  
  // 回复上位机
  array_def *a=arr_creat();
  arr_append(a,0);
  emit tran_reply_signal(uu->p,arr_temp(a));
}

// 初始化读函数
static void updata_prepar_read(updata_def *u,uint8_t cmd,array_def *data)
{
  ucport_def *uu=(ucport_def *)u;
  int len=0;
  if(u->addr[4]!=0xff){
    u->all_bytes=u->addr[0]|(u->addr[1]<<8)|(u->addr[2]<<16)|(u->addr[3]<<24);
    u->packet_all=(u->all_bytes+199)/200;
    uu->cmd=cmd;
    uu->dolater=updata_read_dolater;
    uu->del=(void (*)(ucport_def *))free;
    len=strlen((char *)&u->addr[4]);
    len+=4;
  
    // 回复上位机
    array_def *a=arr_creat();
    arr_append(a,0);
    arr_appends(a,u->addr,len);
    emit tran_reply_signal(uu->p,arr_temp(a));
    u->addr+=FLASH_FILE_HEAD_SIZE;
  }else{
    // 本机没有相应数据
    uu->del=(void (*)(ucport_def *))free;
    array_def *a=arr_creat();
    arr_append(a,0xff);
    emit tran_reply_signal(uu->p,arr_temp(a));
    uu->cmd=0;
  }
}


static void when_scheme_done(ucport_def *u)
{
  // 升级完成后延时50ms重启
  later_execute((void (*)(void *))bk_reboot_app,0,50);
}
static ucport_def *tran_scheme(tran_def *t, uint8_t cmd,array_def *data)
{
  if(arr_length(data)<1){
    DBG_WARN("cmd format err.");
    return 0;
  }
  uint8_t op=arr_get(data,0);
  updata_def *u=calloc(1,sizeof(updata_def));
  arr_remove(data,0,1);
  u->recved_bytes=0;
  u->addr=(uint8_t *)flash_get_scheme();
  u->u.p=t;
  if(op==1)
  {
    u->doexert=when_scheme_done;
    flash_erase_scheme();
    updata_prepar_write(u,cmd,data);
  }else if(op==0)
  {
    updata_prepar_read(u,cmd,data);
  }
  else{
    DBG_WARN("cmd=%02x err,op!=0 or 1 .",cmd);
    free(u);
    return 0;
  }
  return (ucport_def *)u;
}



// data[0]==0读；data[0]==1写
// 最前4个字节是文件长度，然后是文件名
static ucport_def *tran_judge(tran_def *t,uint8_t cmd,array_def *data)
{
  if(arr_length(data)<1){
    DBG_WARN("cmd format err.");
    return 0;
  }
  uint8_t op=arr_get(data,0);
  updata_def *u=calloc(1,sizeof(updata_def));
  arr_remove(data,0,1);
  u->recved_bytes=0;
  u->addr=(uint8_t *)flash_get_lua();
  u->u.p=t;
  if(op==1)
  {
    flash_erase_lua();
    updata_prepar_write(u,cmd,data);
  }else if(op==0)
  {
    updata_prepar_read(u,cmd,data);
  }
  else{
    DBG_WARN("cmd=%02x err,op!=0 or 1 .",cmd);
    free(u);
    return 0;
  }
  DBG_LOG("u->cmd=%d",u->u.cmd);
  return (ucport_def *)u;
}



static void when_upself_done(ucport_def *u)
{
  // 升级完成后延时50ms重启
  later_execute((void (*)(void *))bk_reboot_updata,0,50);
}

static ucport_def *tran_upself(tran_def *t,uint8_t cmd,array_def *data)
{
  if(arr_length(data)<1){
    DBG_WARN("cmd format err.");
    return 0;
  }
  uint8_t op=arr_get(data,0);
  updata_def *u=calloc(1,sizeof(updata_def));
  arr_remove(data,0,1);
  u->recved_bytes=0;
  u->addr=(uint8_t *)flash_get_app2();
  u->u.p=t;
  if(op==1)
  {
    // 升级完成后重启
    u->doexert =when_upself_done;
    flash_erase_app2();
    updata_prepar_write(u,cmd,data);
  }else if(op==0)
  {
    updata_prepar_read(u,cmd,data);
  }
  else{
    DBG_WARN("cmd=%02x err,op!=0 or 1 .",cmd);
    free(u);
    return 0;
  }
  return (ucport_def *)u;
}
static ucport_def *tran_upslave(tran_def *t,uint8_t cmd,array_def *data)
{
  if(arr_length(data)<1){
    DBG_WARN("cmd format err.");
    return 0;
  }
  uint8_t op=arr_get(data,0);
  updata_def *u=calloc(1,sizeof(updata_def));
  arr_remove(data,0,1);
  u->recved_bytes=0;
  u->addr=(uint8_t *)flash_get_slave();
  u->u.p=t;
  if(op==1)
  {
    flash_erase_slave();
    updata_prepar_write(u,cmd,data);
  }else if(op==0)
  {
    updata_prepar_read(u,cmd,data);
  }
  else{
    DBG_WARN("cmd=%02x err,op!=0 or 1 .",cmd);
    free(u);
    return 0;
  }
  return (ucport_def *)u;
}



static ucport_def *tran_upjwtcode(tran_def *t,uint8_t cmd,array_def *data)
{
  if(arr_length(data)<1){
    DBG_WARN("cmd format err.");
    return 0;
  }
  uint8_t op=arr_get(data,0);
  updata_def *u=calloc(1,sizeof(updata_def));
  arr_remove(data,0,1);
  u->recved_bytes=0;
  u->addr=(uint8_t *)flash_get_jwtcode();
  u->u.p=t;
  if(op==1)
  {
    flash_erase_jwtcode();
    updata_prepar_write(u,cmd,data);
  }else if(op==0)
  {
    updata_prepar_read(u,cmd,data);
  }
  else{
    DBG_WARN("cmd=%02x err,op!=0 or 1 .",cmd);
    free(u);
    return 0;
  }
  return (ucport_def *)u;
}






transmit_export(ym_checker,0x32,tran_scheme)
transmit_export(ym_checker,0x36,tran_judge)
transmit_export(ym_checker,0xed,tran_upself)
transmit_export(ym_checker,0xee,tran_upslave)
transmit_export(ym_checker,0xec,tran_upjwtcode)









