#include "NyLPC_cApipa.h"
#include "NyLPC_uipService.h"
#include "../uip/NyLPC_cUipService_protected.h"
#include <stdio.h>
#include <string.h>


/**
 * ARPテーブルに指定IPが現れるまで待ちます。
 */
static NyLPC_TBool waitForArpResponse(const struct NyLPC_TIPv4Addr* i_ip,NyLPC_TUInt32 i_wait_in_ms)
{
    NyLPC_TcStopwatch_t sw;
    NyLPC_cStopwatch_initialize(&sw);
    NyLPC_cStopwatch_startExpire(&sw,i_wait_in_ms);
    while(!NyLPC_cStopwatch_isExpired(&sw)){
        NyLPC_cThread_yield();
        if(NyLPC_cUipService_hasArpInfo(i_ip)){
            return NyLPC_TBool_TRUE;
        }
    }
    NyLPC_cStopwatch_finalize(&sw);
    return NyLPC_TBool_FALSE;
}
static void makeIP(NyLPC_TcApipa_t* i_inst,struct NyLPC_TIPv4Addr* i_ip)
{
//  NyLPC_TIPv4Addr_set(i_ip,192,168,128,206);//for conflict test!
    NyLPC_TIPv4Addr_set(i_ip,169,254,(i_inst->_seed>>8)&0xff,i_inst->_seed & 0xff);
}
static void updateSeed(NyLPC_TcApipa_t* i_inst)
{
    do{
        i_inst->_seed=(391*i_inst->_seed+392);
    }while(((i_inst->_seed & 0xff)==0) || ((i_inst->_seed & 0xff00)==0));
}



void NyLPC_cApipa_initialize(NyLPC_TcApipa_t* i_inst)
{
    i_inst->_seed=0;
}


/**
 * この関数はuipを操作します。
 * cNetは停止中である必要があります。
 */
NyLPC_TBool NyLPC_cApipa_requestAddr(NyLPC_TcApipa_t* i_inst,NyLPC_TcIPv4Config_t* i_cfg,NyLPC_TInt16 i_repeat)
{
    int i;
    NyLPC_TcIPv4Config_t cfg;
    struct NyLPC_TIPv4Addr caip;

    //ゼロコンフィギュレーション用のIPを設定
    NyLPC_cIPv4Config_initialzeCopy(&cfg,i_cfg);
    //seedを更新
    for(i=0;i<6;i++)
    {
        i_inst->_seed+=i_cfg->eth_mac.addr[i];
    }
    NyLPC_cIPv4Config_setDefaultRoute(&cfg,&NyLPC_TIPv4Addr_ZERO);
    for(i=i_repeat-1;i>=0;i--){
        NyLPC_cIPv4Config_setIp(&cfg,&NyLPC_TIPv4Addr_ZERO,&NyLPC_TIPv4Addr_ZERO);
        updateSeed(i_inst);
        makeIP(i_inst,&caip);
        //startInterface
        NyLPC_cUipService_start(&cfg);
        NyLPC_cUipService_sendArpRequest(&caip);
        //テーブル更新待ち
        if(waitForArpResponse(&caip,512+(i_inst->_seed % 256))){
            NyLPC_cUipService_stop();
            continue;
        }
        NyLPC_cUipService_stop();
        //IPのコンフリクトテスト
        NyLPC_cIPv4Config_setIp(&cfg,&caip,&NyLPC_TIPv4Addr_APIPA_MASK);
        NyLPC_cUipService_start(&cfg);
        //!ARP送信
        NyLPC_cUipService_sendArpRequest(&caip);
        if(waitForArpResponse(&caip,512+(256-(i_inst->_seed % 256)))){
            //応答があったらエラー
            NyLPC_cUipService_stop();
            continue;
        }
        //OK
        NyLPC_cUipService_stop();
        NyLPC_cIPv4Config_setIp(i_cfg,&cfg.ip_addr,&cfg.netmask);
        return NyLPC_TBool_TRUE;
    }
    return NyLPC_TBool_FALSE;
}
